我有一个相当复杂的Django模型,其中包含一些只能在某些情况下保存的字段。举个简单的例子,
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=200)
counter = models.IntegerField(default=0)
def increment_counter(self):
self.counter = models.F('counter') + 1
self.save(update_fields=['counter'])
这里我使用F expressions来增加计数器时避免竞争条件。我通常永远不想将计数器的值保存在increment_counter
函数之外,因为这可能会撤消从另一个线程或进程调用的增量。
所以问题是,默认情况下,在模型的保存功能中排除某些字段的最佳方式是什么?我尝试了以下
def save(self, **kwargs):
if update_fields not in kwargs:
update_fields = set(self._meta.get_all_field_names())
update_fields.difference_update({
'counter',
})
kwargs['update_fields'] = tuple(update_fields)
super().save(**kwargs)
但结果为ValueError: The following fields do not exist in this model or are m2m fields: id
。我当然可以在差异更新中添加id
和任何m2m字段,但这开始看起来像一个不可维护的混乱,特别是一旦其他模型开始引用这个,这将在{{1中添加其他名称需要从self._meta.get_all_field_names()
中排除。
为了它的价值,我主要需要这个功能来与django管理站点进行交互;代码中的每个其他位置都可以相对使用正确的update_fields
轻松调用model_obj.save()
。
答案 0 :(得分:1)
我最终使用了以下内容:
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=200)
counter = models.IntegerField(default=0)
default_save_fields = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.default_save_fields is None:
# This block should only get called for the first object loaded
default_save_fields = {
f.name for f in self._meta.get_fields()
if f.concrete and not f.many_to_many and not f.auto_created
}
default_save_fields.difference_update({
'counter',
})
self.__class__.default_save_fields = tuple(default_save_fields)
def increment_counter(self):
self.counter = models.F('counter') + 1
self.save(update_fields=['counter'])
def save(self, **kwargs):
if self.id is not None and 'update_fields' not in kwargs:
# If self.id is None (meaning the object has yet to be saved)
# then do a normal update with all fields.
# Otherwise, make sure `update_fields` is in kwargs.
kwargs['update_fields'] = self.default_save_fields
super().save(**kwargs)
这似乎适用于我在其他模型中作为ForeignKey引用的更复杂的模型,尽管可能存在一些它未涵盖的边缘情况。
答案 1 :(得分:0)
我受clwainwright's answer的启发,创建了一个mixin类以使其易于添加到模型中。尽管它使用第二个mixin类来跟踪受this答案启发而更改的字段。