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,之前表存在时没有暴露:
generate_vid函数的return语句写在while循环内部,第一次生成随机串时不管是否重名都会直接返回,完全没有实现循环校验唯一性的设计目标- 生成v_id的随机字符集包含了
g,不符合十六进制字符规范 parent_id外键关联的是User表的字符串类型主键v_id,设置default=0存在类型不匹配问题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
相关产品推荐
相关产品推荐

