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

Django创建超级用户时roll_number唯一约束错误求助

Django超级用户创建时roll_number唯一约束问题解决方案

问题背景

开发高校学生联系Django应用时遇到以下矛盾:

  • 学生注册表单要求roll_number字段必填,因此模型中该字段不能设置null=True或blank=True
  • 创建超级用户时不需要填写roll_number,但执行createsuperuser命令时触发django.db.utils.IntegrityError: UNIQUE constraint failed: accounts_account.roll_number错误

错误原因

创建超级用户时未给roll_number赋值,字段默认存储为空字符串。由于roll_number设置了unique=True,空字符串会被视为重复值,导致数据库唯一约束冲突。

解决方案

方法一:创建超级用户时自动生成唯一roll_number

修改用户管理器的create_superuser方法,自动生成一个不会与学生学号冲突的唯一标识,确保超级用户的roll_number字段有值且唯一。

修改后的accounts/manager.py:

from django.contrib.auth.models import BaseUserManager
import uuid

class UserManager(BaseUserManager):
    def create_user(self, email, first_name, last_name, roll_no, password):
        if not email:
            raise ValueError("User requires an Email")
        if not first_name:
            raise ValueError("User requires a First name")
        if not roll_no:
            raise ValueError("User requires a Roll Number")

        # 修复字段名匹配错误:模型字段是roll_number,不是roll_no
        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            roll_number=roll_no,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(
        self, email, first_name, last_name, password, department="OTHER"
    ):
        if not email:
            raise ValueError("Admin requires an Email")
        if not first_name:
            raise ValueError("Admin requires a First name")

        # 生成唯一的管理员专属roll_number,避免与学生学号冲突
        admin_roll_number = f"ADMIN-{uuid.uuid4().hex[:6]}"
        # 确保生成的编号不重复
        while self.model.objects.filter(roll_number=admin_roll_number).exists():
            admin_roll_number = f"ADMIN-{uuid.uuid4().hex[:6]}"

        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            department=department,
            roll_number=admin_roll_number,
        )
        user.set_password(password)
        user.is_staff = True
        user.is_admin = True
        user.is_superadmin = True
        user.is_active = True
        user.save(using=self._db)
        return user

方法二:通过模型验证区分超级用户与普通用户

在模型的clean方法中添加逻辑:非超级用户必须填写roll_number且保证唯一;超级用户自动生成唯一roll_number。

修改后的accounts/models.py:

from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from .manager import UserManager
from django.core.exceptions import ValidationError
import uuid

class Account(AbstractBaseUser):
    # choices for department
    class Department(models.TextChoices):
        CSE = "COMPUTER SCIENCE"
        IT = "INFORMATION TECHNOLOGY"
        EE = "ELECTRIC ENGINEERING"
        CE = "CIVIL ENGINEERING"
        TE = "TEXTILE ENGINEERING"
        ME = "MECHANICAL ENGINEERING"
        OT = "OTHER"

    email = models.EmailField(max_length=100, unique=True)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    roll_number = models.CharField(max_length=10, unique=True)

    department = models.CharField(
        max_length=30,
        choices=Department.choices,
        default=Department.OT,
    )

    # required
    date_joined = models.DateTimeField(auto_now=True)
    last_login = models.DateTimeField(auto_now_add=True)
    is_admin = models.BooleanField(default=False)
    is_superadmin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)

    USERNAME_FIELD: str = "email"
    REQUIRED_FIELDS = ["first_name", "last_name", "department"]

    object = UserManager()

    def __str__(self) -> str:
        if self.roll_number:
            return "{} - {}".format(self.email, self.roll_number)
        return self.email

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, add_label):
        return True

    def clean(self):
        super().clean()
        # 非超级用户必须填写roll_number,且不能与其他学生重复
        if not self.is_superadmin:
            if not self.roll_number:
                raise ValidationError("Roll number is required for non-admin users")
            existing_student = Account.objects.filter(
                roll_number=self.roll_number
            ).exclude(id=self.id).exclude(is_superadmin=True)
            if existing_student.exists():
                raise ValidationError("This roll number is already used by another student")
        # 超级用户自动生成唯一roll_number
        elif not self.roll_number:
            admin_roll_number = f"ADMIN-{uuid.uuid4().hex[:6]}"
            while Account.objects.filter(roll_number=admin_roll_number).exists():
                admin_roll_number = f"ADMIN-{uuid.uuid4().hex[:6]}"
            self.roll_number = admin_roll_number

同时确保create_superuser方法中不需要手动处理roll_number,模型的clean方法会自动完成赋值。

注意事项

  • 原代码中create_user方法存在字段名不匹配问题:模型字段是roll_number,但代码中写的是roll_no=roll_no,该错误会导致普通用户创建失败,需优先修复。
  • 生成管理员roll_number时,可根据实际需求调整格式(比如使用固定前缀+自增数字),只要保证与学生学号不冲突即可。

内容的提问来源于stack exchange,提问作者Dhruv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 07:20:35