Generating a non-sequential ID/PK for a Django Model
There is built-in Django way to achieve what you want. Add a field to the model of "custom page" with primary_key=True
and default=
name of key generation function, like this:
class CustomPage(models.Model): ... mykey = models.CharField(max_length=6, primary_key=True, default=pkgen) ...
Now, for every model instance page
, page.pk
becomes an alias for page.mykey
, which is being auto-assigned with the string returned by your function pkgen()
at the moment of creation of that instance.
Fast&dirty implementation:
def pkgen(): from base64 import b32encode from hashlib import sha1 from random import random rude = ('lol',) bad_pk = True while bad_pk: pk = b32encode(sha1(str(random())).digest()).lower()[:6] bad_pk = False for rw in rude: if pk.find(rw) >= 0: bad_pk = True return pk
The probability of two pages getting identical primary keys is very low (assuming random()
is random enough), and there are no concurrency issues. And, of couse, this method is easilly extensible by slicing more chars from encoded string.
Here's what I ended up doing. I made an abstract model. My use-case for this is needing several models that generate their own, random slugs.
A slug looks like AA##AA
so that's 52x52x10x10x52x52 = 731,161,600
combinations. Probably a thousand times more than I'll need and if that's ever an issue, I can add a letter for 52 times more combinations.
Use of the default
argument wouldn't cut it as the abstract model needs to check for slug collisions on the child. Inheritance was the easiest, possibly only way of doing that.
from django.db import modelsfrom django.contrib.auth.models import Userimport string, randomclass SluggedModel(models.Model): slug = models.SlugField(primary_key=True, unique=True, editable=False, blank=True) def save(self, *args, **kwargs): while not self.slug: newslug = ''.join([ random.sample(string.letters, 2), random.sample(string.digits, 2), random.sample(string.letters, 2), ]) if not self.objects.filter(pk=newslug).exists(): self.slug = newslug super().save(*args, **kwargs) class Meta: abstract = True
Django now includes an UUIDField type, so you don't need any custom code or the external package Srikanth Chundi suggested. This implementation uses HEX strings with dashes, so the text is pretty child-safe, other than 1337 expressions like abad1d3a :)
You would use it like this to alias pk
to the uuid
field as a primary key:
import uuidfrom django.db import modelsclass MyModel(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # other fields
Note, however, that when you're routing to this view in urls.py, you need a different regex as mentioned here, e.g.:
urlpatterns = [ url(r'mymodel/(?P<pk>[^/]+)/$', MyModelDetailView.as_view(), name='mymodel'),]