Primary keys vs. natural keys

60 views
Skip to first unread message

Torsten Bronger

unread,
Oct 31, 2014, 5:14:29 AM10/31/14
to django...@googlegroups.com
Hallöchen!

Do I understand it correctly that in the Django community, it is
preferred to use surrogate primary keys (the auto ID field) instead
of explicitly setting primary keys? Currently, I add natural_key()
methods to many of my models, but some of them return only one
field. Now I wonder whether I made a mistake and should have chosen
that field as the primary key.

My use case is that I need to retrieve the natural keys of all
objects in the database of a particular model. As far as I can see,
this can only be achieved by fetching all objects and calling the
natural_key() method for all of them. In contrast,

model.objects.values_list("pk", flat=True)

is probably *much* faster.

Is it possibly to specify a single field a natural key somehow
(besides making it the PK)? For example, it is possible to add a
Meta attribute to the model?

Tschö,
Torsten.

--
Torsten Bronger Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Carl Meyer

unread,
Oct 31, 2014, 5:55:21 AM10/31/14
to django...@googlegroups.com
Hi Torsten,

> On Oct 31, 2014, at 3:13 AM, Torsten Bronger <bro...@physik.rwth-aachen.de> wrote:
> Do I understand it correctly that in the Django community, it is
> preferred to use surrogate primary keys (the auto ID field) instead
> of explicitly setting primary keys?

Yes, I'd say this is true.

> Currently, I add natural_key()
> methods to many of my models, but some of them return only one
> field. Now I wonder whether I made a mistake and should have chosen
> that field as the primary key.
>
> My use case is that I need to retrieve the natural keys of all
> objects in the database of a particular model. As far as I can see,
> this can only be achieved by fetching all objects and calling the
> natural_key() method for all of them. In contrast,
>
> model.objects.values_list("pk", flat=True)
>
> is probably *much* faster.
>
> Is it possibly to specify a single field a natural key somehow
> (besides making it the PK)? For example, it is possible to add a
> Meta attribute to the model?

There is no built in feature for this, but it doesn't seem like a hard problem to solve with your own conventions. For instance, rather than hardcoding the name of the natural key field inside the natural_key method, make it a model class attribute, e.g. MyModel.natural_key_field. Then the natural_key method can return getattr(self, self.natural_key_field) (you could even implement this method just once in an abstract base or mixin), and you can also use the attribute to do your efficient query: MyModel.objects.values_list(MyModel.natural_key_field, flat=True)

There may be reasons to consider natural primary keys in your data model (though personally I think surrogates are usually a better choice), but I don't think the problem you presented implies that you made the wrong choice.

Carl

Torsten Bronger

unread,
Oct 31, 2014, 6:20:31 AM10/31/14
to django...@googlegroups.com
Hallöchen!

Carl Meyer writes:

> [...]
>
> There is no built in feature for this, but it doesn't seem like a
> hard problem to solve with your own conventions. For instance,
> rather than hardcoding the name of the natural key field inside
> the natural_key method, make it a model class attribute,
> e.g. MyModel.natural_key_field.

Do you mean this:

class ExternalOperator(models.Model):

name = models.CharField(_("name"), max_length=30, unique=True)
natural_key_field = "name"

It works (at least, it doesn't abort) but I thought only fields were
allowed as attributes.

Carl Meyer

unread,
Oct 31, 2014, 12:52:59 PM10/31/14
to django...@googlegroups.com

> On Oct 31, 2014, at 4:19 AM, Torsten Bronger <bro...@physik.rwth-aachen.de> wrote:
> Carl Meyer writes:
>> [...]
>>
>> There is no built in feature for this, but it doesn't seem like a
>> hard problem to solve with your own conventions. For instance,
>> rather than hardcoding the name of the natural key field inside
>> the natural_key method, make it a model class attribute,
>> e.g. MyModel.natural_key_field.
>
> Do you mean this:
>
> class ExternalOperator(models.Model):
>
> name = models.CharField(_("name"), max_length=30, unique=True)
> natural_key_field = "name"
>
> It works (at least, it doesn't abort) but I thought only fields were
> allowed as attributes.

Yes, that's what I mean (though usually for clarity I would place any non-field attributes in a separate visual block - separated by a blank line - from field attributes). There is no requirement that all class attributes of models must be fields. Django can tell which are subclasses of Field and ignores the others.

Carl

Torsten Bronger

unread,
Oct 31, 2014, 5:32:28 PM10/31/14
to django...@googlegroups.com
Hallöchen!

Carl Meyer writes:

>> On Oct 31, 2014, at 4:19 AM, Torsten Bronger <bro...@physik.rwth-aachen.de> wrote:
>>
>> [...]
>>
>> Do you mean this:
>>
>> class ExternalOperator(models.Model):
>>
>> name = models.CharField(_("name"), max_length=30, unique=True)
>> natural_key_field = "name"
>>
>> It works (at least, it doesn't abort) but I thought only fields
>> were allowed as attributes.
>
> Yes, that's what I mean (though usually for clarity I would place
> any non-field attributes in a separate visual block - separated by
> a blank line - from field attributes). There is no requirement
> that all class attributes of models must be fields. Django can
> tell which are subclasses of Field and ignores the others.

See https://code.djangoproject.com/ticket/5793 -- can't this be
solved by such attributes then? I wonder because I subscribed to
this ticket long ago for a similar reason.

Carl Meyer

unread,
Oct 31, 2014, 11:34:16 PM10/31/14
to django...@googlegroups.com

> On Oct 31, 2014, at 3:26 PM, Torsten Bronger <bro...@physik.rwth-aachen.de> wrote:
>
> Hallöchen!
>
> Carl Meyer writes:
>
>>> On Oct 31, 2014, at 4:19 AM, Torsten Bronger <bro...@physik.rwth-aachen.de> wrote:
>>>
>>> [...]
>>>
>>> Do you mean this:
>>>
>>> class ExternalOperator(models.Model):
>>>
>>> name = models.CharField(_("name"), max_length=30, unique=True)
>>> natural_key_field = "name"
>>>
>>> It works (at least, it doesn't abort) but I thought only fields
>>> were allowed as attributes.
>>
>> Yes, that's what I mean (though usually for clarity I would place
>> any non-field attributes in a separate visual block - separated by
>> a blank line - from field attributes). There is no requirement
>> that all class attributes of models must be fields. Django can
>> tell which are subclasses of Field and ignores the others.
>
> See https://code.djangoproject.com/ticket/5793 -- can't this be
> solved by such attributes then? I wonder because I subscribed to
> this ticket long ago for a similar reason.

True - there's nothing you could do with custom Meta attributes that can't just as well be done with class attributes. The desire for custom Meta attributes is purely a matter of API aesthetics.

Carl
Reply all
Reply to author
Forward
0 new messages