I migrated a ForeignKey
to a GenericForeignKey
, using the contrib.contenttypes
framework. To access the ContentType
object I need to migrate the data, I used this code:
ContentType = apps.get_model('contenttypes', 'ContentType')
my_model_content_type = ContentType.objects.get(
app_label='my_app',
model='my_model'
)
The migration works when I run manage.py migrate
, and I can then play with the updated model in the shell without problems.
However, when I attempt to run manage.py test
, I get the following error in the ContentTypes.object.get()
line:
__fake__.DoesNotExist: ContentType matching query does not exist.
Querying for ContentType.objects.all()
at that time returns an empty queryset.
I have tried (as directed by another answer here in SO) to run this before my query, but to no avail:
update_contenttypes(apps.app_configs['contenttypes'])
update_contenttypes(apps.app_configs['my_app'])
How can I ensure that the ContentType
rows exist at that point in the test database migration?
Migrations are Django’s way of propagating changes you make to your models (adding a field, deleting a model, etc.) into your database schema. They’re designed to be mostly automatic, but you’ll need to know when to make migrations, when to run them, and the common problems you might run into.
We use a very strict django project setup with several quality checks for our migrations: We write all data migration as typed functions in our main source code. Then we check everything with mypy and test as regular functions
In order to guarantee that all TestCasecode starts with a clean database, the Django test runner reorders tests in the following way: All TestCasesubclasses are run first. Then, all other Django-based tests (test cases based on SimpleTestCase, including TransactionTestCase) are run with no particular ordering guaranteed nor enforced among them.
You can test forward and rollback migrations and their ordering with the help of django-test-migrations. It is simple, friendly, and already works with the test framework of your choice. I also want to say “thank you” to these awesome people. Without their work it would take me much longer to come up with the working solution.
This is what ended up working for me. First, import update_contenttypes
:
from django.contrib.contenttypes.management import update_contenttypes
Second, list the initial ContentType
migration as a dependency:
dependencies = [
('contenttypes', '0001_initial'),
...
]
Finally, in the forward
migration function (invoked via RunPython
in the migration operations
):
# Ensure ContentType objects exist at this point:
app_config = apps.get_app_config('my_app')
app_config.models_module = app_config.models_module or True
update_contenttypes(app_config)
You may need to run the above code for more than one app_config
. You can obtain the all the app_config
objects using apps.get_app_configs()
and iterate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With