Problems with contenttypes when loading a fixture in Django

Viewed 49247

I am having trouble loading Django fixtures into my MySQL database because of contenttypes conflicts. First I tried dumping the data from only my app like this:

./manage.py dumpdata escola > fixture.json

but I kept getting missing foreign key problems, because my app "escola" uses tables from other applications. I kept adding additional apps until I got to this:

./manage.py dumpdata contenttypes auth escola > fixture.json

Now the problem is the following constraint violation when I try to load the data as a test fixture:

IntegrityError: (1062, "Duplicate entry 'escola-t23aluno' for key 2")

It seems the problem is that Django is trying to dynamically recreate contenttypes with different primary key values that conflict with the primary key values from the fixture. This appears to be the same as bug documented here: http://code.djangoproject.com/ticket/7052

The problem is that the recommended workaround is to dump the contenttypes app which I'm already doing!? What gives? If it makes any difference I do have some custom model permissions as documented here: http://docs.djangoproject.com/en/dev/ref/models/options/#permissions

16 Answers

Yes, this is really irritating. For a while I worked around it by doing a "manage.py reset" on the contenttypes app prior to loading the fixture (to get rid of the automatically-generated contenttypes data that differed from the dumped version). That worked, but eventually I got sick of the hassles and abandoned fixtures entirely in favor of straight SQL dumps (of course, then you lose DB portability).

update - the best answer is to use the --natural flag to dumpdata, as noted in an answer below. That flag did not exist yet when I wrote this answer.

You need to use natural keys to represent any foreign key and many-to-many relationships. Moreover, it might be a good idea to exclude the session table in the sessions app, and the logentry table in the admin app.

Django 1.7+

python manage.py dumpdata --natural-foreign --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > fixture.json

Django <1.7

python manage.py dumpdata --natural --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > fixture.json

According to the Django documentation, --natural has been deprecated in version 1.7, so the option --natural-foreign should be used instead.

You can also omit the primary key in the serialized data of this object since it can be calculated during deserialization by passing the --natural-primary flag.

python manage.py dumpdata --natural-foreign --natural-primary --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > fixture.json
./manage.py dumpdata app.Model --natural-foreign

will change

  "content_type": 123

to

  "content_type": [
    "app_label",
    "model"
  ],

And fixture works for TestCase now

Django 2.2.5

python manage.py dumpdata --exclude=contenttypes > datadump.json

it helped me

I tried every method from above, Nothing worked for me. I have to exclude the complete auth model and works fine.

python manage.py dumpdata --natural-primary --exclude=contenttypes --exclude=auth --exclude=admin.logentry --exclude=sessions.session --indent 4 > live.json

In my case I had dumped the data from auth (./manage.py dumpddata auth > fixtures/auth.json) to use the fixture for testing purposes.

The development continued and I removed most of the models I had defined in models.py and this is when I started to see this annoying problem.

My solution was regenerating the auth.json fixture again. This one had removed lots of entries in auth.permission related to the old models I had.

I've fixed this by adding in my tests setUp and tearDown

from django.core import management

=====

def setUp(self):
    management.call_command("loaddata", "all-data.yaml", verbosity=0)
    super(login_page_test, self).setUp()

def tearDown(self):
    management.call_command("flush", verbosity=0, interactive=False)
    super(login_page_test, self).setUp()
Related