Django - 覆盖 Model.create() 方法?

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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)

重点是:

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

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

【讨论】:

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

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

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

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

【讨论】:

以上是关于Django - 覆盖 Model.create() 方法?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 无法使用 Model::create() 和 $model->save() 创建模型

在 Sequelize Model.create 上设置 raw = true

MongooseJS - 具有现有引用的Model.create

Mongoose:Model.create 和 Collection.insert 有啥区别

传递文档数组时,Mongoose 中 Model.create 的原子性

传递文档数组时,Mongoose 中 Model.create 的原子性