Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为Django模型生成非顺序ID/PK_Django_Url_Django Models_Primary Key - Fatal编程技术网

为Django模型生成非顺序ID/PK

为Django模型生成非顺序ID/PK,django,url,django-models,primary-key,Django,Url,Django Models,Primary Key,我即将开始开发一个新的网络应用程序。其中一部分将为用户提供他们可以在一对多关系中定制的页面。这些页面自然需要有唯一的URL 离开自己的设备,Django通常会为模型分配一个标准的AUTOINCREMENTID。虽然这工作得非常好,但它看起来并不好,而且它也使页面非常可预测(在本例中这是不需要的) 而不是1,2,3,4,我想设置长度,随机生成的字母数字字符串(如h2esj4)。一组36个字符中的6个点应该给我20多亿个组合,这在现阶段已经足够了。当然,如果我能在以后扩展这个,那也会很好 但有两个问

我即将开始开发一个新的网络应用程序。其中一部分将为用户提供他们可以在一对多关系中定制的页面。这些页面自然需要有唯一的URL

离开自己的设备,Django通常会为模型分配一个标准的
AUTOINCREMENT
ID。虽然这工作得非常好,但它看起来并不好,而且它也使页面非常可预测(在本例中这是不需要的)

而不是1,2,3,4,我想设置长度,随机生成的字母数字字符串(如h2esj4)。一组36个字符中的6个点应该给我20多亿个组合,这在现阶段已经足够了。当然,如果我能在以后扩展这个,那也会很好

但有两个问题:

  • 随机字符串偶尔会拼写出不好的单词或其他冒犯性短语。有没有一个体面的方法来回避这一点?公平地说,我可能会接受一个数字字符串,但它确实对冲突的可能性有很大影响

  • 如何让Django(或数据库)在insert上完成繁重的工作?我不希望先插入,然后再算出钥匙(因为那不是一个多大的钥匙)。我认为,如果同时生成两个新页面,并且第二个页面(尽管不太可能)在提交第一个页面之前神奇地获得了与第一个页面相同的密钥,那么也需要注意并发性问题


  • 我认为这与URL缩短器生成ID的方式没有什么不同。如果有一个像样的Django实现,我可以利用它。

    也许你需要看看,它可以生成随机的长字符。但是,您可以对其进行切片,并使用所需的字符数,而无需进行任何检查,以确保即使在切片之后它也是唯一的

    如果您不想自己痛苦地生成UUID,这个代码片段可能会对您有所帮助


    还可以看看这个

    有一个内置的Django方法来实现您想要的。在“自定义页面”模型中添加一个字段,其中包含键生成函数的
    primary\u key=True
    default=
    名称,如下所示:

    class CustomPage(models.Model):
        ...
        mykey = models.CharField(max_length=6, primary_key=True, default=pkgen)
        ...
    
    现在,对于每个模型实例
    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
    

    两个页面获得相同主键的概率非常低(假设
    random()
    足够随机),并且不存在并发问题。当然,通过从编码字符串中分割更多的字符,这个方法很容易扩展。

    下面是我最后要做的。我做了一个抽象模型。我的用例是需要几个模型来生成它们自己的随机段塞

    一个slug看起来像
    AA##AA
    ,这就是
    52x52x10x10x10x52x52=731161600
    组合。可能比我需要的多1000倍,如果这是个问题,我可以添加一封信,增加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
    

    奥利:如果你担心拼写粗俗的单词,你可以使用django亵渎过滤器比较/搜索你的UUID字段,并跳过任何可能触发的UUID。

    这就是我最终使用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现在包含一个,因此您不需要任何自定义代码或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
    
    但是,请注意,在URL.py中路由到此视图时,需要使用不同的正则表达式,例如:

    urlpatterns=[
    url(r'mymodel/(?P[^/]+)/$”,MyModelDetailView.as_view(),
    name='mymodel'),
    ]
    
    看看上面的答案,下面是我现在使用的

    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):
        ...
    

    这并没有真正绕过我在问题中强调的两个问题中的任何一个。当然,
    UUIDField
    有助于将一些代码从我的模型中抽象出来,但它仍然在数据库之外(我非常喜欢它),并且仍然能够很好地拼写出粗鲁的单词。我不理解b32encode和sha1在这个概念中的意义。一个简单的随机字符列表不会产生同样的随机结果,并且开销(和代码)会少很多吗?@Oli您可以生成任何您想要的字符串,关键是将回调函数设置为默认值是将字符串指定为PK的方式。对我来说似乎是正确的解决方案+1 UpvoteIn在可重用的设置中,它不能进行冲突检查。同一段代码的模型实例不能超过一次。这是
    default
    参数中的一个缺陷,该参数无法获取附加信息(将类传递给生成器)。
    random\u key=lambda:{k:032X}.format(k=random.getrandbits(128))
    id=django.utils.http.int\u to_base36(uuid.uuid4().int)[:length]
    。我最近决定采取一些UK ID的方法来处理一些PK,但是我也会考虑这个问题。我认为你的片段在任何情况下都是一样的。只需将您生成的4行“ret”替换为类似“ret=uuid.uuid1()”的内容,我正在尝试使用您的方法,但我发现无法通过ClassName实例访问管理器错误。你是如何克服这一点的?这是一个古老的线索,但对于任何偶然发现这一点并使用MySQL的人来说,有一件事需要警惕,那就是MySQL在默认情况下对字符串匹配不区分大小写,因此“AB12AB”和“AB12AB”的ID都会被找到,除非你明确告诉MySQL使用区分大小写的匹配:@Oli:
    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):
        ...