简体   繁体   English

如何在 Django 中创建随机 slug

[英]How to create random slug in Django

I have a Django app that will create a contract between two people.我有一个 Django 应用程序,它将在两个人之间创建合同。 The user creates the contract and, upon creation, a random url-safe string is generated using secrets . user创建合约,并在创建后使用secrets生成一个随机的 url 安全字符串。

However, when I create more than one instance of a Contract , the slugs are the same.但是,当我创建多个Contract实例时,slug 是相同的。 The only way to get a different randomly generated string is to restart my server.获得不同的随机生成字符串的唯一方法是重新启动我的服务器。 What am I doing wrong?我究竟做错了什么?

Here's the part that matters in my models.py这是我的models.py中重要的部分

    slug = models.SlugField(
        default=secrets.token_urlsafe(64),
        editable=False,
        blank=False
    )

Here are the last three Contracts I created.这是我创建的最后三个Contracts Changing the values of the other fields doesn't change the slug.更改其他字段的值不会更改 slug。

    {
        "model_email": "POOP@pooop.com",
        "shoot_location": "Atlanta, GA",
        "shoot_date": "2020-05-24",
        "model_name": "",
        "created_at": "2020-05-24T19:24:20.220845Z",
        "sent_completed_to_model": null,
        "sent_completed_to_photographer": null,
        "photographer": 1,
        "signed": false,
        "slug": "Dqyk5-kDzE219_-abRKABETRuOCGOBxzvFVrXZaUGEe8ZLva6i6tdwUYMksSvKJ_BEABV_Vt1H_ttZ6p-yzRyg"
    },
    {
        "model_email": "POOP@pooop.com",
        "shoot_location": "Atlanta, GA",
        "shoot_date": "2020-05-24",
        "model_name": "",
        "created_at": "2020-05-24T19:24:21.216218Z",
        "sent_completed_to_model": null,
        "sent_completed_to_photographer": null,
        "photographer": 1,
        "signed": false,
        "slug": "Dqyk5-kDzE219_-abRKABETRuOCGOBxzvFVrXZaUGEe8ZLva6i6tdwUYMksSvKJ_BEABV_Vt1H_ttZ6p-yzRyg"
    },
    {
        "model_email": "POOP@pooop.com",
        "shoot_location": "Atlanta, GA",
        "shoot_date": "2020-05-24",
        "model_name": "",
        "created_at": "2020-05-24T19:24:22.120845Z",
        "sent_completed_to_model": null,
        "sent_completed_to_photographer": null,
        "photographer": 1,
        "signed": false,
        "slug": "Dqyk5-kDzE219_-abRKABETRuOCGOBxzvFVrXZaUGEe8ZLva6i6tdwUYMksSvKJ_BEABV_Vt1H_ttZ6p-yzRyg"
    }

This happen because your default value is not function that will be called on each save, it's rather a constant value generated when your app start发生这种情况是因为您的默认值不是 function 将在每次保存时调用,而是在您的应用启动时生成的常量值

An other option would be moving the logic to save method of your model:另一种选择是移动逻辑以save model 的方法:

    slug = models.SlugField(
        default='',
        editable=False,
        blank=False
    )

    ...

    def save(self, *args, **kwargs):
        if not self.default:
            self.default = secrets.token_urlsafe(64)

        super().save(*args, **kwargs)

Change your code to:将您的代码更改为:

def get_token_slug():
    return secrets.token_urlsafe(64)

... ...

slug = models.SlugField(
        default=get_token_slug,
        editable=False,
        blank=False,
        unique=True,             # also add this
    )

The problem was that the function call that you give as a default value is computed once and the result is used as a default value in all new instances of this model.问题在于,您作为默认值提供的 function 调用只计算一次,结果在此 model 的所有新实例中用作默认值。

Instead, you should give a callable as a default value, which will then be called when a new instance of the model is created.相反,您应该将可调用对象作为默认值,然后在创建 model 的新实例时调用它。 So just wrap the token_urlsafe call in a function, and give it as an argument (the function, not a call, so without parentheses () )所以只需将token_urlsafe调用包装在 function 中,并将其作为参数提供(function,不是调用,所以没有括号()

In terms of creating a unique slug field when saving an instance of your model, you will need to alter your save method so that it can create a slug which has the next available number appended to the end of your slug.在保存 model 的实例时创建一个唯一的 slug 字段,您需要更改保存方法,以便它可以创建一个 slug,在您的 slug 末尾附加下一个可用编号。 It might look something like the below...它可能看起来像下面这样......

Replace MyModel and field_you_are_using_to_slugify as applicable.根据需要替换MyModelfield_you_are_using_to_slugify

def save(self):
    if not self.slug.strip():
        self.slug = slugify(self.field_you_are_using_to_slugify)

    _slug = self.slug
    _count = 1

    while True:
        try:
            MyModel.objects.all().exclude(pk=self.pk).get(slug=_slug)
        except MultipleObjectsReturned:
            pass
        except ObjectDoesNotExist:
            break
        _slug = f"{self.slug}{_count}"
        _count += 1

    self.slug = _slug

    super(MyModel, self).save()

It should be noted however that if you're after a URL which cannot be interpreted and uses random string generation, then Django provides a UUID field for that purpose.但是应该注意的是,如果您使用的是无法解释的 URL 并使用随机字符串生成,那么 Django 会为此目的提供一个 UUID 字段。 Here is a link to the docs这是文档的链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM