Natural key serialization of OneToOneField primary keys

Hi folks,

I’ve hit a small snag with the django.core.serializers.base.Serializer class in that it’s not serializing a OneToOneField(primary_key=True) field that’s pointed at a model that does have a natural key.

My question to the community is: should a OneToOneField(primary_key=True, parent_link=False) be serialized if related model has a natural_key function defined?

We use this setup to add product specific information to core models (maybe I should be doing something else?). For example:

class Course(models.Model):
    slug = models.SlugField()

    def natural_key(self):
        return (self.slug, )

class DjangoCourseConfig(models.Model):
    course = models.OneToOneField(Course, primary_key=True, ...)
    special_field = models.CharField()

    def natural_key(self):
        return self.course.natural_key()

If I serialize this with natural primary keys and natural foreign keys I get:

{"model": "app.DjangoCourseConfig",
 "fields": {"special_field": "hello world"}}

While I believe it should be treated like another relationship instead returning:

{"model": "app.DjangoCourseConfig",
 "fields": {"course": ["slug1"], "special_field": "hello world"}}

There is some special handler logic for primary keys to handle model inheritance to determine if a non-serializable field (pk) should actually be serialized. Theoretically this can be extended to check this additional case. But I’m probably short-sighted of other implications.

To answer the question of, why not use model inheritance? Course and DjangoCourseConfig have fields that have the same name, but different meaning.

My current workaround is to define my own Serializer class to override the behavior of serialize which is less than ideal considering the function’s size.

Obviously, something needs to be serialized in order to establish the connection between the two models.

Note that dumpdata also does the wrong thing here.

Also of an interesting note, if I make slug the primary key, then it also ends up being ignored and nothing is dumped for those rows!

However, this isn’t something new - there’s quite a bit of history around this topic.


1 Like

Additionally, if I add another field to natural_key, e.g.:

class Course(models.Model):
    slug = models.CharField(max_length=80, primary_key=True)
    value = models.IntegerField(default=1)

    objects = CourseManager()

    def natural_key(self):
        return (self.slug, self.value)

Then value is serialized in fields, but slug isn’t.

Note that both of these situations are independent of the OneToOneField issue. It appears that identifying the primary_key within the natural_key function breaks the serialization. (My interpretation of events.)