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