Oli*_*Oli 29 django url primary-key django-models
我正在开始研究新的webapp.部分内容将为用户提供可以在一对多关系中自定义的页面.这些页面自然需要具有唯一的URL.
Django留给自己的设备,通常AUTOINCREMENT会为模型分配标准ID.虽然这很有效,但它看起来并不好看,它也使得页面非常容易预测(在这种情况下,这是不可取的).
而不是1,2,3,4我想要设置长度,随机生成的字母数字字符串(例如h2esj4).可能的36个字符中的6个点应该给我超过20亿个组合,在这个阶段应该绰绰有余.当然,如果我可以在以后扩展它,那也会很好.
但有两个问题:
随机字符串偶尔会拼出坏词或其他令人反感的短语.有没有一个体面的方式回避这个?公平地说,我可能会接受一个数字字符串,但它确实会对冲突的可能性产生重大影响.
如何让Django(或数据库)在插入时进行繁重的工作?我宁愿不插入然后解决密钥(因为这不是一个关键).我假设有并发问题也要注意,但如果同时生成两个新页面而第二个(克服所有可能性)神奇地获得与第一个提交之前的第一个相同的密钥.
我不认为这与URL缩短器生成ID的方式有一百万英里的差异.如果有一个体面的Django实现,我可以捎带它.
ato*_*zer 22
有内置的Django方式来实现你想要的.将字段添加到"自定义页面"的模型中,primary_key=True并default=使用密钥生成函数的名称,如下所示:
class CustomPage(models.Model):
...
mykey = models.CharField(max_length=6, primary_key=True, default=pkgen)
...
Run Code Online (Sandbox Code Playgroud)
现在,对于每个模型实例page,它将page.pk成为一个别名page.mykey,该别名将pkgen()在创建该实例时自动分配函数返回的字符串.
快速而肮脏的实施:
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
Run Code Online (Sandbox Code Playgroud)
两个页面获得相同主键的概率非常低(假设random()足够随机),并且没有并发问题.而且,通过从编码字符串中切割更多字符,这种方法可以轻松扩展.
这就是我最终做的事情.我做了一个抽象的模型.我的用例是需要几个模型来生成自己的随机slu ..
一个slug看起来像AA##AA这样的52x52x10x10x52x52 = 731,161,600组合.可能比我需要的数千倍,如果这是一个问题,我可以添加52倍以上的组合.
使用default参数不会削减它,因为抽象模型需要检查孩子上的slug碰撞.继承是最简单的,可能只是这样做的方式.
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
Run Code Online (Sandbox Code Playgroud)
Django 现在包含一个UUIDField 类型,因此您不需要任何自定义代码或 Srikanth Chundi 建议的外部包。这个实现使用带破折号的十六进制字符串,所以除了像 abad1d3a 这样的 1337 表达式之外,文本非常适合儿童使用 :)
您可以像这样使用它作为主键别名pk到该uuid字段:
import uuid
from django.db import models
class MyModel(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# other fields
Run Code Online (Sandbox Code Playgroud)
但是请注意,当您在urls.py 中路由到此视图时,您需要此处提到的不同正则表达式,例如:
urlpatterns = [
url(r'mymodel/(?P<pk>[^/]+)/$', MyModelDetailView.as_view(),
name='mymodel'),
]
Run Code Online (Sandbox Code Playgroud)