Django 用 Signals 的 pre_save 构建 models 的 SlugField、

Django 信号 pre_save,预处理 models

如果一个 models 在保存到数据库之前,我们需要对存入数据进行预处理,这时候我们就需要 pre_save。

pre_save 和 post_save 类似于 React 的生命周期,是针对 Django models 在保存(save())前后的钩子(hook),这两个方法可以让我们在 models 的保存前后,对数据或者其他内容进行一些处理。而这种钩子函数,在 Django 中被称之为 signal(信号)。

SlugField 构建语义化 url

SlugField 本质上相当于存放字符串,但是在意义上,主要用于把某些字段形成语义化的,可以访问的短网址(slug)字符串。

举例而言,假设有一个关于文章的 models,有两个字段,分别是标题 title 和短网址 slug。

class Article(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(max_length=40)

这时候存入 models 的一条信息,title 是 The song of ice and fire。如果我们想用文章的标题作为 url 进行访问,完整的标题可能是:

www.xxx.com/article/The song of ice and fire

但是 url 是不能出现空格的,因此空格会被转变成 %20,最后网址得到

www.xxx.com/article/The%20song%20of%20ice%20and%20fire

我们不希望出现这么多难看的 %20,而是希望用短横线 - 替代空格,得到

www.xxx.com/article/the-song-of-ice-and-fire

而 Django 的 django.utils.text提供了一个方法叫 slugify,可以把刚才的文章标题 The song of ice and fire 做两个转变:

  • 全部转化成小写字母
  • 空格部分替换成短横线 -

因为这种转变主要用于做 url 拼接,所以我们把这种结果都统一放在 SlugField 字段里面,用以构建语义化的 url 网址。

pre_save + slugify 构建 SlugField

完整的代码如下示例:

  • slug 是唯一的字段,不能出现重复 slug,所以 SlugField 的属性是 unique=True
  • 使用 pre_save.connect(pre_save_post_receiver, sender=Post) 把 Post 这个 models 以及自定义的处理函数pre_save_post_receiver 连接起来,表示在 Post 存储之前,先用该函数进行数据预处理。
  • 预处理主要针对 slug,如果没有 slug,就把标题 title 用 slugify 转化成 slug
  • 如果数据库中已经有了相同的 slug,就把之前的 slug 对应的数据的 id 拿过来,拼接在本次 slug 的后面。当然也可以用其他方法进行区分,避免重复。

在 models.py

from django.db import models
from django.db.models.signals import pre_save
from django.utils.text import slugify

class Post(models.Model):
    # ...
    title = models.CharField(max_length=150)
    slug = models.SlugField(unique=True)

def create_slug(instance):
    slug = slugify(instance.title)
    qs = Post.objects.filter(slug=slug).order_by("-id")
    exists = qs.exists()
    if exists:
        new_slug = "{0}-{1}".format(slug, qs.first().id)
        return new_slug
    return slug


def pre_save_post_receiver(sender, instance, *args, **kwargs):
    if not instance.slug:
        instance.slug = create_slug(instance)


pre_save.connect(pre_save_post_receiver, sender=Post)

参考

官方文档:Models signals: pre_save

官方文档:SlugField

stackoverflow 解释 What is a “slug” in Django?

针对中文的标题,slugify会导致空字符串,这时候可以用第三方包 django-uuslug 转化成拼音

在 Django 中生成 slug

你可能感兴趣的:(Django 用 Signals 的 pre_save 构建 models 的 SlugField、)