Opened 7 years ago

Last modified 5 years ago

#28385 closed Bug

deserializers ignore natural keys when primary key has a default value — at Version 1

Reported by: Daniel Knell Owned by: nobody
Component: Core (Serialization) Version: 1.11
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description (last modified by Tim Graham)

def build_instance(Model, data, db):
    """
    Build a model instance.
    If the model instance doesn't have a primary key and the model supports
    natural keys, try to retrieve it from the database.
    """
    obj = Model(**data)
    if (obj.pk is None and hasattr(Model, 'natural_key') and
            hasattr(Model._default_manager, 'get_by_natural_key')):
        natural_key = obj.natural_key()
        try:
            obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
        except Model.DoesNotExist:
            pass
    return obj

(source on GitHub)

this causes loaddata to fail on second attempt when using natural keys and uuid primary keys as pk is set.

class FooManager(models.Manager):
    def get_by_natural_key(self, name):
        return self.get(name=name)

class Foo(DateTimeMixin, models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100, unique=True)

    objects = FooManager()

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

checking the primary key was not actually set seems to fix things:

def build_instance(Model, data, db):
    obj = Model(**data)
    pk = data.get(Model._meta.pk.name, None)
    
    if (pk is None and hasattr(Model, 'natural_key') and
            hasattr(Model._default_manager, 'get_by_natural_key')):
        natural_key = obj.natural_key()
        try:
            obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
        except Model.DoesNotExist:
            pass
    return obj

import django.core.serializers.base

django.core.serializers.base.build_instance = build_instance

Change History (1)

comment:1 by Tim Graham, 7 years ago

Description: modified (diff)
Summary: deserialisers ignore natural keys when primary key has a default valuedeserializers ignore natural keys when primary key has a default value
Triage Stage: UnreviewedAccepted
Note: See TracTickets for help on using tickets.
Back to Top