#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.