【问题标题】:Populating django field with pre_save()?用 pre_save() 填充 django 字段?
【发布时间】:2011-09-21 15:55:52
【问题描述】:
class TodoList(models.Model):
    title = models.CharField(maxlength=100)
    slug = models.SlugField(maxlength=100)
    def save(self):
        self.slug = title
        super(TodoList, self).save()

我假设以上是在TodoList表中插入标题时如何创建和存储slug,如果不是,请纠正我!

无论如何,我一直在研究 pre_save() 作为另一种方法,但无法弄清楚它是如何工作的。 pre_save() 怎么做?

是这样的

def pre_save(self):
     self.slug = title

我猜没有。执行此操作的代码是什么?

谢谢!

【问题讨论】:

    标签: python database django model triggers


    【解决方案1】:

    pre_save() signal hook 确实是处理大量模型的 slugification 的好地方。诀窍是要知道哪些模型需要生成 slug,哪些字段应该是 slug 值的基础。

    为此我使用了一个类装饰器,它可以让我标记模型以进行自动 slug 生成,以及它基于什么字段:

    from django.db import models
    from django.dispatch import receiver
    from django.utils.text import slugify
    
    def autoslug(fieldname):
        def decorator(model):
            # some sanity checks first
            assert hasattr(model, fieldname), f"Model has no field {fieldname!r}"
            assert hasattr(model, "slug"), "Model is missing a slug field"
    
            @receiver(models.signals.pre_save, sender=model, weak=False)
            def generate_slug(sender, instance, *args, raw=False, **kwargs):
                if not raw and not instance.slug:
                    source = getattr(instance, fieldname)
                    slug = slugify(source)
                    if slug:  # not all strings result in a slug value
                        instance.slug = slug
            return model
        return decorator
    

    这只会为特定模型注册一个信号处理程序,并允许您使用每个修饰的模型来改变源字段:

    @autoslug("name")
    class NamedModel(models.Model):
        name = models.CharField(max_length=100)
        slug = models.SlugField()
    
    @autoslug("title")
    class TitledModel(models.Model):
        title = models.CharField(max_length=255)
        slug = models.SlugField()
    

    请注意,不会尝试生成 唯一 slug 值。这将需要检查事务中的完整性异常或使用来自足够大池的 slug 中的随机值以使冲突不太可能发生。完整性异常检查只能在save()方法中进行,不能在信号钩子中进行。

    【讨论】:

    • 我需要将weak=False 添加到@receiver 装饰器以使其正常工作。本地函数可以按照文档 (docs.djangoproject.com/en/3.0/ref/signals/…) 中的说明进行垃圾收集,这会阻止调用我的函数。
    • @Gnietschow:啊,好点子!我碰巧有一个更复杂的实现,我已经为这个答案简化了,而且..失去了参考。所以我自己没有遇到这个问题。我会更新答案。
    【解决方案2】:

    接收函数必须是这样的:

    def my_callback(sender, **kwargs):
        print("Request finished!")
    

    请注意,该函数采用 sender 参数以及通配符关键字参数 (**kwargs);所有信号处理程序都必须采用这些参数。

    所有信号都发送关键字参数,并且可能随时更改这些关键字参数。

    参考here

    【讨论】:

      【解决方案3】:

      您很可能指的是django's pre_save signal。你可以这样设置:

      from django.db.models.signals import pre_save
      from django.dispatch import receiver
      from django.template.defaultfilters import slugify
      
      @receiver(pre_save)
      def my_callback(sender, instance, *args, **kwargs):
          instance.slug = slugify(instance.title)
      

      如果你没有在装饰器中包含 sender 参数,比如@receiver(pre_save, sender=MyModel),那么所有模型都会调用回调。

      您可以将代码放在应用程序执行期间解析的任何文件中,models.py 是一个很好的地方。

      【讨论】:

      • @Derek:只需覆盖save()。它更简单、更可预测。
      • 更好?它的作用基本相同...如果您想更改现有应用程序的功能,那么信号肯定是首选方式...
      • 太棒了。这个答案应该包含在 Django 文档中。文档中确实没有关于使用 signals 的示例。
      • @Firula 对,你是。我应该说没有solid例子。谢谢顺便说一句。
      • @simon-steinberger pre_save not 当您在 QuerySets 上使用 update 方法时调用 - Django docs 说“最后,意识到 update() 在SQL 级别,因此不会在模型上调用任何 save() 方法,也不会发出 pre_save 或 post_save 信号(这是调用 Model.save() 的结果)。"
      【解决方案4】:

      您可以使用 django 信号.pre_save:

      from django.db.models.signals import post_save, post_delete, pre_save
      
      class TodoList(models.Model):
          @staticmethod
          def pre_save(sender, instance, **kwargs):
              #do anything you want
      
      pre_save.connect(TodoList.pre_save, TodoList, dispatch_uid="sightera.yourpackage.models.TodoList") 
      

      【讨论】:

      • 无论出于何种原因,接受的解决方案都对我不起作用。但是,这个干净优雅的解决方案确实做到了。
      • 如果要修改模型,不妨修改 save() 方法。仅当您需要将代码与模型解耦时才应使用信号。
      【解决方案5】:
      @receiver(pre_save, sender=TodoList)
      def my_callback(sender, instance, *args, **kwargs):
          instance.slug = slugify(instance.title)
      

      【讨论】:

        猜你喜欢
        • 2014-01-02
        • 1970-01-01
        • 2016-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-23
        • 2021-10-05
        相关资源
        最近更新 更多