richardcornish
richardcornish

Reputation: 154

How to load a data fixture with get_by_natural_key() on ContentType foreign key in Django?

I'm having trouble loading an external data fixture for a model that uses a ContentType foreign key.

I'm using a manager in the models, like the docs say. Unfortunately, although the docs talk about the importance of a get_by_natural_key method on a ContentType foreign key, it then launches into a different example instead. I'm having trouble figuring out just what the manager would look like. My best guess is to use get_by_natural_key again, and assign app_label and model lookups, but I could be way off.

# models.py 
from django.db import models 
from django.contrib.contenttypes.models import ContentType 
from django.utils.translation import ugettext_lazy as _ 

class InlineTypeManager(models.Manager): 
    def get_by_natural_key(self, title, app_label, model): 
        return self.get(title=title, content_type=ContentType.objects.get_by_natural_key(app_label=content_type__name, model=content_type__id)) 

class InlineType(models.Model): 
    title = models.CharField(_("title"), max_length=255) 
    content_type = models.ForeignKey(ContentType, limit_choices_to={"model__in": ("Link", "Document", "Image", "Audio", "Video", "MediaSet", "Flash", "Testimonial")}) 

    objects = InlineTypeManager() 

    class Meta: 
        ordering  = ["title"] 
        verbose_name = _("inline type") 
        verbose_name_plural = _("inline types") 

    def __unicode__(self): 
        return u"%s" % (self.title) 

https://docs.djangoproject.com/en/dev/topics/serialization/#natural-keys

My initial_data.json:

[
    {
        "model": "inlines.inlinetype",
        "pk": 1,
        "fields": {
            "title": "Image",
            "content_type": "image"
        }
    }, {
        "model": "inlines.inlinetype",
        "pk": 2,
        "fields": {
            "title": "Video",
            "content_type": "video"
        }
    }
]

When I loaddata my JSON, I receive the error:

DeserializationError: [u"'image' value must be an integer."] 

The point of get_by_natural_key is to load non-integer fields in a "human-friendly" lookup because hard-coded IDs in the JSON is a bad idea because of its unpredictability, so I'm guessing my manager is failing. Or should I use get_for_model()/get_for_models()?

Upvotes: 1

Views: 2950

Answers (1)

okm
okm

Reputation: 23871

natural key in Django is

The default serialization strategy for foreign keys and many-to-many relations is to serialize the value of the primary key(s) of the objects in the relation.

You don't need to implement methods such as natural_key and get_by_natural_key in Manager for those models which do not occur as ForeignKey/ManyToManyField in targets to dump. So you could remove InlineTypeManager() lines.

Also, the values of content_type field inside dumped initial_data.json are incorrect. Django only treats a list as natural key, a string like "image" is still treated as surrogate key and will fail because it cannot be coerced to int successfully. Correct ContentType dump looks like

from django.contrib.contenttypes.models import ContentType
from django.utils import simplejson

>>> simplejson.dumps(ContentType.objects.get(model='user').natural_key())
'["auth", "user"]'

Upvotes: 1

Related Questions