You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django自定义用户模型实现合法性及优化建议咨询

Django自定义用户模型与角色关联Profile的代码审查与优化建议

作为编程新手,我重写了Django默认用户模型,将用户Profile与CustomUser关联,通过信号接收器在用户创建时根据角色自动生成对应Profile实例,以下是我的代码:

from django.db import models
from django.contrib.auth.models import AbstractUser
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from .managers  import CustomUserManager
from django.db.models.signals import post_save


# Create your models here.


class CustomUser(AbstractUser):
    username = None
    email = models.EmailField(_("email"),primary_key=True,unique=True,blank=False,null=False)
    phone_number = models.CharField(max_length=10,null=False,blank=False)
    ROLE_CHOICES = (
        ('ADMIN', 'ADMIN'),
        ('STUDENT','STUDENT'),
        ('TEACHER', 'TEACHER'),
        ('PRINCIPAL', 'PRINCIPAL'),
    )
    role = models.CharField(choices=ROLE_CHOICES, blank=True,default='ADMIN',max_length=10)   
    REQUIRED_FIELDS = ['phone_number','first_name','last_name']
    USERNAME_FIELD = "email"

    objects = CustomUserManager()

    def __str__(self):
        return self.email

   


class StudentProfile(models.Model):
    user = models.OneToOneField(CustomUser,on_delete=models.CASCADE)
    id = models.AutoField(primary_key=True,unique=True)
    merit = models.CharField(max_length=6,blank=True,null=True)

    def __str__(self):
        return self.user.email

class TeacherProfile(models.Model):
    user = models.OneToOneField(CustomUser,on_delete=models.CASCADE)
    id = models.AutoField(primary_key=True,unique=True)
    salary = models.CharField(max_length=6,blank=True,null=True)
    education = models.CharField(max_length=100,blank=True,null=True)

    def __str__(self):
        return self.user.email
    

class PrincipalProfile(models.Model):
    user = models.OneToOneField(CustomUser,on_delete=models.CASCADE)
    id = models.AutoField(primary_key=True,unique=True)
    salary = models.CharField(max_length=6,blank=True,null=True)
    education = models.CharField(max_length=100,blank=True,null=True)
    years_of_experience = models.IntegerField(null=True, blank=True)

    def __str__(self):
        return self.user.email
    


@receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        if instance.role == 'PRINCIPAL':
            PrincipalProfile.objects.create(user=instance)
        elif instance.role == 'STUDENT':
            StudentProfile.objects.create(user=instance)
        elif instance.role == 'TEACHER':
            TeacherProfile.objects.create(user=instance)
        else:
            pass
    else:
        print("Profile not created")

合法性审查

整体实现逻辑是合法的:

  • 继承AbstractUser自定义用户模型是Django官方推荐的扩展用户模型方式
  • 使用OneToOneField关联不同角色的Profile模型,符合一对一关联的业务逻辑
  • post_save信号自动创建Profile的思路,是Django中实现用户与档案联动的常规方案
  • 自定义用户模型的USERNAME_FIELD、REQUIRED_FIELDS配置正确,管理器引入也符合规范(假设CustomUserManager实现无误)

优化建议

1. 角色字段规范化

  • 用Django内置的TextChoices枚举类替代元组选项,避免硬编码字符串,同时支持国际化:
    from django.db.models import TextChoices
    
    class RoleChoices(TextChoices):
        ADMIN = 'ADMIN', _('ADMIN')
        STUDENT = 'STUDENT', _('STUDENT')
        TEACHER = 'TEACHER', _('TEACHER')
        PRINCIPAL = 'PRINCIPAL', _('PRINCIPAL')
    
  • 角色字段设置blank=False,因为已有默认值ADMIN,避免出现空值:
    role = models.CharField(choices=RoleChoices.choices, default=RoleChoices.ADMIN, max_length=10, blank=False)
    

2. Profile模型简化与字段优化

  • 删除手动定义的id = models.AutoField(primary_key=True,unique=True),Django会自动为每个模型生成自增主键,无需重复定义
  • 薪资字段salary改用DecimalField(数值类型)替代CharField,方便后续计算、排序:
    salary = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    
  • 为OneToOneField添加related_name,方便从用户反向查询关联档案:
    user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='student_profile')
    
    之后可以通过user.student_profile快速获取学生档案,无需额外查询。

3. 信号接收器优化

  • 移除else分支的print("Profile not created"),生产环境中print输出无意义,且用户更新时本就无需创建Profile
  • 用字典映射替代多分支if-elif,让代码更简洁易维护:
    PROFILE_MAP = {
        RoleChoices.PRINCIPAL: PrincipalProfile,
        RoleChoices.STUDENT: StudentProfile,
        RoleChoices.TEACHER: TeacherProfile,
    }
    
    @receiver(post_save, sender=CustomUser, dispatch_uid='create_user_profile_signal')
    def create_user_profile(sender, instance, created, **kwargs):
        if created:
            profile_class = PROFILE_MAP.get(instance.role)
            if profile_class:
                profile_class.objects.create(user=instance)
    
  • 添加dispatch_uid参数,避免信号因重复导入而重复注册

4. 细节完善

  • 给字段添加清晰的verbose_name,优化后台管理界面的显示:
    phone_number = models.CharField(_("phone number"), max_length=10, null=False, blank=False)
    
  • 给手机号添加格式验证,确保输入符合要求:
    from django.core.validators import RegexValidator
    
    phone_regex = RegexValidator(
        regex=r'^[0-9]{10}$',
        message="Phone number must be exactly 10 digits."
    )
    phone_number = models.CharField(
        _("phone number"),
        max_length=10,
        null=False,
        blank=False,
        validators=[phone_regex]
    )
    
  • 若需要支持用户角色变更时同步更新Profile,可在信号中处理created=False的情况,比如删除旧Profile并创建新的对应档案

内容的提问来源于stack exchange,提问作者Mr. Devloper_0310

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:36:11