Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django abstract model + DB migrations: tests throw "cannot ALTER TABLE because it has pending trigger events"

I want to write an abstract model mixin, that I can use to make OneToOne - relations to the user model. Here is my code:

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

I've written the following test for this model:

class TestUserable(TestCase):

    mixin = Userable

    def setUp(self):
        user = User.objects.create_user(
            email="[email protected]",
            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)

My problem is, that this test in it's tearDown() method throws the follwing error:

django.db.utils.OperationalError: cannot DROP TABLE "core___testmodel__userable" because it has pending trigger events

What could be the cause of this? I did run python manage.py makemigrations and python manage.py migrate, but there are no pending migrations (as is expected, since this is an abstract model).

EDIT: It seems to have something to do with OneToOneFields or ForeignKeys (relations). If I use this code altered for regular fields, like CharFields or IntegerFields, it works.

EDIT2: If you have another better way of testing abstract base model that use ForeignKeys, please let me know!

like image 353
J. Hesters Avatar asked May 27 '18 14:05

J. Hesters


People also ask

Why is my Django database not matching my models?

You have not manually edited your database - Django won’t be able to detect that your database doesn’t match your models, you’ll just get errors when migrations try to modify those tables. Migrations can be reversed with migrate by passing the number of the previous migration.

What is --fake-initial migration in Django?

When the migrate --fake-initial option is used, these initial migrations are treated specially. For an initial migration that creates one or more tables ( CreateModel operation), Django checks that all of those tables already exist in the database and fake-applies the migration if so.

What is a model in Django?

A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table. Each model is a Python class that subclasses django.db.models.Model. Each attribute of the model represents a database field.

Which Django version should I use for Migration?

If you are the maintainer of a third-party app with models, you may need to ship migrations that support multiple Django versions. In this case, you should always run makemigrations with the lowest Django version you wish to support.


1 Answers

Common practice for testing abstract models is to create actual models just for tests

here is example in model-utils project 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="[email protected]",
            name="Test User",
            password="test1234test"
        )
        self.user = user

    def test_user(self):
        UserableTest.objects.create(user=self.user)
        self.assertEqual(UserableTest.objects.count(), 1)

In this project they have separate settings 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'

And models are described in https://github.com/jazzband/django-model-utils/blob/master/tests/models.py

from myapp.models import Userable

class UserableTest(Userable):
    pass
like image 123
Sardorbek Imomaliev Avatar answered Sep 21 '22 17:09

Sardorbek Imomaliev