美文网首页Django
Django 用 Signals 的 pre_save 构建 m

Django 用 Signals 的 pre_save 构建 m

作者: Tim_Lee | 来源:发表于2017-07-18 16:09 被阅读22次

    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 构建 m

        本文链接:https://www.haomeiwen.com/subject/tvdhkxtx.html