#28385: deserializers ignore natural keys when primary key has a default value
--------------------------------------+------------------------------------
     Reporter:  Daniel Knell          |                    Owner:  nobody
         Type:  Bug                   |                   Status:  new
    Component:  Core (Serialization)  |                  Version:  1.11
     Severity:  Normal                |               Resolution:
     Keywords:                        |             Triage Stage:  Accepted
    Has patch:  0                     |      Needs documentation:  0
  Needs tests:  0                     |  Patch needs improvement:  0
Easy pickings:  1                     |                    UI/UX:  0
--------------------------------------+------------------------------------
Changes (by Tim Graham):

 * stage:  Unreviewed => Accepted


Old description:

> https://github.com/django/django/blob/master/django/core/serializers/base.py#L223
>

> {{{
> 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
> }}}
>

> 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
> }}}

New description:

 {{{
 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
 }}}
 
([https://github.com/django/django/blob/169c3b3e07829d9ffa409b6eb5c1094d8ef918a8/django/core/serializers/base.py#L223
 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
 }}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/28385#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/069.76e164a779a0e6658db665458f51ffc5%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to