I’m working on a new Django project at the moment, and it uses the ContentTypes framework to refer to any model in the project. The problem with doing this is that the ContentType objects that are created have random IDs, so if you dump your database objects into a fixture, the ContentType objects those refer to will not be what you initially expected.
After a few days of searching and finding no solution, I got suggestions from a few people that I can use signals to detect when the objects were being created. My first thought was to delete the objects after creation so they could be loaded from a test fixture, but this had two problems: Firstly, how would I detect whether they are being created by Django’s setup or by my fixture, and secondly, it just didn’t work when I tried it.
I came upon a more elegant (but still incredibly hacky) solution to that after thinking a bit more. What I do now is dump my ContentTypes database to a fixture, and read the IDs of the objects from that fixture at testing time. Then, when the objects are being created, I override their IDs with mine. For this to work, the IDs have to be unused, so I set every other object’s ID to something past 1000.
I am not proud of this workaround, but it gets the job done, and this is better than not having any tests. Here is the code, you just need to put this on the top of your tests.py file, dump your ContentTypes table to a fixture and change the path the code to point to that fixture (do not add it to your test fixtures):
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import pre_save
from django.core import serializers
data = open("path/to/contenttypes.json").read()
deserialized = serializers.deserialize("json", data)
object_dict = dict([[object.object.model, object.object.id] for object in deserialized if isinstance(object.object, ContentType)])
counter = 1000
def set_content_type_id(sender, **kwargs):
content_type = kwargs.get('instance')
new_id = object_dict.get(content_type.model, None)
if new_id:
content_type.pk = object_dict[content_type.model]
else:
global counter
content_type.pk = counter
counter += 1
pre_save.connect(set_content_type_id, sender=ContentType)
I hope this saves you a few headaches and helps you get your unit tests running again.