Wagtail 自定义用户模型(Custom User Models)完整指南:模型、表单、模板与 UserViewSet 定制
【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail
导读
本文基于 Wagtail 官方文档《Custom user models》并结合当前仓库源码,系统讲解如何在 Wagtail 项目中自定义 Django 用户模型(AUTH_USER_MODEL),并为 Wagtail 后台的用户管理界面定制表单、模板与视图。你将掌握从「创建自定义 User 模型」到「通过自定义AppConfig接入UserViewSet」的完整链路,并了解 Wagtail 内部表单、模板与视图的底层实现细节,可直接套用到实际项目中。
一、为什么 Wagtail 需要自定义用户模型
Wagtail 是一个基于 Django 的内容管理系统,其后台用户管理模块(wagtail/users)默认针对 Django 标准的auth.User模型构建。当项目需要在用户上扩展业务字段——例如会员等级、国家地区、头像附件等——就必须遵循 Django 的「可替换用户模型」机制,将AUTH_USER_MODEL指向自定义模型,并让 Wagtail 后台的增删改查界面同步支持这些新字段。
从仓库源码看,Wagtail 对自定义用户模型有相当完善的适配:
- wagtail/users/forms.py 中所有表单通过
get_user_model()动态获取当前生效的用户模型(第 24 行User = get_user_model()),并在 standard_fields 中声明了每个用户模型至少应具备的标准字段集合:email、first_name、last_name、is_superuser、groups; - wagtail/users/views/users.py 中的
UserViewSet直接以get_user_model()作为model(第 44 行、第 348 行),并使用User.USERNAME_FIELD动态适配用户名/登录名字段; - Wagtail 自带的测试项目中就维护着一个完整的自定义用户模型示例 wagtail/test/customuser/models.py,用于验证整套机制。
二、创建自定义用户模型
2.1 模型的最低要求
自定义用户模型至少必须继承Django 的AbstractBaseUser与PermissionsMixin,以保证具备密码存储、登录会话与权限系统的基础能力。官方示例直接继承AbstractUser(它已同时包含上述两个基类的功能),并新增两个业务字段:
# myapp/models.py from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): country = models.CharField(verbose_name="country", max_length=255) status = models.ForeignKey( MembershipStatus, on_delete=models.SET_NULL, null=True, default=1 )MembershipStatus是另一个自定义模型(文档中未展示其定义),status外键通过on_delete=models.SET_NULL保证关联记录删除时用户记录不被误删。
如果选择继承更底层的AbstractBaseUser + PermissionsMixin(不继承AbstractUser),则需要自行定义USERNAME_FIELD、REQUIRED_FIELDS、objects管理器以及get_full_name()/get_short_name()等方法。仓库中的测试示例 wagtail/test/customuser/models.py 即采用这种完整自定义写法,可以作为参考:
class CustomUser(index.Indexed, AbstractBaseUser, PermissionsMixin): identifier = ConvertedValueField(primary_key=True) username = models.CharField(max_length=100, unique=True) email = models.EmailField(max_length=255, blank=True) is_staff = models.BooleanField(default=True) is_active = models.BooleanField(default=True) first_name = models.CharField(max_length=50, blank=True) last_name = models.CharField(max_length=50, blank=True) country = models.CharField(max_length=100, blank=True) attachment = models.FileField(blank=True) USERNAME_FIELD = "username" REQUIRED_FIELDS = ["email"] objects = CustomUserManager()注意该测试模型还继承了index.Indexed,将自定义字段(country、first_name等)注册为 Wagtail 搜索索引字段,这意味着自定义用户模型同样可以接入 Wagtail 的搜索后端。
2.2 接入 INSTALLED_APPS 与 AUTH_USER_MODEL
- 将包含用户模型的应用加入
INSTALLED_APPS,必须位于'wagtail.users'之前,以便覆盖 Wagtail 内置模板(Django 模板加载按 INSTALLED_APPS 顺序查找); - 设置
AUTH_USER_MODEL指向该模型:
AUTH_USER_MODEL = "myapp.User"⚠️ Django 官方强烈建议:
AUTH_USER_MODEL必须在首次执行数据库迁移之前设定。项目初始化阶段就应确定用户模型,中途更换需要额外数据迁移,成本很高。
三、创建自定义用户表单(UserEditForm / UserCreationForm)
Wagtail 后台创建/编辑用户时,默认使用 wagtail/users/forms.py 中的UserCreationForm与UserEditForm。要让后台表单支持自定义字段,需要继承它们并扩展字段集合:
# myapp/forms.py from django import forms from django.utils.translation import gettext_lazy as _ from wagtail.users.forms import UserEditForm, UserCreationForm from myapp.models import MembershipStatus class CustomUserEditForm(UserEditForm): status = forms.ModelChoiceField( queryset=MembershipStatus.objects, required=True, label=_("Status") ) # 利用 ModelForm 的自动字段生成能力处理 country 字段, # 同时为 status 显式声明自定义表单字段。 class Meta(UserEditForm.Meta): fields = UserEditForm.Meta.fields | {"country", "status"} class CustomUserCreationForm(UserCreationForm): status = forms.ModelChoiceField( queryset=MembershipStatus.objects, required=True, label=_("Status") ) class Meta(UserCreationForm.Meta): fields = UserCreationForm.Meta.fields | {"country", "status"}关键实现细节(源码佐证):
- 基类
UserCreationForm.Meta.fields定义为{User.USERNAME_FIELD} | standard_fields(wagtail/users/forms.py),UserEditForm.Meta.fields为{User.USERNAME_FIELD, "is_active"} | standard_fields(wagtail/users/forms.py)。因此子类用|集合运算追加{"country", "status"}即可,country会由 ModelForm 依据模型字段自动生成表单字段(CharField),而status使用显式声明的ModelChoiceField; - 基类
UserForm已声明了email、first_name、last_name、password1、password2、is_superuser等标准字段,并对用户名做重复性校验(_clean_username,见 wagtail/users/forms.py)、密码一致性校验与 Django 密码策略校验(validate_password,见 wagtail/users/forms.py); UserEditForm在editing_self=True(用户编辑自己的资料)时会移除is_active和is_superuser字段,防止用户自我提权(wagtail/users/forms.py),此逻辑在自定义表单中同样生效;- 密码字段默认
required=False,是否必填由设置项WAGTAILUSERS_PASSWORD_REQUIRED(默认True)控制,可通过WAGTAILUSERS_PASSWORD_ENABLED(默认True)整体启用/停用密码字段(见 wagtail/users/forms.py)。
仓库测试中的等价实现可参考 wagtail/test/customuser/forms.py,其中用country(CharField)与attachment(FileField)两个字段演示了「自动生成」与「显式声明」两种字段来源。
四、扩展创建与编辑模板
自定义字段要渲染到后台页面,需要覆盖 Wagtail 的用户创建/编辑模板。扩展模板需放在任意合法模板目录下的wagtailusers/users/子目录中——例如myapp/templates/wagtailusers/users/(正是由于自定义应用排在'wagtail.users'之前,Django 才能优先命中这些模板)。
myapp/templates/wagtailusers/users/create.html:
{% extends "wagtailusers/users/create.html" %} {% block extra_fields %} <li>{% include "wagtailadmin/shared/field.html" with field=form.country %}</li> <li>{% include "wagtailadmin/shared/field.html" with field=form.status %}</li> {% endblock extra_fields %}myapp/templates/wagtailusers/users/edit.html:
{% extends "wagtailusers/users/edit.html" %} {% block extra_fields %} <li>{% include "wagtailadmin/shared/field.html" with field=form.country %}</li> <li>{% include "wagtailadmin/shared/field.html" with field=form.status %}</li> {% endblock extra_fields %}模板扩展点说明:
extra_fields块:插槽位于默认模板中last_name字段之后、密码字段之前。查看默认模板 wagtail/users/templates/wagtailusers/users/create.html 与 wagtail/users/templates/wagtailusers/users/edit.html 可以看到{% block extra_fields %}{% endblock extra_fields %}的确切位置;fields块:包裹整个字段列表。可以完全重定义所有字段的顺序,或在列表开头/结尾追加字段;- 两个默认模板均采用「Account(账户) / Roles(角色)」双 Tab 布局:账户页签渲染用户名、邮箱、姓名、密码等,角色页签渲染
is_superuser与groups分组选择。默认模板中使用的是{% formattedfield %}模板标签而非文档示例中的wagtailadmin/shared/field.html片段——两种写法均可,前者是更现代的替代方案; - 编辑模板在
form.is_active存在时还会渲染「Active」开关(wagtail/users/templates/wagtailusers/users/edit.html),若你的自定义模型删除了is_active字段,该片段会自动跳过。
五、创建自定义 UserViewSet 并接入
5.1 自定义 UserViewSet
仅定义表单还不够,Wagtail 后台默认的UserViewSet仍会使用内置的UserCreationForm/UserEditForm。需要继承wagtail.users.views.users.UserViewSet并覆写get_form_class:
# myapp/viewsets.py from wagtail.users.views.users import UserViewSet as WagtailUserViewSet from .forms import CustomUserCreationForm, CustomUserEditForm class UserViewSet(WagtailUserViewSet): def get_form_class(self, for_update=False): if for_update: return CustomUserEditForm return CustomUserCreationFormfor_update=False对应创建(add)场景,for_update=True对应编辑(edit)场景——这与源码中get_add_view_kwargs/get_edit_view_kwargs的调用方式一一对应(wagtail/admin/viewsets/model.py)。
仓库测试中的同款实现见 wagtail/test/customuser/viewsets.py,并额外展示了通过icon = "custom-icon"更换后台图标;对应的测试用例 wagtail/users/tests/test_admin_views.py 验证了视图集能正确返回自定义表单类。
5.2 通过自定义 AppConfig 替换 wagtail.users
接下来把自定义视图集注册到应用配置中。在项目主包(包含 settings 与 urls 的包)下创建/编辑apps.py:
# myproject/apps.py from wagtail.users.apps import WagtailUsersAppConfig class CustomUsersAppConfig(WagtailUsersAppConfig): user_viewset = "myapp.viewsets.UserViewSet"然后替换INSTALLED_APPS中的wagtail.users:
INSTALLED_APPS = [ ..., # 注意保留两个独立条目: "myapp", # 包含自定义用户模型的 app "myproject.apps.CustomUsersAppConfig", # wagtail.users 的自定义 AppConfig # "wagtail.users", # 应删除,替换为上面的自定义 AppConfig ..., ]原理说明:默认的 WagtailUsersAppConfig 中声明了user_viewset = "wagtail.users.views.users.UserViewSet"与group_viewset = "wagtail.users.views.groups.GroupViewSet"两个类属性,并负责在ready()中为User和Group注册权限策略。通过点路径字符串覆写user_viewset,Wagtail 即可延迟加载并使用你的自定义视图集。
5.3 重要警告:不要合并两个 AppConfig
文档特别提醒:如果想把WagtailUsersAppConfig子类放进自定义用户模型所在 app 的apps.py,必须新建一个独立的配置类,而不要把现有的AppConfig子类直接改成继承WagtailUsersAppConfig——否则 Django 会把你的自定义用户模型误判为wagtail.users的一部分,引发模型归属混乱。同时,如果该 app 的AppConfig在INSTALLED_APPS中不是用点路径显式引用,可能还需要给自有AppConfig设置default = True。
六、UserViewSet 的更多定制空间
UserViewSet继承自wagtail.admin.viewsets.model.ModelViewSet(见 wagtail/admin/viewsets/model.py),因此可以复用 ModelViewSet 提供的绝大多数定制能力。
6.1 自定义模板目录(template_prefix)
class UserViewSet(WagtailUserViewSet): template_prefix = "myapp/users/"设置后,各视图会按{template_prefix}{app_label}/{model_name}/{name}.html→{template_prefix}{app_label}/{name}.html→{template_prefix}{name}.html的顺序查找模板,找不到再回退到默认模板(实现见 wagtail/admin/viewsets/model.py)。默认情况下UserViewSet.template_prefix = "wagtailusers/users/"(wagtail/users/views/users.py)。
6.2 精确指定创建/编辑模板
class UserViewSet(WagtailUserViewSet): create_template_name = "myapp/users/create.html" edit_template_name = "myapp/users/edit.html"6.3 其他可复用能力
从 ModelViewSet 源码看,还可定制:list_per_page(分页,默认 20)、ordering(排序)、filterset_class(列表筛选)、menu_label/menu_order/icon(菜单项)、inspect_view_enabled与inspect_view_fields(详情视图)、copy_view_enabled(复制视图)、自定义搜索字段search_fields等。UserViewSet自身还固化了若干行为:add_to_reference_index = False、add_to_settings_menu = True(用户管理入口位于「设置」菜单)、并注册了名为users的后台搜索区(wagtail/users/views/users.py)。
6.4 组的定制
组(Group)的表单与视图可用类似方式定制:默认GroupForm与GroupViewSet分别位于 wagtail/users/forms.py 和 wagtail/users/views/groups.py,通过覆写WagtailUsersAppConfig.group_viewset即可接入自定义 GroupViewSet。更详细的组视图定制参见 docs/extending/customizing_group_views.md。
七、完整流程清单
把以上步骤串起来,自定义用户模型的完整接入流程为:
- 在业务 app(如
myapp)中定义继承AbstractUser(或AbstractBaseUser + PermissionsMixin)的用户模型,追加业务字段; - 将
myapp加入INSTALLED_APPS(置于wagtail.users之前),并设置AUTH_USER_MODEL = "myapp.User"; - 执行
makemigrations与migrate,完成自定义用户表建表(必须在首次迁移前设置好AUTH_USER_MODEL); - 继承
UserCreationForm/UserEditForm定义CustomUserCreationForm/CustomUserEditForm,用Meta.fields | {...}追加字段; - 在
myapp/templates/wagtailusers/users/下创建create.html/edit.html,通过{% block extra_fields %}渲染新增字段; - 继承
UserViewSet覆写get_form_class; - 在项目主包
apps.py中创建WagtailUsersAppConfig子类并覆写user_viewset; - 将
INSTALLED_APPS中的wagtail.users替换为自定义 AppConfig 点路径; - 重启开发服务器,在后台「设置 → 用户」中验证新增字段的创建与编辑是否正常渲染、保存。
参考资源(仓库内)
- 官方文档:docs/advanced_topics/customization/custom_user_models.md
- 表单实现:wagtail/users/forms.py
- 用户视图集实现:wagtail/users/views/users.py
- AppConfig 实现:wagtail/users/apps.py
- 默认创建/编辑模板:wagtail/users/templates/wagtailusers/users/create.html、wagtail/users/templates/wagtailusers/users/edit.html
- ModelViewSet 基类:wagtail/admin/viewsets/model.py
- 仓库内完整测试示例:wagtail/test/customuser/models.py、wagtail/test/customuser/forms.py、wagtail/test/customuser/viewsets.py
- 相关测试:wagtail/users/tests/test_admin_views.py
【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考