【发布时间】:2011-01-19 11:01:57
【问题描述】:
Django docs 仅列出了覆盖save() 和delete() 的示例。不过,我想为我的模型仅在创建时定义一些额外的处理。对于任何熟悉 Rails 的人来说,这相当于创建一个 :before_create 过滤器。这可能吗?
【问题讨论】:
标签: django django-models django-forms
Django docs 仅列出了覆盖save() 和delete() 的示例。不过,我想为我的模型仅在创建时定义一些额外的处理。对于任何熟悉 Rails 的人来说,这相当于创建一个 :before_create 过滤器。这可能吗?
【问题讨论】:
标签: django django-models django-forms
覆盖__init__() 将允许您在模型实例化时执行代码。不要忘记拨打家长的__init__()。
【讨论】:
覆盖__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)
【讨论】:
create?这是一个有趣的解决方案,但在使用 Object(**kwargs).save() 或任何其他变体创建对象的情况下它不起作用。
super(MyModel, self).save(*args, **kwargs)吗?
self.pk 不是检查对象是新创建还是刚刚更新的最佳选择。有时您在创建时提供对象 ID(自定义的非数据库生成值,如 KSUID),这将导致该子句永远不会执行...有 self._state.adding 值来确保它是否为第一个保存时间或只是更新,这在这些情况下会有所帮助。
如何创建 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 查询。
您可以使用自定义管理器覆盖 create 方法或在模型类上添加 classmethod。 https://docs.djangoproject.com/en/1.11/ref/models/instances/#creating-objects
【讨论】:
从字面上回答这个问题,模型管理器中的 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)。除此之外,这是一个绝妙的解决方案
这是旧的,有一个公认的有效答案(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)
重点是:
@classmethod 而不是@staticmethod,因为您很可能最终需要在代码中引用静态类成员李>
如果核心 Django 有一个实际的post_create 信号,那就更清楚了。 (恕我直言,如果您需要传递一个布尔参数来更改方法的行为,那应该是 2 个方法。)
【讨论】:
post_init 不是更合适吗?我是信号新手,但我认为您设置它的方式,每个save() 都会调用该方法,因此更新也会调用它。
首选答案是正确的,但如果您的模型派生自 UUIDModel,则判断对象是否正在创建的测试不起作用。 pk 字段已经有值了。
在这种情况下,您可以这样做:
already_created = MyModel.objects.filter(pk=self.pk).exists()
【讨论】: