Django3.2迁移报错UndefinedTable: relation "user" does not exist
Django 部署执行migrate报错:relation "user" does not exist
已尝试的排查操作
- 删除所有应用下的迁移文件
- 新建数据库并重新生成迁移文件,仍然出现相同报错
异常现象
- 部署完成后在服务器本地运行Django应用,使用
curl在服务器本地调用API(例如用户注册接口)可正常执行,但查看数据库时对应表为空,user表甚至无字段展示,其余表可正常查看字段。
- 通过服务器对外端点调用API时返回500错误,异常信息如下:
relation "user" does not exist
LINE 1: SELECT (1) AS "a" FROM "user" WHERE "user"."email" = ' - 执行
migrate后可正常创建超级用户,但尝试登录时返回500错误,同样提示UndefinedTable: relation "user" does not exist
基础环境与配置
- 运行环境:Python 3.10、Django 3.2
- 核心配置项:
AUTH_USER_MODEL = 'registration.User'
相关代码
models.py
from django.db import models import uuid from django.db import models from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.base_user import AbstractBaseUser from django.utils import timezone from .managers import CustomUserManager from rest_framework_simplejwt.tokens import RefreshToken AUTH_PROVIDERS = {'facebook': 'facebook', 'google': 'google', 'twitter': 'twitter', 'email': 'email'} class User(AbstractBaseUser, PermissionsMixin): A = 1 I = 2 C = 3 USER_ROLE = ( (A, 'A'), (I, 'I'), (C, 'C') ) uuid = models.UUIDField(unique=True, editable=False, default=uuid.uuid4) username = models.CharField(max_length=255, unique=True, db_index=True) email = models.EmailField(unique=True) first_name = models.CharField(max_length=50, blank=True) last_name = models.CharField(max_length=50, blank=True) role = models.PositiveSmallIntegerField(choices=USER_ROLE, blank=True, null=True) date_joined = models.DateTimeField(auto_now_add=True) is_verified = models.BooleanField(default=False) is_active = models.BooleanField(default=True) is_deleted = models.BooleanField(default=False) created_date = models.DateTimeField(default=timezone.now) modified_date = models.DateTimeField(default=timezone.now) auth_provider = models.CharField( max_length=255, blank=False, null=False, default=AUTH_PROVIDERS.get('email')) is_staff = models.BooleanField(default=False) is_superuser = models.BooleanField(default=False) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username', 'role'] objects = CustomUserManager() class Meta: db_table = 'user' verbose_name = 'user' verbose_name_plural = 'users' def __str__(self): return self.email def save(self, *args, **kwargs): self.is_staff = True if self.role == self.ADMIN else False self.is_superuser = True if self.role == self.ADMIN else False super(User, self).save(*args, **kwargs) def tokens(self): refresh = RefreshToken.for_user(self) return { 'refresh': str(refresh), 'access': str(refresh.access_token) }
managers.py(CustomUserManager)
from django.contrib.auth.base_user import BaseUserManager from django.utils.translation import ugettext_lazy as _ class CustomUserManager(BaseUserManager): """ 自定义用户管理器,以邮箱作为唯一认证标识 """ def create_user(self, username, email, role, password=None): if username is None: raise TypeError('Users should have a username') if email is None: raise TypeError('Users should have a Email') user = self.model(username=username, role=role, email=self.normalize_email(email)) user.set_password(password) user.save() return user def create_superuser(self, username, role, email, password=None): if password is None: raise TypeError('Password should not be none') user = self.create_user( username, email, role, password=password, ) user.is_staff = True user.is_superuser = True user.save(using=self._db) return user
views.py
renderer_classes = (UserRenderer,) permission_classes = [AllowAny] def post(self, request): user = request.data serializer = self.serializer_class(data=user) serializer.is_valid(raise_exception=True) serializer.save() user_data = serializer.data user = User.objects.get(email=user_data['email']) token = RefreshToken.for_user(user).access_token current_site = get_current_site(request).domain relativeLink = reverse('email-verify') absurl = 'http://' + current_site + relativeLink + "?token=" + str(token) email_body = 'Hi ' + user.username + \ ' Use the link below to verify your email \n' + absurl data = {'email_body': email_body, 'to_email': user.email, 'email_subject': 'Verify your email'} mail.send( [user.email], "abc@gmail.com", subject='Account Verification', message=email_body, priority='now', ) return Response(user_data, status=status.HTTP_201_CREATED)
serializer.py
class RegisterSerializer(serializers.ModelSerializer): password = serializers.CharField( max_length=68, min_length=6, write_only=True) role = serializers.IntegerField(write_only=True) default_error_messages = { 'username': 'The username should only contain alphanumeric characters'} class Meta: model = User fields = ['email', 'username', 'password', 'role'] def validate(self, attrs): email = attrs.get('email', '') username = attrs.get('username', '') role = attrs.get('role', '') if not username.isalnum(): raise serializers.ValidationError( self.default_error_messages) return attrs def create(self, validated_data): return User.objects.create_user(**validated_data)
修复步骤
- 优先排查服务多实例数据库连接不一致问题:本地curl调用正常、外部访问报错,是典型的多进程加载不同配置的表现——对外提供服务的Gunicorn/Uwsgi进程没有重启,仍在连接旧的空数据库/其他数据库实例,本地手动执行的runserver连接的是新建的数据库。先全量重启所有Django相关服务进程,确认所有进程加载同一份配置文件、连接同一个数据库地址。
- 修复代码硬bug:
User模型的save方法中引用了未定义的self.ADMIN常量,模型内仅定义了A/I/C三个角色常量,执行用户保存操作时会抛出属性错误,中断表创建/数据写入流程,将判断逻辑修改为对应常量:def save(self, *args, **kwargs): self.is_staff = True if self.role == self.A else False self.is_superuser = True if self.role == self.A else False super(User, self).save(*args, **kwargs)managers.py中导入的ugettext_lazy在Django 3.2版本已标记为废弃,替换为正式导入from django.utils.translation import gettext_lazy as _,避免导入异常。
- 重新执行迁移:
- 清空目标数据库所有表
- 删除所有app下
migrations目录内除__init__.py外的所有文件 - 依次执行
python manage.py makemigrations、python manage.py migrate - 重启所有服务进程,验证表结构与接口逻辑
内容的提问来源于stack exchange,提问作者wizard
相关产品推荐
相关产品推荐

