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

Django makemigrations报错 自定义User关联base_user不存在

报错根因

Django执行所有manage.py类命令(包括makemigrations)时,启动流程会先运行内置系统检查,其中auth模块的校验逻辑会实例化自定义User模型,实例化过程会自动拉取所有字段的默认值,也就是直接触发你写的generate_vid函数执行。
这个函数内部直接调用ORM查询User.objects.filter(v_id = vid).exists()访问base_user表,但此时如果数据库中该表尚未创建(比如重建数据库、迁移状态损坏、测试环境初始化阶段),就会直接抛出relation "base_user" does not exist的错误。

另外你的代码里还存在几个隐藏bug,之前表存在时没有暴露:

  1. generate_vid函数的return语句写在while循环内部,第一次生成随机串时不管是否重名都会直接返回,完全没有实现循环校验唯一性的设计目标
  2. 生成v_id的随机字符集包含了g,不符合十六进制字符规范
  3. parent_id外键关联的是User表的字符串类型主键v_id,设置default=0存在类型不匹配问题
  4. UserManager.create_user中查询父用户的逻辑错误:get()返回单个模型实例,不是QuerySet,不能调用first()方法,取实例属性不能用字典下标访问

之前命令能正常运行、测试能通过,只是因为当时数据库里已经存在base_user表,检查阶段查表不会触发异常,一旦遇到表不存在的场景,哪怕模型代码没改动也会直接报错。

修复步骤
  • 第一步:改造generate_vid函数,增加表不存在场景的异常捕获,避免迁移阶段查表报错,同时修复循环逻辑和字符集错误:
from django.db import OperationalError, ProgrammingError
from django.utils.crypto import get_random_string

def generate_vid():
    """Generates a vid for the users"""
    try:
        while True:
            # 修正字符集,去掉多余的g,符合十六进制规范
            vid = get_random_string(10, 'abcdef0123456789')
            if not User.objects.filter(v_id=vid).exists():
                return vid
    # 捕获表不存在、连接异常等迁移阶段的常见错误
    except (OperationalError, ProgrammingError):
        # 迁移阶段表未创建时直接返回随机串,不做查表校验
        return get_random_string(10, 'abcdef0123456789')
  • 第二步:修复parent_id外键配置,去掉类型不匹配的0默认值,改为允许为空,根节点用户的父级设为NULL即可:
parent_id = models.ForeignKey(
    'self', 
    on_delete=models.SET_NULL, 
    null=True, 
    blank=True,
    verbose_name="父级用户"
)

对应的create_user逻辑也要同步修改,创建v类型用户时直接把parent_id设为None即可。

  • 第三步:修复UserManager.create_user中查询父用户的错误逻辑:
def create_user(self, username, password, **params):
    """Create and return a user"""
    u_type = params.pop('usertype')
    p_username = params.pop('parent_username', None)

    if u_type == 'v':
        params.update({'parent_id': None})
    else:
        parent_user = User.objects.get(username=p_username)
        params.update({'parent_id': parent_user})

    user = self.model(username=username, **params)
    user.set_password(password)
    user.save(using=self._db)
    return user
  • 第四步:如果当前是数据库被清空/重建的场景,修复完代码后重新执行迁移命令即可正常生成迁移文件、完成表结构创建。

注意:不要在字段默认值函数中直接编写强依赖数据库表存在的查询逻辑,这类逻辑会在Django启动检查、迁移阶段提前触发,很容易在表未创建时抛出异常。唯一性校验可以放到模型save()方法或者业务层创建用户的逻辑中实现,配合数据库层的主键唯一约束做兜底即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:54:17