Django执行manage.py test时无法向测试数据库迁移模型求助
我在运行Django单元测试时碰到了一个头疼的问题:执行./manage.py test时,测试数据库创建后直接抛出django.db.utils.ProgrammingError: relation "accounts_user" does not exist错误,而且测试数据库是空的也没被自动删除——哪怕我完全没有写任何测试代码,这个问题依然会出现。
根据Django文档,默认测试流程应该会自动创建测试数据库并执行migrate迁移模型,但现在显然迁移步骤没正常完成。我项目里用了自定义用户模型,还安装了django-registration,多个模型用models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE)关联用户,不确定到底是自定义模型的问题还是外键引用导致的。
我的自定义用户模型代码如下:
def user_image_path(instance, filename): ''' https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.FileField.upload_to ''' first_name = instance.first_name.lower().replace(" ", "_") last_name = instance.last_name.lower().replace(" ", "_") cleaned_filename = filename.lower().replace(" ", "_") return '{0}_{1}/images/avatar/{2}'.format(first_name, last_name, cleaned_filename) class UserManager(BaseUserManager): def create_user(self, email, first_name, last_name, username=None, password=None): if not email: raise ValueError("Users must have a valid email address") if not first_name and last_name: raise ValueError("Users must have a first and last name") user = self.model( email=self.normalize_email(email), first_name=first_name, last_name=last_name, ) user.set_password(password) user.save() return user def create_superuser(self, email, first_name, last_name, password): user = self.create_user( email=email, first_name=first_name, last_name=last_name, password=password, ) user.is_staff = True user.is_admin = True user.is_superuser = True user.save() return user class User(AbstractBaseUser, PermissionsMixin): ''' ''' email = models.EmailField(unique=True) first_name = models.CharField(max_length=40, blank=False) last_name = models.CharField(max_length=40, blank=False) avatar = models.ImageField(blank=True, null=True, upload_to=user_image_path) phone_number = models.CharField(max_length=15, blank=True, null=True) address_1 = models.CharField(max_length=128, blank=True, null=True) address_2 = models.CharField(max_length=128, blank=True, null=True) city = models.CharField(max_length=64, blank=True, null=True) YOUR_STATE_CHOICES = list(STATE_CHOICES) YOUR_STATE_CHOICES.insert(0, ('', '---------')) state = USStateField(blank=True, null=True, choices=YOUR_STATE_CHOICES) zip_code = models.CharField(max_length=5, blank=True, null=True) stripe_id = models.CharField(max_length=255, blank=True, null=True) email_user = models.EmailField(unique=True) date_joined = models.DateTimeField(auto_now_add=True) date_updated = models.DateTimeField(auto_now=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) is_admin = models.BooleanField(default=False) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['first_name','last_name',] def __str__(self): return "{} @{}, {}".format(self.email, self.last_name, self.first_name) def get_short_name(self): return self.last_name def get_full_name(self): return ' '.join([self.first_name, self.last_name]) def email_user(self, *args, **kwargs): send_mail( '{}'.format(args[0]), '{}'.format(args[1]), 'support@example.com', [self.email], fail_silently=False, )
环境信息:
- Django 2.0.1
- Postgresql
- macOS 10.13.2
- 依赖列表:appdirs1.4.3 beautifulsoup44.6.0 boto31.5.18 botocore1.8.32 certifi2018.1.18 chardet3.0.4 confusable-homoglyphs2.0.2 coverage4.4.2 Django2.0.1 django-appconf1.0.2 django-bootstrap40.0.5 django-classy-tags0.8.0 django-extensions1.9.9 django-localflavor2.0 django-registration2.3 django-storages1.6.5 djangorestframework3.7.7 docutils0.14 idna2.6 jmespath0.9.3 lxml4.1.1 olefile0.44 packaging16.8 Pillow5.0.0 psycopg22.7.3.2 pyparsing2.2.0 python-dateutil2.6.1 pytz2017.3 requests2.18.4 s3transfer0.1.12 six1.11.0 stripe1.77.1 typing3.6.2 urllib31.22
问题分析与解决方案
1. 核心问题:模型命名冲突
看你的User模型,发现了两个明显的冲突点:
- 你定义了
email_user字段,同时又定义了同名的email_user方法,这会直接覆盖Django内置的User.email_user方法,而且模型字段和方法重名会导致模型初始化、迁移时出现隐性异常,直接中断迁移流程,测试数据库自然不会生成accounts_user表。 - 两个
EmailField都设置了unique=True,如果没有处理空值的情况,后续也可能引发数据验证问题。
2. 分步修复方案
修复命名冲突
把email_user字段重命名(比如改为secondary_email),同时调整邮件发送方法的名称避免和内置方法冲突:# 替换原email_user字段 secondary_email = models.EmailField(unique=True, blank=True, null=True) # 替换原email_user方法 def send_notification_email(self, *args, **kwargs): send_mail( args[0], args[1], 'support@example.com', [self.email], fail_silently=False, )重置迁移文件
之前的迁移文件可能已经因为命名冲突损坏,需要重新生成:- 备份现有数据库
- 删除
accounts应用下migrations文件夹中除__init__.py外的所有文件 - 执行命令重新生成并应用迁移:
./manage.py makemigrations accounts ./manage.py migrate - 可以先手动测试测试数据库的迁移:
./manage.py migrate --database=test
验证django-registration适配
确保settings.py中已正确配置AUTH_USER_MODEL = 'accounts.User',如果使用了django-registration的表单类,需要确保表单继承自django.contrib.auth.forms.UserCreationForm并指定model = User。清理残留测试数据库
手动删除PostgreSQL中已创建的test_xxx格式测试数据库,避免旧的损坏数据干扰测试流程。
内容的提问来源于stack exchange,提问作者JillianSN

