Django-重写Model.create()方法?


87

Django文档只列出了重写的例子save()delete()。但是,我只想在模型创建时为其定义一些额外的处理。对于任何熟悉Rails的人来说,这等同于创建:before_create过滤器。这可能吗?

Answers:


160

__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)

7
我实际上找到了一个使用信号的解决方案:docs.djangoproject.com/en/dev/topics/signals(具体来说是pre_save信号)。但是,这似乎是一个更为实用的解决方案。谢谢你
Ground5hark 2010年

4
我假设您的意思是覆盖管理器方法create?这是一个有趣的解决方案,但是在使用Object(**kwargs).save()或其他任何形式创建对象的情况下,该方法将无效。
Zach

3
我不认为这是hack。这是官方的解决方案之一。

6
不是super(MyModel, self).save(*args, **kwargs)吗?
Mark Chackerian

1
也许检查self.pk不是检查对象是新创建还是刚更新的最佳选择。有时候你在创建的时候(定制非数据库生成的值一样提供对象ID KSUID),它会导致这一条款从未执行...有self._state.adding值,以确保它是否节能的第一次,或只是更新,这在这些情况下会有所帮助。
Shahinism

22

如何创建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查询。
Mike Shultz

信号与自定义保存方法的链接已断开。
Sander Vanden Hautte

21

这很古老,有一个公认的答案(Zach是有效的),也有一个比较惯用的答案(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. 使用信号(在此处查看更多官方文档
  2. 使用一种很好的命名空间方法(如果有意义)...并且我将其标记为,@classmethod而不是@staticmethod因为最有可能您最终需要在代码中引用静态类成员

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


15

为了从字面上回答这个问题,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的methodcreate方法,以在实际创建实例之前进行一些额外的处理。

注意:类似的代码

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

将执行此修改后的create方法,但是类似

my_new_unsaved_instance = MyModel(my_field='my_field value')

将不会。




1

首选答案是正确的,但是如果您的模型是从UUIDModel派生的,则无法判断对象是否正在创建的测试。pk字段将已经有一个值。

在这种情况下,您可以执行以下操作:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.