☰
Django生产部署三支柱:Docker+PostgreSQL+自定义用户模型
2026/10/26 23:30:27 网站建设 项目流程

简介:本资源是William S. Vincent所著《Django for Professionals》中文版PDF电子书,面向具备Python基础、正从Django入门迈向生产级开发的中高级Web开发者。全书系统讲解如何构建可部署、高安全、易维护的专业网站,覆盖Docker容器化部署、PostgreSQL生产数据库集成、自定义用户模型与邮箱验证认证流程、全面测试策略(单元/模板/HTML)、环境变量管理、静态资源优化及Django Crispy Forms等关键实践。资源为1个25.89MB的PDF文件,内容结构清晰,含7大核心章节,从Docker起步到用户注册进阶,每章均配实操代码、Git管理建议与测试范例,便于边学边练。目前已有61人学习下载,适合希望摆脱玩具项目、掌握Django社区主流生产规范的工程师系统精读与工程复用。

1. Django生产级网站不是“跑起来就行”:Docker+PostgreSQL+自定义用户模型,三道坎跨不过去就别谈上线

你用django-admin startproject建过项目,也写过几个ListView和ModelForm,本地runserver跑得飞起——但真要部署到服务器上,第二天就被运维叫去查日志:数据库连接超时、静态文件404、用户注册邮件发不出、DEBUG=True被扫出来一堆敏感路径……这不是玄学,是《Django for Professionals》开篇就点破的现实:生产级网站和玩具项目的分水岭,不在功能多寡,而在环境隔离性、数据持久性、身份可信性这三根支柱是否立得住。这本书不是教你怎么写视图,而是手把手带你把Docker容器当开发沙盒、把PostgreSQL当唯一数据源、把AbstractBaseUser当用户系统地基——每一步都卡在真实部署场景的痛点上。适合谁?适合已经能写CRUD但一碰ALLOWED_HOSTS就懵、看到psycopg2.OperationalError就重启服务器、改个SECRET_KEY还得翻Git历史找旧值的中级开发者。如果你正卡在“本地能跑,线上报错;功能完整,不敢上线”的临界点,这篇安装笔记就是为你写的血泪经验复盘。

2. Docker环境搭建:从Hello World到Django容器化,版本选错直接翻车

Docker不是锦上添花的工具,而是这本书所有后续章节(PostgreSQL、环境变量、静态文件收集)的运行底座。它解决的核心问题是:让开发、测试、预发布、生产四套环境共享同一套镜像定义,彻底消灭“在我机器上是好的”这类黑匣子问题。书中第1章明确要求使用Docker Desktop(macOS/Windows)或Docker Engine(Linux),而非Docker Toolbox等已淘汰方案。我们按2024年稳定实践来落地。

2.1 安装Docker:避开ARM64与旧版兼容性陷阱

提示:Mac M系列芯片用户务必确认安装的是Docker Desktop for Mac (Apple Silicon)版本,而非Intel版。后者在运行postgres:15-alpine镜像时会因架构不匹配导致容器启动失败,报错类似exec /bin/sh: exec format error。

  • macOS(Apple Silicon):
    下载地址:https://desktop.docker.com/mac/main/arm64/Docker.dmg
    安装后打开Docker Desktop,等待右上角鲸鱼图标变蓝,终端执行:

    docker --version # 输出应为:Docker version 24.0.7, build afdd53b docker run hello-world # 成功输出"Hello from Docker!"即通过
  • Ubuntu 22.04 LTS(推荐):
    用官方仓库安装,避免snap包带来的权限和路径问题:

    # 卸载可能存在的旧版 sudo apt remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt update && sudo apt install -y \ ca-certificates \ curl \ gnupg \ lsb-release # 添加Docker官方GPG密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加稳定版仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update # 安装Docker Engine sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 将当前用户加入docker组(免sudo) sudo usermod -aG docker $USER # 重启shell或执行:newgrp docker
  • Windows 11(WSL2):
    必须启用WSL2并安装Ubuntu 22.04发行版(非Debian或Alpine),再在WSL内执行上述Ubuntu安装步骤。严禁在Windows原生CMD/PowerShell中直接安装Docker Desktop——会导致docker-compose无法正确挂载Windows路径到容器内,后续manage.py migrate必报错。

2.2 构建第一个Django容器:Dockerfile详解与常见参数取舍

书中第1章的Django Hello, World不是简单docker run python:3.11-slim django-admin startproject,而是要求你手写Dockerfile,这是生产级思维的第一课。以下是本书配套实践的最小可行Dockerfile(基于Python 3.11 + Django 4.2 LTS):

# 使用官方Python基础镜像(slim版,不含编译工具,体积小、攻击面小) FROM python:3.11-slim # 设置工作目录(必须与后续volume挂载路径一致) WORKDIR /app # 复制requirements.txt优先(利用Docker layer缓存) COPY requirements.txt . # 安装Python依赖(-q静默,--no-cache-dir避免镜像膨胀) RUN pip install -q --no-cache-dir -r requirements.txt # 复制项目源码(此时才复制,避免每次改代码都重装pip包) COPY . . # 暴露端口(仅声明,不实际绑定;由docker run -p控制) EXPOSE 8000 # 启动命令(使用gunicorn替代runserver,符合生产规范) CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "mysite.wsgi:application"]

关键参数说明:

  • python:3.11-slim:不选alpine。虽然体积更小,但psycopg2在Alpine上需额外编译gcc和postgresql-dev,极易因musl libc兼容性导致ImportError: libpq.so.5。slim基于Debian,预编译wheel支持完美。
  • --no-cache-dir:Docker构建层不可变,pip缓存无意义且增大镜像。
  • gunicorn:runserver仅用于开发,生产必须用WSGI服务器。本书第16章性能优化会深入对比gunicorn/uwsgi。

2.3 docker-compose.yml:定义服务拓扑,让Django和PostgreSQL“说同一种语言”

单靠Dockerfile只能跑Django,但生产环境必然需要数据库。docker-compose.yml是本书第2章的核心,它把Django、PostgreSQL、Nginx(后续章节)组织成可协同工作的服务网络:

# docker-compose.yml version: '3.9' services: # Django应用服务 web: build: . command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/app # 本地代码实时映射到容器 ports: - "8000:8000" environment: - DEBUG=1 - SECRET_KEY=django-insecure-xxx # 开发临时密钥 - DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres depends_on: - db # 关键:与db服务在同一自定义网络,DNS解析可用 networks: - django-net # PostgreSQL数据库服务 db: image: postgres:15-alpine restart: always environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres volumes: - postgres_data:/var/lib/postgresql/data/ # 持久化数据卷 networks: - django-net # 自定义网络,确保服务间DNS可达(web可ping通db) networks: django-net: driver: bridge # 持久化数据卷,独立于容器生命周期 volumes: postgres_data:

为什么必须用depends_on+networks?因为Django启动时会立即尝试连接数据库。若无此配置,web服务常因db未就绪而崩溃退出。depends_on仅控制启动顺序,真正的健康检查需配合healthcheck(本书第8章环境变量后补充)。

2.4 避坑:Docker常见问题排查(现象→原因→解决)

  • 现象:docker-compose up后web容器反复重启,日志显示django.db.utils.OperationalError: could not connect to server: Connection refused
    原因:web服务启动快于db初始化完成(PostgreSQL首次启动需初始化数据目录,耗时5~10秒)。depends_on不检查服务内部健康状态。
    解决:在web服务中添加启动前等待脚本。创建wait-for-db.sh:

    #!/bin/sh while ! nc -z db 5432; do sleep 1 done

    修改docker-compose.yml中web.command为:sh -c "sh wait-for-db.sh && python manage.py runserver 0.0.0.0:8000"

  • 现象:Mac上docker-compose up后,浏览器访问http://localhost:8000空白,Django日志无输出
    原因:Docker Desktop for Mac的文件系统挂载机制导致/app目录权限异常,manage.py无法读取settings.py。
    解决:在docker-compose.yml的web.volumes下添加权限修正:

    volumes: - .:/app:cached # macOS必须加:cached提升性能并修复权限
  • 现象:Ubuntu上执行docker run hello-world报错Cannot connect to the Docker daemon
    原因:Docker服务未启动,或当前用户未加入docker组。
    解决:

    sudo systemctl start docker sudo systemctl enable docker # 开机自启 sudo usermod -aG docker $USER # 退出终端重登,或执行:newgrp docker
  • 现象:docker-compose build卡在Step 4/6 : RUN pip install ...,长时间无响应
    原因:国内网络访问PyPI超时。
    解决:在Dockerfile的RUN pip install前添加清华源:

    RUN pip install -q --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt

3. PostgreSQL集成:不只是换数据库,而是重构Django的数据契约

Django默认SQLite虽方便,但生产环境必须用PostgreSQL——这不是性能焦虑,而是数据完整性、并发控制、JSONB字段、全文检索等能力的硬性门槛。本书第2章强调:PostgreSQL不是Django的“插件”,而是整个数据层的契约制定者。从psycopg2驱动安装到DATABASE_URL解析,每一步都在重塑Django与数据的对话方式。

3.1 安装psycopg2-binary:绕过编译地狱的务实选择

psycopg2是Python连接PostgreSQL的官方驱动,但源码编译需libpq-dev、python3-dev等系统依赖,极易因版本不匹配失败。本书明确推荐psycopg2-binary(第2章P41),这是官方提供的预编译wheel包,省去所有编译环节:

# requirements.txt 中写入(注意:-binary后缀不可省略) psycopg2-binary==2.9.7

提示:psycopg2-binary在开发/测试环境完全安全。生产环境若对二进制包有审计要求,可切换回源码版,但需在Dockerfile中显式安装依赖:

RUN apt-get update && apt-get install -y \ libpq-dev \ python3-dev \ && rm -rf /var/lib/apt/lists/* RUN pip install psycopg2==2.9.7

3.2 配置Django数据库:从settings.py到DATABASE_URL的演进

书中第2章P39的Settings部分,核心是教会你用环境变量驱动数据库配置,而非硬编码。这是生产安全的基石(SECRET_KEY、数据库密码绝不进Git)。以下是本书推荐的settings/base.py数据库配置模式:

# mysite/settings/base.py import dj_database_url # 需安装 django-heroku 或单独 pip install dj-database-url # 从环境变量 DATABASE_URL 解析(格式:postgres://user:pass@host:port/dbname) DATABASES = { 'default': dj_database_url.config( default='sqlite:///db.sqlite3', # 开发兜底 conn_max_age=600, # 连接池最大存活时间(秒) ssl_require=False # 生产环境设为True(见第17章安全) ) }

dj_database_url的作用是将字符串postgres://postgres:postgres@db:5432/postgres自动转换为Django所需的字典结构,无需手动拆解URL。其conn_max_age=600参数至关重要——它启用连接池复用,避免高并发下频繁创建/销毁数据库连接,这是本书第16章性能优化的前提。

3.3 初始化数据库:migrate、createsuperuser与Docker Volume持久化验证

Docker容器删除后,若数据未持久化,migrate结果将丢失。本书第2章P43的New Database强调:postgres_data卷是数据的生命线。验证步骤如下:

# 1. 启动服务(首次会初始化postgres_data卷) docker-compose up -d db # 2. 进入db容器,验证数据库存在 docker-compose exec db psql -U postgres -c "\l" # 应看到名为"postgres"的数据库 # 3. 启动web服务并执行migrate docker-compose up -d web docker-compose exec web python manage.py migrate # 4. 创建超级用户(密码输入两次) docker-compose exec web python manage.py createsuperuser # 5. 验证数据持久化:停止并删除容器 docker-compose down # 6. 重新启动,检查migrate记录和superuser是否还在 docker-compose up -d docker-compose exec web python manage.py showmigrations # 所有migration应显示 [X](已应用) docker-compose exec web python manage.py shell -c "from django.contrib.auth import get_user_model; print(get_user_model().objects.count())" # 应输出 1(即刚才创建的superuser)

若第6步showmigrations显示全为[ ],说明postgres_data卷未生效——检查docker-compose.yml中db.volumes路径是否拼写错误(如/var/lib/postgresql/data/末尾斜杠缺失)。

3.4 避坑:PostgreSQL连接与权限的典型故障(现象→原因→解决)

  • 现象:docker-compose exec web python manage.py migrate报错django.db.utils.OperationalError: FATAL: password authentication failed for user "postgres"
    原因:db服务的POSTGRES_PASSWORD环境变量与DATABASE_URL中的密码不一致。Docker Compose中environment变量作用域仅限该服务,web服务里的DATABASE_URL必须与db的密码严格匹配。
    解决:统一在.env文件中定义,docker-compose.yml引用:

    # .env 文件 POSTGRES_PASSWORD=mysecretpassword DATABASE_URL=postgresql://postgres:mysecretpassword@db:5432/postgres
    # docker-compose.yml services: db: environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} web: environment: - DATABASE_URL=${DATABASE_URL}
  • 现象:psql -U postgres -h localhost在宿主机可连,但在web容器内psql -U postgres -h db连不上
    原因:db服务暴露的端口是5432,但Docker网络中服务名db解析为容器IP,-h db正确;而宿主机localhost指向本机,非容器网络。
    解决:永远在容器内用服务名(db)而非localhost连接其他服务。这是Docker网络设计原则,也是本书所有配置的基石。

  • 现象:migrate成功,但Django Admin登录时提示relation "auth_user" does not exist
    原因:migrate未应用auth、contenttypes等Django内置app的migration。
    解决:检查INSTALLED_APPS是否包含'django.contrib.auth'等,默认Django项目已包含,但若手动删减过需补全。

  • 现象:PostgreSQL容器日志刷屏LOG: incomplete startup packet
    原因:Django应用频繁建立短连接(如未配置连接池),或健康检查探针配置不当。
    解决:在docker-compose.yml中为db服务添加健康检查,并确保Django配置CONN_MAX_AGE:

    db: healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] interval: 30s timeout: 10s retries: 5

4. 自定义用户模型:从AbstractUser到AbstractBaseUser,为什么必须重写?

Django默认的User模型(django.contrib.auth.models.User)为快速开发而生,但生产网站几乎必然要扩展:邮箱作为唯一登录凭证、手机号验证、用户角色分级、头像存储路径……本书第3章P54直击要害:不重写用户模型,等于把用户数据的主权拱手交给框架,后续所有认证、权限、Profile扩展都将陷入打补丁的泥潭。这不是过度设计,而是技术债的止损点。

4.1 选择AbstractBaseUser还是AbstractUser:一个影响三年的决定

  • AbstractUser:继承AbstractBaseUser+PermissionsMixin,自带username、first_name、last_name、email、is_staff等字段。适合只需扩展字段(如加phone_number)、保留用户名登录的场景。
  • AbstractBaseUser:仅提供密码哈希、token生成等核心认证逻辑,所有字段(包括邮箱、姓名)需自行定义。本书第3章采用此方案,因其强制你思考:用户标识到底是什么?是邮箱?手机号?还是第三方ID?这是生产级身份设计的起点。

本书实践选择AbstractBaseUser,创建users/models.py:

# users/models.py from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.db import models class UserManager(BaseUserManager): """自定义用户管理器,覆盖create_user和create_superuser""" def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('Email is required') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) # 加密存储 user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self.create_user(email, password, **extra_fields) class User(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) # 唯一标识 first_name = models.CharField(max_length=30, blank=True) last_name = models.CharField(max_length=30, blank=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(auto_now_add=True) objects = UserManager() # 关联自定义管理器 USERNAME_FIELD = 'email' # 登录凭证字段 REQUIRED_FIELDS = ['first_name', 'last_name'] # createsuperuser时必填字段 def __str__(self): return self.email

注意:USERNAME_FIELD = 'email'是关键开关,它告诉Django认证系统:“用邮箱而非用户名登录”。REQUIRED_FIELDS定义了createsuperuser命令的交互式输入项。

4.2 配置AUTH_USER_MODEL:settings.py中的一行,决定项目生死

在settings/base.py中添加(必须在任何model导入前设置):

# mysite/settings/base.py AUTH_USER_MODEL = 'users.User' # 'app_name.ModelName'

这一行必须放在INSTALLED_APPS之后、所有models.py被导入之前。若在INSTALLED_APPS之前设置,Django启动时会因找不到usersapp而崩溃;若在某个model之后设置,则makemigrations会报错LookupError: No installed app with label 'users'。

4.3 迁移与Admin适配:从零开始重建用户生态

重写用户模型后,所有migration必须从头开始。这是本书第3章P60强调的“不可逆操作”:

# 1. 删除所有已有migration文件(除__init__.py) rm users/migrations/0*.py rm mysite/migrations/0*.py # 2. 生成初始migration(此时Django还不知道User模型) python manage.py makemigrations users # 3. 强制标记为已应用(因数据库已存在默认User表,需跳过) python manage.py migrate --fake-initial # 4. 创建超级用户(此时用新User模型) python manage.py createsuperuser # 输入邮箱、密码等,不再有username字段

Admin适配(users/admin.py)需重写:

# users/admin.py from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from .models import User @admin.register(User) class UserAdmin(BaseUserAdmin): # 列表页显示字段 list_display = ('email', 'first_name', 'last_name', 'is_staff', 'date_joined') # 搜索字段 search_fields = ('email', 'first_name', 'last_name') # 过滤器 list_filter = ('is_staff', 'is_superuser', 'is_active', 'date_joined') # 编辑页布局 fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal Info', {'fields': ('first_name', 'last_name')}), ('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}), ('Important dates', {'fields': ('last_login', 'date_joined')}), ) # 创建用户时的字段(无password字段,由set_password处理) add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'first_name', 'last_name', 'password1', 'password2'), }), ) # 默认排序 ordering = ('email',)

4.4 避坑:自定义用户模型的致命陷阱(现象→原因→解决)

  • 现象:python manage.py makemigrations报错SystemCheckError: System check identified some issues: ERRORS: ?: (auth.E001) 'AUTH_USER_MODEL' refers to model 'users.User' that has not been installed
    原因:usersapp未加入INSTALLED_APPS,或AUTH_USER_MODEL设置位置错误(在INSTALLED_APPS之前)。
    解决:检查settings.py中INSTALLED_APPS是否包含'users',且AUTH_USER_MODEL设置在INSTALLED_APPS之后。

  • 现象:createsuperuser后登录Admin,提示Please enter the correct email and password...,但邮箱密码确认无误
    原因:UserManager.create_superuser()中未调用user.set_password(password),或set_password被覆盖。
    解决:严格按本书代码,create_superuser必须调用create_user(已含set_password),或手动添加user.set_password(password)。

  • 现象:迁移后Admin页面显示<User: >(空字符串)
    原因:User.__str__()方法返回None或空字符串。
    解决:确保__str__()返回非空字符串,如return self.email。

  • 现象:python manage.py migrate报错django.db.migrations.exceptions.InconsistentMigrationHistory
    原因:数据库中已存在auth_user表(来自旧User模型),但新migration试图创建同名表。
    解决:必须先清空数据库(docker-compose down -v删除postgres_data卷),再执行makemigrations和migrate。这是重写用户模型的代价,也是本书强调“生产前规划”的原因。

5. django-allauth集成:从基础登录到邮箱验证、社交登录的工业级认证

Django内置的auth系统解决了“用户是谁”,但生产网站还需回答:“用户如何证明自己?”、“如何找回密码?”、“能否用微信/Google一键登录?”。本书第7章P137引入django-allauth,它不是简单替换login视图,而是提供一套可插拔、可定制、符合OAuth2/OpenID Connect标准的认证协议栈。跳过它,意味着你要手写邮箱验证码、密码重置Token、社交登录OAuth Flow——这些全是安全敏感的黑匣子。

5.1 安装与基础配置:INSTALLED_APPS与MIDDLEWARE的精确排布

django-allauth需在settings/base.py中精确配置,顺序错误将导致认证失效:

# settings/base.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # allauth required 'django.contrib.sites', # 必须!用于多站点支持 # allauth apps 'allauth', 'allauth.account', 'allauth.socialaccount', # 如需社交登录 # 'allauth.socialaccount.providers.google', # 示例:Google Provider # 你的app 'users', 'pages', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # allauth middleware(必须在AuthenticationMiddleware之后) 'allauth.account.middleware.AccountMiddleware', ] # allauth required settings SITE_ID = 1 # 对应django_site表的id,allauth依赖 LOGIN_REDIRECT_URL = '/' # 登录后跳转 LOGOUT_REDIRECT_URL = '/' # 登出后跳转 ACCOUNT_LOGOUT_ON_GET = True # 点击登出链接即登出(无需确认页) ACCOUNT_EMAIL_REQUIRED = True # 注册时必须填邮箱 ACCOUNT_USERNAME_REQUIRED = False # 不需要用户名(与自定义User模型一致) ACCOUNT_AUTHENTICATION_METHOD = 'email' # 用邮箱认证 ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # 强制邮箱验证

注意:'django.contrib.sites'和SITE_ID = 1是allauth的硬性依赖,缺一不可。ACCOUNT_AUTHENTICATION_METHOD = 'email'必须与User.USERNAME_FIELD = 'email'保持一致。

5.2 URL路由:覆盖Django默认auth,接管所有认证入口

在mysite/urls.py中,用allauth的URL覆盖Django内置auth:

# mysite/urls.py from django.contrib import admin from django.urls import path, include from django.views.generic import TemplateView urlpatterns = [ path('admin/', admin.site.urls), # allauth URLs(必须放在最前,确保覆盖) path('accounts/', include('allauth.urls')), # 其他app path('', include('pages.urls')), ]

allauth.urls自动注册以下路径:

  • /accounts/login/→ 登录页
  • /accounts/logout/→ 登出
  • /accounts/signup/→ 注册页(带邮箱验证)
  • /accounts/password/reset/→ 密码重置
  • /accounts/confirm-email/→ 邮箱验证确认页

5.3 邮箱后端配置:从console到SMTP,生产环境必须实名

开发阶段用console后端看邮件内容,但生产必须用真实SMTP。本书第9章P175强调:邮箱是用户身份的延伸,不可伪造。配置settings/base.py:

# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # 开发用 # 生产环境(以腾讯企业邮箱为例) EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.exmail.qq.com' EMAIL_PORT = 465 EMAIL_USE_SSL = True EMAIL_HOST_USER = 'noreply@yourdomain.com' # 发件邮箱 EMAIL_HOST_PASSWORD = 'your_app_password' # 应用专用密码,非邮箱密码 DEFAULT_FROM_EMAIL = 'Your Site <noreply@yourdomain.com>'

提示:EMAIL_HOST_PASSWORD必须是邮箱服务商提供的“应用专用密码”(App Password),开启SMTP服务后生成。绝不可使用邮箱登录密码,这是安全红线。

5.4 自定义模板:覆盖allauth默认UI,融入项目品牌

allauth提供默认模板,但生产网站必须定制。创建目录结构:

templates/ ├── account/ │ ├── base.html # 所有account页面继承 │ ├── login.html # 登录页 │ ├── signup.html # 注册页 │ ├── email_confirm.html # 邮箱确认页 │ └── password_reset.html # 密码重置页

templates/account/base.html示例(精简):

<!-- templates/account/base.html --> {% extends "base.html" %} {% block content %} <div class="container mt-5"> <div class="row justify-content-center"> <div class="col-md-6"> {% if messages %} {% for message in messages %} <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert"> {{ message }} <button type="button" class="btn-close"><!-- templates/account/login.html --> {% extends "account/base.html" %} {% block inner_content %} <h2>Login</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit" class="btn btn-primary">Login</button> </form> <p><a href="{{ signup_url }}">Don't have an account? Sign up</a></p> {% endblock %}

5.5 避坑:allauth集成的高频故障(现象→原因→解决)

  • 现象:访问/accounts/login/报错Reverse for 'account_login' not found
    原因:urls.py中include('allauth.urls')的路径未匹配,或INSTALLED_APPS中'allauth.account'未启用。
    解决:确认INSTALLED_APPS包含'allauth.account',且urls.py中path('accounts/', include('allauth.urls'))路径正确。

  • 现象:注册后收不到验证邮件,console后端也无输出
    原因:ACCOUNT_EMAIL_VERIFICATION = 'mandatory'要求邮箱验证,但EMAIL_BACKEND未正确配置或DEFAULT_FROM_EMAIL格式错误(必须含<email>)。
    解决:检查EMAIL_BACKEND配置,用python manage.py sendtestemail admin@example.com测试。

  • 现象:登录成功后跳转到/accounts/profile/(404),而非LOGIN_REDIRECT_URL
    原因:allauth检测到用户有accountapp的UserProfile模型,试图跳转。
    解决:删除accountapp相关配置,或在settings.py中显式设置LOGIN_REDIRECT_URL。

  • 现象:/accounts/signup/页面显示This field is required.(邮箱字段)
    原因:User模型的email字段未设blank=False,或UserManager.create_user未校验邮箱。
    解决:确保User.email = models.EmailField(unique=True),且UserManager.create_user有if not email:校验。

6. 环境变量与Docker Compose进阶:从.env到健康检查,构建可交付的部署包

生产环境没有DEBUG=True的宽容,也没有SECRET_KEY明文写在代码里的侥幸。本书第8章P167和第17章P320反复强调:环境变量是生产与开发的楚河汉界,而Docker Compose是这条界线的物理实现。本章将带你把.env、docker-compose.prod.yml、健康检查、多阶段构建全部串起来,产出一个可直接交付运维的部署包。

6.1 .env文件:隔离敏感信息,让Git仓库真正安全

创建项目根目录下的.env文件(务必加入.gitignore):

# .env - 开发环境变量 DEBUG=1 SECRET_KEY=django-insecure-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend # 生产环境变量(另存为 .env.prod,不提交) # DEBUG=0 # SECRET_KEY=your-production-secret-key-here # DATABASE_URL=postgresql://produser:prodpass@prod-db:5 <p> <a href="https://download.csdn.net/download/weixin_31569663/90830803" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询