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

gro*_*ark 70 django django-models django-forms

Django文档只列出了重写的例子save()delete().但是,我想为我的模型创建一些额外的处理.对于熟悉Rails的人来说,这相当于创建一个:before_create过滤器.这可能吗?

Zac*_*ach 139

__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)
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*tra 21

如何创建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)
Run Code Online (Sandbox Code Playgroud)

这是一个深思熟虑的讨论,是否最好使用信号或自定义保存方法https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/ Django的信号-VS-定制保存法/

在我看来,使用这个任务的信号更健壮,更容易阅读,但更长.


Neu*_*onQ 16

这是古老的,有一个可接受的答案(Zach's),也是一个更惯用的答案(Michael Bylstra's),但由于它仍然是谷歌的第一个结果,大多数人看到,我认为我们需要一个更好的实践现代-jjango风格答案在这里:

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)
Run Code Online (Sandbox Code Playgroud)

关键在于:

  1. 使用信号(在官方文档中阅读更多内容)
  2. 使用一个很好的命名空间的方法(如果有意义的话)......我将其标记为@classmethod而不是@staticmethod因为最有可能你最终需要在代码中引用静态类成员

如果核心Django有一个实际post_create信号,那就更清洁了.(Imho,如果你需要传递一个布尔arg来改变方法的行为,那应该是2个方法.)


Ign*_*ams 7

覆盖__init__()将允许您在实例化模型时执行代码。别忘了给家长打电话__init__()


Mar*_*ian 6

为了从字面上回答这个问题,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()
Run Code Online (Sandbox Code Playgroud)

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

注意:类似的代码

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

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

my_new_unsaved_instance = MyModel(my_field='my_field value')

将不会。

  • 我认为这是“保存”超载之后的最佳解决方案。信号的逻辑更难遵循,并且不适合 Django 模型简洁的基于类的解决方案。我总是更喜欢重载模型方法或使用管理器,而不是信号。 (3认同)
  • 就像评论说的,Python 3 语法!! (2认同)