Django testing and ContentType generic relatations fixtures
Asked Answered
B

3

10

How do I use GenericRelations in Django unit testing?

I have read and tried countless suggestions on the internets to no avail.

This one was promising Problems with contenttypes when loading a fixture in Django

but "reset" command is no longer there in Django 1.6

My project uses GenericForeign key to ContentType and a Foreign key to auth.Users. I dumped the data only for my app using the --natural option but this lead to the problem where users didn't exist in the test database. Now I removed the --natural option and instead dumped data for all 3 apps myApp, auth, contenttypes. When I'm running the test I get "Could not load contenttypes.ContentType(pk=50): columns app_label, model are not unique". I think this is due to the contenttypes being created dynamically when models are imported.

The command used:

python manage.py dumpdata auth myApp contenttypes --natural --indent=4 > auhtmtmnatural.json

Next I dumped myApp, auth leaving off contenttypes. I figured that since ContentTypes app db is created dynamically and my fixtures have natural keys, this should work. It doesn't.

Now I'm getting:

DeserializationError: Problem installing fixture 'auhtmtmnatural.json': ContentType matching query does not exist.

How do I get fixutres with contetypes to work? Is it better to use setup and create objects in testing?

Birdlime answered 21/3, 2014 at 15:33 Comment(0)
H
-3

There are several reasons why you shouldn't use fixtures:

  • It's slow
  • It's hard to update/maintain;
  • Is not practice;

Instead of fixtures you should use a "mock" app like factory boy:

http://factoryboy.readthedocs.org/en/latest/

https://pypi.python.org/pypi/factory_boy/

Honoria answered 11/7, 2014 at 12:7 Comment(0)
D
7

Use content_type: [<app_label>, <model>], instead of content_type: <hard-coded pk of model>

Delamination answered 24/4, 2017 at 13:13 Comment(0)
A
2

I realise it's more than a year since this question was asked, however you (or someone else) might like to know that I was able to use fixtures in testing a model with generic foreign key to ContentType. There's a caveat to this though, which is that I had to hard-code the content_type_id in the test fixtures, and that is probably a bit unstable in terms of order in which ContentTypes are created in the database. (I'm beginning to re-write my tests with factory_boy for the reasons @Lara mentioned)

I am currently using Django 1.8.3, Python 2.7.9.

The key feature seems to be the order of loading fixtures. In my tests.py I have:

tests.py:

class MyModelViewTests(TestCase):
    fixtures = [
        'auth_user.json',
        'gfk_model.json',
        'my_model.json',
    ]

    def test_something(self):
        # your tests

Above, the gfk_model.json contains fixtures for the model containing the generic foreign key to ContentType. The my_model.json contains the fixtures for whatever model your Django project or app needs. So I instantiate the model instances containing generic foreign keys first. It's true that their object_id entries reference database rows for my_model which don't quite exist yet, but will exist as soon as the my_model.json fixtures are loaded.

MyModel/models.py

class MyModel(models.Model):
    book_title = models.TextField()
    book_isbn = models.CharField(max_length=13)

GFKModel/models.py

class GFKModel(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()
    your_field = models.TextField()

If I'm associating the generic foreign key with one particular ContentType, then I can get the id of the content type from the SQL database or content_type_id = ContentType.objects.get_for_model(MyModel).pk, executed in a django shell (python manage.py shell). I then hardcoded (yukky I know!) the id thus obtained (13 in my case) in my fixtures.

So, finally the fixtures (obtained from dumpdata, then edited) look something like this

gfk_model.json

[
{
    "fields": {
        "your_field": "Stuff you need to associate with my_model",
        "object_id": 1,
        "content_type": 13,
    },
    "model": "myapp.gfk_model",
    "pk": 1
},
{
    "fields": {
        "your_field": "Stuff you need to associate with my_model",
        "object_id": 2,
        "content_type": 13,
    },
    "model": "gfk_app.gfk_model",
    "pk": 2
}
]

my_model.json

[
{
  "pk": 1, 
  "model": "myapp.my_model", 
  "fields": {
    "book_name": "How to bath your cat", 
    "book_isbn": "123456", 
  }
},
{
  "pk": 2, 
  "model": "myapp.my_model", 
  "fields": {
    "book_name": "How About Wednesday?", 
    "book_isbn": "654321", 
  }
}
]

In the gfk_model.json the object_id of 1 and 2 correspond to the PKs 1, 2 of the my_model.json respectively. I've provided PKs for the gkf_model instances too (although possibly I don't need to, haven't tried removing them).

All in all, it was actually much quicker to get it working than to write this post!

It's also pretty straightforward to get GFK tests working just by using the ORM in the test setUp(). Also fine with factory_boy too.

If you've got several different ContentTypes, and especially if they are likely to change their pk in the contenttypes table, I don't think fixtures are going to be very successful.

Augmenter answered 26/8, 2015 at 22:54 Comment(0)
H
-3

There are several reasons why you shouldn't use fixtures:

  • It's slow
  • It's hard to update/maintain;
  • Is not practice;

Instead of fixtures you should use a "mock" app like factory boy:

http://factoryboy.readthedocs.org/en/latest/

https://pypi.python.org/pypi/factory_boy/

Honoria answered 11/7, 2014 at 12:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.