【问题标题】:Django - Overriding the Model.create() method?Django - 覆盖 Model.create() 方法?
【发布时间】:2011-01-19 11:01:57
【问题描述】:

Django docs 仅列出了覆盖save()delete() 的示例。不过,我想为我的模型仅在创建时定义一些额外的处理。对于任何熟悉 Rails 的人来说,这相当于创建一个 :before_create 过滤器。这可能吗?

【问题讨论】:

    标签: django django-models django-forms


    【解决方案1】:

    覆盖__init__() 将允许您在模型实例化时执行代码。不要忘记拨打家长的__init__()

    【讨论】:

    • 啊,是的,这就是答案。不知道我怎么忽略了这一点。谢谢伊格纳西奥。
    【解决方案2】:

    覆盖__init__() 将导致在实例化对象的python 表示时执行代码。我不知道rails,但:before_created 过滤器听起来像是在数据库中创建对象时要执行的代码。如果要在数据库中创建新对象时执行代码,则应覆盖save(),检查对象是否具有pk 属性。代码看起来像这样:

    def save(self, *args, **kwargs):
        if not self.pk:
            # This code only happens if the objects is
            # not in the database yet. Otherwise it would
            # have pk
        super(MyModel, self).save(*args, **kwargs)
    

    【讨论】:

    • 我实际上找到了一个使用信号的解决方案:docs.djangoproject.com/en/dev/topics/signals(特别是 pre_save 信号)。然而,这似乎是一个更加务实的解决方案。非常感谢。
    • 我假设您的意思是覆盖管理器方法create?这是一个有趣的解决方案,但在使用 Object(**kwargs).save() 或任何其他变体创建对象的情况下它不起作用。
    • 我不认为这是一个 hack。这是官方解决方案之一。
    • 不应该是super(MyModel, self).save(*args, **kwargs)吗?
    • 也许检查self.pk 不是检查对象是新创建还是刚刚更新的最佳选择。有时您在创建时提供对象 ID(自定义的非数据库生成值,如 KSUID),这将导致该子句永远不会执行...有 self._state.adding 值来确保它是否为第一个保存时间或只是更新,这在这些情况下会有所帮助。
    【解决方案3】:

    如何创建 post_save 信号的示例(来自http://djangosnippets.org/snippets/500/

    from django.db.models.signals import post_save
    from django.dispatch import receiver
    
    @receiver(post_save, sender=User)
    def create_profile(sender, instance, created, **kwargs):
        """Create a matching profile whenever a user object is created."""
        if created: 
            profile, new = UserProfile.objects.get_or_create(user=instance)
    

    这里是关于最好使用信号还是自定义保存方法的深思熟虑的讨论https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/django-signals-vs-custom-save-method/

    在我看来,为这项任务使用信号更健壮、更容易阅读但更长。

    【讨论】:

    • 这是首选方法,而不是弄乱对象内部,但是,如果您对相关模型进行修改,而不仅仅是在上面的示例中创建另一个,不要忘记致电instance.save()。因此,在这种情况下,还会有性能损失,因为将对数据库进行一次 INSERT 和一次 UPDATE 查询。
    • 信号与自定义保存方法的链接已损坏。
    【解决方案4】:

    您可以使用自定义管理器覆盖 create 方法或在模型类上添加 classmethod。 https://docs.djangoproject.com/en/1.11/ref/models/instances/#creating-objects

    【讨论】:

      【解决方案5】:

      从字面上回答这个问题,模型管理器中的 create 方法是在 Django 中创建新对象的标准方法。要覆盖,请执行以下操作

      from django.db import models
      
      class MyModelManager(models.Manager):
          def create(self, **obj_data):
              # Do some extra stuff here on the submitted data before saving...
              # For example...
              obj_data['my_field'] = my_computed_value(obj_data['my_other_field'])
      
              # Now call the super method which does the actual creation
              return super().create(**obj_data) # Python 3 syntax!!
      
      class MyModel(models.model):
          # An example model
          my_field = models.CharField(max_length=250)
          my_other_field = models.CharField(max_length=250)
      
          objects = MyModelManager()
      

      在此示例中,我将覆盖 Manager 的方法 create 方法,以便在实际创建实例之前进行一些额外的处理。

      注意:类似

      的代码

      my_new_instance = MyModel.objects.create(my_field='my_field value')

      将执行这个修改后的create方法,但代码类似于

      my_new_unsaved_instance = MyModel(my_field='my_field value')

      不会。

      【讨论】:

      • 我相信你必须打电话给super(MyModelManager, self).create(**obj_data),而不仅仅是super().create(**obj_data)。除此之外,这是一个绝妙的解决方案
      • 正如评论所说,Python 3 语法!!
      • 我认为这是“保存”重载之后的最佳解决方案。信号的逻辑更难遵循,不适合 Django 模型基于类的简洁解决方案。我总是更喜欢重载模型方法或使用管理器,而不是信号。
      【解决方案6】:

      这是旧的,有一个公认的有效答案(Zach's),还有一个更惯用的答案(Michael Bylstra's),但由于它仍然是大多数人在 Google 上看到的第一个结果,我认为我们需要更多最佳实践现代 django 风格的答案在这里

      from django.db.models.signals import post_save
      
      class MyModel(models.Model):
          # ...
          @classmethod
          def post_create(cls, sender, instance, created, *args, **kwargs):
              if not created:
                  return
              # ...what needs to happen on create
      
      post_save.connect(MyModel.post_create, sender=MyModel)
      

      重点是:

      1. 使用信号(阅读更多here in the official docs
      2. 使用良好命名空间的方法(如果有意义的话)...我将其标记为@classmethod 而不是@staticmethod,因为您很可能最终需要在代码中引用静态类成员李>

      如果核心 Django 有一个实际的post_create 信号,那就更清楚了。 (恕我直言,如果您需要传递一个布尔参数来更改方法的行为,那应该是 2 个方法。)

      【讨论】:

      • post_init 不是更合适吗?我是信号新手,但我认为您设置它的方式,每个save() 都会调用该方法,因此更新也会调用它。
      【解决方案7】:

      首选答案是正确的,但如果您的模型派生自 UUIDModel,则判断对象是否正在创建的测试不起作用。 pk 字段已经有值了。

      在这种情况下,您可以这样做:

      already_created = MyModel.objects.filter(pk=self.pk).exists()

      【讨论】:

        猜你喜欢
        • 2017-12-11
        • 2020-05-03
        • 2020-06-14
        • 2015-02-25
        • 1970-01-01
        • 2016-07-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多