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 有啥区别