如何在Django中建模并实现账号与账号专属用户功能?
嘿,我之前刚好碰到过完全一样的需求——给Django项目做「账号(Account)」+「账号专属用户」的分层,而且必须要兼容自带的Admin后台,折腾了好一阵才找到顺手的方案,分享给你参考:
核心是要保留Django Auth的原生能力(登录、权限、Session这些),同时把Account作为顶层实体,让多个User关联到同一个Account。我试过直接新增Account和AccountUser中间表,但Admin适配确实麻烦,后来调整成「Profile关联」或者「自定义User模型」的方式,就顺畅多了。
方案一:基于原生User的Profile关联(适合已启动的项目)
如果你的项目已经用了原生的auth.User,不想替换它,就用这种方式:
1. 定义Account主模型
先建一个代表「账号」的模型,这是所有用户的顶层归属:
from django.db import models class Account(models.Model): name = models.CharField(max_length=100, unique=True, verbose_name="账号名称") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # 可以根据需求加其他字段,比如账号类型、过期时间、企业信息等 def __str__(self): return self.name
2. 用Profile关联User和Account
创建一个和User一对一关联的Profile模型,用来存储用户和账号的绑定关系,还能加账号内的角色:
from django.contrib.auth.models import User class AccountUserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="account_profile", verbose_name="关联用户") account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="users", verbose_name="所属账号") # 可选:账号内的角色区分 ROLE_CHOICES = ( ('admin', '账号管理员'), ('member', '普通成员'), ) role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='member', verbose_name="账号角色") def __str__(self): return f"{self.user.username} - {self.account.name} ({self.get_role_display()})"
3. 适配Django Admin(重点解决你的痛点)
要让Admin能同时管理账号、用户,并且在用户页面直接看到账号关联信息,需要自定义Admin类:
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib import admin from .models import Account, AccountUserProfile # 给User页面加账号关联的Inline class AccountUserProfileInline(admin.StackedInline): model = AccountUserProfile can_delete = False verbose_name_plural = "账号关联信息" # 替换原生的UserAdmin class CustomUserAdmin(BaseUserAdmin): inlines = (AccountUserProfileInline,) # 可选:在用户列表页直接显示所属账号 list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'get_account_name') def get_account_name(self, obj): if hasattr(obj, 'account_profile'): return obj.account_profile.account.name return "未关联账号" get_account_name.short_description = "所属账号" admin.site.unregister(User) admin.site.register(User, CustomUserAdmin) # 给Account页面加下属用户的Inline class AccountUserInline(admin.TabularInline): model = AccountUserProfile fk_name = 'account' verbose_name_plural = "账号成员" class AccountAdmin(admin.ModelAdmin): inlines = (AccountUserInline,) list_display = ('name', 'created_at', 'get_member_count') def get_member_count(self, obj): return obj.users.count() get_member_count.short_description = "成员数量" admin.site.register(Account, AccountAdmin)
这样配置后,你在Admin里:
- 打开用户详情页,能直接设置/查看所属账号和角色
- 打开账号详情页,能看到所有关联的用户,还能直接添加新用户
- 用户列表页和账号列表页都有直观的统计信息
方案二:自定义User模型(适合新项目)
如果你的项目还在初期,强烈推荐直接自定义User模型,这样不用额外的Profile表,结构更简洁:
1. 先定义Account模型(和方案一一样)
2. 自定义User模型
from django.contrib.auth.models import AbstractUser class CustomUser(AbstractUser): account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True, blank=True, related_name="users", verbose_name="所属账号") # 可选:账号内角色 ROLE_CHOICES = ( ('admin', '账号管理员'), ('member', '普通成员'), ) account_role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='member', verbose_name="账号角色")
然后在settings.py里指定自定义User:
AUTH_USER_MODEL = 'your_app_name.CustomUser'
3. 适配Admin
这种方式的Admin适配更简单,直接继承UserAdmin并添加字段即可:
from django.contrib.auth.admin import UserAdmin from django.contrib import admin from .models import Account, CustomUser class CustomUserAdmin(UserAdmin): # 在用户列表页显示账号信息 list_display = ('username', 'email', 'account', 'account_role', 'is_staff') # 在编辑页面添加账号相关字段 fieldsets = UserAdmin.fieldsets + ( ('账号关联信息', {'fields': ('account', 'account_role')}), ) add_fieldsets = UserAdmin.add_fieldsets + ( ('账号关联信息', {'fields': ('account', 'account_role')}), ) admin.site.register(CustomUser, CustomUserAdmin) # Account的Admin配置和方案一类似,加用户Inline即可 class AccountUserInline(admin.TabularInline): model = CustomUser fk_name = 'account' verbose_name_plural = "账号成员" class AccountAdmin(admin.ModelAdmin): inlines = (AccountUserInline,) list_display = ('name', 'created_at', 'get_member_count') def get_member_count(self, obj): return obj.users.count() get_member_count.short_description = "成员数量" admin.site.register(Account, AccountAdmin)
如果需要做权限控制,比如普通管理员只能查看/编辑自己账号下的用户,可以在Admin的get_queryset方法里限制:
class CustomUserAdmin(UserAdmin): # ... 其他代码 def get_queryset(self, request): qs = super().get_queryset(request) if not request.user.is_superuser: return qs.filter(account=request.user.account) return qs
这个方案我在好几个生产项目里都用过,既保留了Django Auth的所有原生功能,又完美实现了「账号-用户」的分层,Admin的体验也和原生一致,应该能解决你的问题。
内容的提问来源于stack exchange,提问作者user2321428

