10👍
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 models
from django.contrib.auth.models import User
import string, random
class 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
24👍
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.
- [Django]-Get Primary Key after Saving a ModelForm in Django
- [Django]-Unable to perform collectstatic
- [Django]-How can I handle Exceptions raised by dango-social-auth?
9👍
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 uuid
from django.db import models
class 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'),
]
- [Django]-How to access array elements in a Django template?
- [Django]-Adding a user to a group in django
- [Django]-How to server HTTP/2 Protocol with django
4👍
May be you need to look at Python UUID, it can generate random lengthy characters. But you can slice it and use the number of characters you want with little check to make sure it’s unique even after slicing.
UUIDField snippet may help you if you don’t want to take pain of generating UUID yourself.
Also have a look at this blog post
- [Django]-Django Setup Default Logging
- [Django]-Django static page?
- [Django]-Running Django tests in PyCharm
3👍
Oli: If you’re worried about spelling out rude words, you can always compare/search your UUIDField for them, using the django profanity filter, and skip any UUIDs that might be triggery.
- [Django]-Forbidden (403) CSRF verification failed. Request aborted. Reason given for failure: Origin checking failed does not match any trusted origins
- [Django]-Django internationalization language codes
- [Django]-Serving large files ( with high loads ) in Django
1👍
This is what I ended up using UUID.
import uuid
from django.db import models
from django.contrib.auth.models import User
class SluggedModel(models.Model):
slug = models.SlugField(primary_key=True, unique=True, editable=False, blank=True)
def save(self, *args, **kwargs):
if not self.slug:
uuid.uuid4().hex[:16] # can vary up to 32 chars in length
super(SluggedModel, self).save(*args, **kwargs)
class Meta:
abstract = True
- [Django]-Is "transaction.atomic" same as "transaction.commit_on_success"?
- [Django]-Django startswith on fields
- [Django]-Access Django models with scrapy: defining path to Django project
1👍
Looking at the above answers, here is what I am using now.
import uuid
from django.db import models
from django.utils.http import int_to_base36
ID_LENGTH = 9
def id_gen() -> str:
"""Generates random string whose length is `ID_LENGTH`"""
return int_to_base36(uuid.uuid4().int)[:ID_LENGTH]
class BaseModel(models.Model):
"""Django abstract model whose primary key is a random string"""
id = models.CharField(max_length=ID_LENGTH, primary_key=True, default=id_gen, editable=False)
class Meta:
abstract = True
class CustomPage(BaseModel):
...
- [Django]-Substring in a django template?
- [Django]-Permission denied – nginx and uwsgi socket
- [Django]-Timestamp fields in django