ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Cookiecutter Django FAQ 深度解读:Sites 迁移机制、12-Factor 配置与 Two Scoops 布局差异

Cookiecutter Django FAQ 深度解读:Sites 迁移机制、12-Factor 配置与 Two Scoops 布局差异 Cookiecutter Django FAQ 深度解读Sites 迁移机制、12-Factor 配置与 Two Scoops 布局差异【免费下载链接】cookiecutter-djangoCookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.项目地址: https://gitcode.com/GitHub_Trending/co/cookiecutter-django导读本文围绕仓库官方文档 docs/5-help/faq.rst 中的三个核心问题展开为什么生成的项目里会有一个django.contrib.sites目录、为什么项目不使用单一配置文件、以及为什么项目布局不完全遵循《Two Scoops of Django》。你将理解 Cookiecutter Django 如何通过自定义数据迁移在项目生成瞬间写入域名与站点名掌握其环境变量驱动的 12-Factor 配置哲学并弄清模板布局与经典书籍之间的取舍逻辑——这些问题背后是理解整个模板设计思路的钥匙。一、FAQ 在文档体系中的定位在 Cookiecutter Django 的官方文档中FAQ 位于 docs/index.rst 的 Help 目录下与5-help/troubleshooting并列定位是回答用户在学习、使用和二次开发模板过程中最常提出的为什么类问题。整份 FAQ 目前包含三个条目恰好对应三类典型疑问架构层面为什么模板里有一个django.contrib.sites目录有完整实现答案设计理念层面为什么不用单一配置文件12-Factor App原文中标注为 TODO风格取舍层面为什么布局不遵循《Two Scoops of Django》其中第一个问题拥有最丰富的源码支撑是本文剖析的重点后两个问题则能带你理解该模板的演进哲学。二、为什么 Cookiecutter Django 中有一个django.contrib.sites目录2.1 问题背景example.com的烦恼Django 自带的sites框架默认在数据库中创建一条Site记录其domain和name字段的值是example.com。当你基于模板生成一个新项目并部署到真实域名时通常需要手动登录管理后台或通过shell修改这条记录——这是一件容易遗漏且容易出错的琐事。Cookiecutter Django 的答案是把这个手工活变成一个数据迁移。这样当你运行cookiecutter生成项目、随后执行python manage.py migrate时domain和name就已经是你配置的domain_name和project_name了无需任何手工干预。FAQ 原文对此的解释是这个目录存在的目的是添加一个迁移让你不必手动把sites.Site记录从example.com改成你自己的域名。Cookiecutter 会把你的{{cookiecutter.domain_name}}和{{cookiecutter.project_name}}值分别放入 domain 与 name 字段。2.2 迁移链全景0001 → 0002 → 0003 → 0004自定义的 sites 迁移位于{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/contrib/sites/migrations/目录下共四个文件形成一条完整的数据迁移链迁移文件作用0001_initial.py重新定义Site模型db_table django_sitedomain带_simple_domain_name_validator校验器0002_alter_domain_unique.py给domain字段加uniqueTrue约束0003_set_site_domain_and_name.py核心用 Cookiecutter 变量写入 domain 与 name本文主角0004_alter_options_ordering_domain.py设置Meta.ordering [domain]等模型选项整个自定义迁移通过MIGRATION_MODULES设置接管了 Django 内置的 sites 迁移相关配置见 {{cookiecutter.project_slug}}/config/settings/base.py# MIGRATIONS MIGRATION_MODULES {sites: {{ cookiecutter.project_slug }}.contrib.sites.migrations}同时该文件中还固定了站点 ID# https://docs.djangoproject.com/en/dev/ref/settings/#site-id SITE_ID 1这意味着迁移会以id SITE_ID即 1为准去更新或创建那条唯一的站点记录。2.3 逐行拆解 0003 迁移模板变量如何变成数据库记录0003_set_site_domain_and_name.py 是理解整个机制的核心。它本身是一份 Jinja2 模板文件注意其中{{ ... }}语法在项目生成时会被 Cookiecutter 渲染把模板变量替换成你在cookiecutter.json交互中填写的值。正向迁移update_site_forwarddef update_site_forward(apps, schema_editor): Set site domain and name. Site apps.get_model(sites, Site) _update_or_create_site_with_sequence( Site, schema_editor.connection, {{ cookiecutter.domain_name }}, {{ cookiecutter.project_name[:50] }}, )关键点使用apps.get_model(sites, Site)获取历史模型而不是from django.contrib.sites.models import Site——这是数据迁移的正确写法保证迁移与模型当前状态解耦domain取自{{ cookiecutter.domain_name }}默认值example.com见 cookiecutter.jsonname取自{{ cookiecutter.project_name[:50] }}截断到 50 个字符因为Site.name字段的max_length50见 0001 迁移中的字段定义——这是模板作者处理超长项目名的细节设计。核心工具函数_update_or_create_site_with_sequencedef _update_or_create_site_with_sequence(site_model, connection, domain, name): Update or create the site with default ID and keep the DB sequence in sync. site, created site_model.objects.update_or_create( idsettings.SITE_ID, defaults{ domain: domain, name: name, }, ) if created: max_id site_model.objects.order_by(-id).first().id with connection.cursor() as cursor: cursor.execute(SELECT last_value from django_site_id_seq) (current_id,) cursor.fetchone() if current_id max_id: cursor.execute( alter sequence django_site_id_seq restart with %s, [max_id 1], )这段代码解决了两个实际问题幂等更新update_or_create(idSITE_ID, ...)保证无论数据库处于初始状态还是已有站点记录迁移都能安全执行——已存在则更新不存在则创建序列同步PostgreSQL 陷阱当迁移显式指定id1插入记录时PostgreSQL 的自增序列django_site_id_seq并不知道这次插入序列值仍停留在初始状态。如果放任不管下次通过 ORM 创建新站点时会触发主键唯一约束冲突。因此代码读取序列当前值若小于等于当前最大 id则将其restart with max_id 1从源码注释可见这一处理逻辑well get a unique constraint violation the next time a site is created。反向迁移update_site_backward则把 domain 和 name 还原为example.com保证迁移可回滚migrate sites 0002时可逆。2.4 完整工作流从生成到上线综合以上源码可以梳理出这条链路运行cookiecutter https://gitcode.com/GitHub_Trending/co/cookiecutter-django或本地模板路径在交互中填写domain_name、project_name等值Cookiecutter 渲染模板0003 迁移文件中的{{ cookiecutter.domain_name }}与{{ cookiecutter.project_name[:50] }}被替换为真实值参见 docs/2-local-development/developing-locally.rst 的生成说明执行python manage.py migrate时sites 迁移链按0001 → 0002 → 0003 → 0004顺序执行站点记录被写入正确域名与名称此后django.contrib.sites框架SITE_ID 1在任何需要读取当前站点的场景如生成绝对 URL、Site.objects.get_current()中直接可用。值得强调的是settings.rst 与base.py中还有配套的域名依赖生产环境默认ALLOWED_HOSTS [{{ cookiecutter.domain_name }}]见 production.py与 sites 迁移共享同一个domain_name变量保证一个变量多处一致。三、为什么不用单一配置文件12-Factor AppFAQ 的 TODO 与仓库的真实答案3.1 诚实说明FAQ 原文的留白需要如实指出FAQ 原文中为什么不用一个配置文件12-Factor App这一条目前标注为 TODO尚未给出正式书面回答。但这并不意味着项目没有答案——恰恰相反整个仓库的设计就是对这个问题最好的实证回答。下面基于仓库实际内容设置文件、文档还原这一设计理念。3.2 仓库的实际做法拆分的 settings 与环境变量注入从 {{cookiecutter.project_slug}}/config/settings/ 目录可以看到设置被拆分为四个模块base.py——所有环境共享的公共设置INSTALLED_APPS、数据库、认证、国际化等local.py——本地开发设置如ALLOWED_HOSTS [localhost, 0.0.0.0, 127.0.0.1]production.py——生产设置安全头、ALLOWED_HOSTS、Sentry、云存储等test.py——测试设置。这种拆分本身就是 12-Factor 中配置与代码分离的体现同一个代码库在不同环境加载不同配置而不是在代码里硬编码环境差异。配置值通过环境变量注入典型写法base.pyif os.getenv(DATABASE_URL, defaultNone): DATABASES {default: env.db(DATABASE_URL)} else: DATABASES { default: { ENGINE: django.db.backends.postgresql, NAME: env.str(POSTGRES_DB), USER: env.str(POSTGRES_USER), PASSWORD: env.str(POSTGRES_PASSWORD), HOST: env.str(POSTGRES_HOST, default...), PORT: env.str(POSTGRES_PORT, default5432), }, }settings.rst 用三张表格系统整理了环境变量与 Django 设置的映射并给出了开发/生产环境各自的默认值例如环境变量Django 设置开发默认生产默认DATABASE_URLDATABASESDocker 自动否则postgres://project_slug缺失则报错DJANGO_DEBUGDEBUGTrueFalseDJANGO_SECRET_KEYSECRET_KEY自动生成缺失则报错DJANGO_ALLOWED_HOSTSALLOWED_HOSTS[*][your_domain_name]DJANGO_SECURE_SSL_REDIRECTSECURE_SSL_REDIRECT不适用TrueDJANGO_SESSION_COOKIE_SECURESESSION_COOKIE_SECURE不适用False注意其中两条值得玩味的设计开发环境宽容、生产环境强硬DJANGO_SECRET_KEY、DJANGO_ADMIN_URL、DATABASE_URL等在生产缺省时直接抛错raises error强制部署者显式配置避免带着弱默认值上线环境变量命名带DJANGO_前缀避免与系统中其他软件的环境变量命名冲突。这正是 12-Factor 第三要素在环境中存储配置的工程化落地同时也解释了为什么不是单一配置文件单一文件把**代码settings 逻辑与配置环境相关值**耦合在一起既无法在不同环境间复用也难以安全地保管密钥。3.3 一句话总结FAQ 的 TODO 问题的答案可以用仓库现状概括为Cookiecutter Django 选择按环境拆分的 settings 模块 环境变量注入配置而非单一配置文件是为了实现配置与代码分离、环境间复用、以及生产安全缺省策略——而这也正是 12-Factor App 方法论在 Django 项目中的标准实践。四、为什么不完全遵循《Two Scoops of Django》的布局第三个 FAQ 问题涉及与经典书籍的差异FAQ 原文给出的回答非常坦诚这里完整继承并展开你可能会注意到项目中的某些元素与我们在《Two Scoops of Django 3.x》第三章描述的并不完全一致。原因是这个项目除了别的用途之外还是一个试验新想法、新概念的地方。有些想法成功了有些没有但最终结果就是它不一定会与作者合著的那本书里描述的内容精确一致。这段话揭示了 Cookiecutter Django 的演进机制模板即试验田项目本身是维护者测试新实践如MIGRATION_MODULES接管 sites 迁移、环境变量驱动的 settings 拆分、update_or_create 序列同步等技巧的载体书是静态的项目是动态的书籍描述的是某一时间点的推荐做法而模板持续跟随 Django 生态与社区最佳实践演进读者需要以项目为准当你基于模板生成项目时应以生成的代码与官方文档如 settings.rst、project-generation-options.rst为事实依据书籍可作为背景参考但不应作为唯一标准。从源码看这种试验确实是有效的0003 迁移中的序列同步逻辑、SITE_ID与MIGRATION_MODULES的搭配、settings 的三层拆分都是经过实践验证后被固化进模板的成熟方案。五、FAQ 之外的配套阅读FAQ 是理解模板设计意图的窗口但要真正用好 Cookiecutter Django建议结合以下仓库文档与源码继续深入docs/1-getting-started/settings.rst——全部环境变量与 Django 设置的映射表是配置问题的最终依据docs/1-getting-started/project-generation-options.rst——生成项目时所有可选项的说明对应 cookiecutter.json 中的domain_name、mail_service、cloud_provider等变量{{cookiecutter.project_slug}}/config/settings/base.py——SITE_ID、MIGRATION_MODULES、INSTALLED_APPS的完整定义处{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/contrib/sites/migrations/0003_set_site_domain_and_name.py——FAQ 第一个问题对应的核心实现docs/5-help/troubleshooting.rst——与 FAQ 互补的故障排查指南docs/2-local-development/developing-locally.rst——从生成到本地跑通项目的完整流程。六、结语Cookiecutter Django 的 FAQ 虽然篇幅不长却浓缩了模板最重要的三个设计决策用数据迁移自动化完成站点初始化contrib.sites迁移链、以环境变量实现 12-Factor 配置分离settings 四模块拆分、以务实态度对待经典书籍的布局规范模板优先、持续演进。理解这三条你不仅能回答为什么项目里有这些结构更能举一反三地看懂模板中其他看似多余的目录与文件各自承担的自动化职责。【免费下载链接】cookiecutter-djangoCookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.项目地址: https://gitcode.com/GitHub_Trending/co/cookiecutter-django创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表