Django 抽象模型 + DB 迁移:测试抛出“无法更改表,因为它有待处理的触发事件”
Posted
技术标签:
【中文标题】Django 抽象模型 + DB 迁移:测试抛出“无法更改表,因为它有待处理的触发事件”【英文标题】:Django abstract model + DB migrations: tests throw "cannot ALTER TABLE because it has pending trigger events" 【发布时间】:2018-11-06 06:14:57 【问题描述】:我想编写一个抽象模型 mixin,我可以用它来建立 OneToOne - 与用户模型的关系。这是我的代码:
from django.conf import settings
from django.db import models
class Userable(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
class Meta:
abstract = True
我为此模型编写了以下测试:
class TestUserable(TestCase):
mixin = Userable
def setUp(self):
user = User.objects.create_user(
email="testuser@test.com",
name="Test User",
password="test1234test"
)
self.user = user
self.model = ModelBase(
'__TestModel__' + self.mixin.__name__, (self.mixin,),
'__module__': self.mixin.__module__
)
with connection.schema_editor() as schema_editor:
schema_editor.create_model(self.model)
def test_user(self):
self.model.objects.create(user=self.user)
self.assertEqual(self.model.objects.count(), 1)
def tearDown(self):
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(self.model)
我的问题是,tearDown()
方法中的这个测试会引发以下错误:
django.db.utils.OperationalError: cannot DROP TABLE "core___testmodel__userable" because it has pending trigger events
这可能是什么原因?我确实运行了 python manage.py makemigrations
和 python manage.py migrate
,但没有待处理的迁移(正如预期的那样,因为这是一个抽象模型)。
编辑:它似乎与 OneToOneFields 或 ForeignKeys(关系)有关。如果我将此代码用于常规字段(如 CharFields 或 IntegerFields),则它可以工作。
EDIT2:如果您有另一种更好的方法来测试使用 ForeignKeys 的抽象基础模型,请告诉我!
【问题讨论】:
如果你没有在setUp
..中重新分配self.model,那会不会那么混乱。
那是什么意思?第一个模型设置为Userable,然后设置为self.model?我可以用 mixin 或其他东西替换第一个 model=Userable :)
mixin
会更清楚..
我编辑了,谢谢你的建议。你知道如何解决这个问题吗?
很遗憾没有,不过我也对答案感兴趣..
【参考方案1】:
测试抽象模型的常见做法是为测试创建实际模型
这是model-utils
项目https://github.com/jazzband/django-model-utils/blob/master/tests/test_models/test_timestamped_model.py中的示例
from tests.models import UserableTest
class TestUserable(TestCase):
def setUp(self):
user = User.objects.create_user(
email="testuser@test.com",
name="Test User",
password="test1234test"
)
self.user = user
def test_user(self):
UserableTest.objects.create(user=self.user)
self.assertEqual(UserableTest.objects.count(), 1)
在这个项目中他们有单独的设置DJANGO_SETTINGS_MODULE = tests.settings
https://github.com/jazzband/django-model-utils/blob/master/tests/settings.py
INSTALLED_APPS = (
'model_utils',
'tests',
)
DATABASES =
'default':
'ENGINE': 'django.db.backends.sqlite3'
SECRET_KEY = 'dummy'
并且模型在https://github.com/jazzband/django-model-utils/blob/master/tests/models.py中有描述
from myapp.models import Userable
class UserableTest(Userable):
pass
【讨论】:
我试过这个解决方案。我觉得这可以工作。这是我所做的:我在 core/tests 中创建了一个 testapp。所以核心/测试/测试应用程序。然后我添加了一个从 base.py 和 local.py 继承的 settings.py。我尝试了makemigrations,但它没有迁移任何东西。我也收到错误:psycopg2.ProgrammingError: relation "core_userabletest" does not exist
.
@J.Hesters 你如何运行你的测试?
python manage.py test
然后是我正在测试的应用程序的名称。
给了你赏金,因为感觉它是最接近正确的,但我仍然无法解决我的问题:(【参考方案2】:
试试下面的代码。我已经测试过它可以正常工作。
requirements.txt
Django==1.11.13
pkg-resources==0.0.0
pytz==2018.4
models.py
from django.conf import settings
from django.db import models
class Userable(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
class Meta:
abstract = True
tests.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import TestCase
from .models import Userable
from django.db import connection
from django.db.models.base import ModelBase
from django.contrib.auth.models import User
class TestUserable(TestCase):
def setUp(self):
user = User.objects.create_user(
username="testuser@test.com",
password="test1234test"
)
self.user = user
self.model = ModelBase(
Userable.__name__,
(Userable,),
'__module__': Userable.__module__
)
with connection.schema_editor() as schema_editor:
schema_editor.create_model(self.model)
def test_user(self):
self.model.objects.create(user=self.user)
self.assertEqual(self.model.objects.count(), 1)
def tearDown(self):
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(self.model)
【讨论】:
这对我不起作用,因为我得到了相同错误的版本:django.db.utils.OperationalError: cannot DROP TABLE "core_userable" because it has pending trigger events
。我正在使用自定义用户模型,我不知道这是否会改变任何东西。
在您的数据库表<your table instead foo>
上尝试使用类似UPDATE foo SET bar = '' WHERE bar IS NULL
的sql 列<your column instead bar>
不幸的是,这不起作用,因为 Userable 是抽象的,因此没有相应的 SQL 迁移。以上是关于Django 抽象模型 + DB 迁移:测试抛出“无法更改表,因为它有待处理的触发事件”的主要内容,如果未能解决你的问题,请参考以下文章
有没有一种简单的方法可以将 Django 的模型和迁移链与 db 验证一致性进行比较?