保存期间如何排除Django模型字段? [英] How to exclude django model fields during a save?

查看:266
本文介绍了保存期间如何排除Django模型字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当复杂的Django模型,其中包含一些字段,仅在某些情况下才应保存。作为一个简单的例子,

 从django.db导入模型

class MyModel(models.Model) :
name = models.CharField(max_length = 200)
counter = models.IntegerField(default = 0)

def增量计数器(self):
self.counter = models.F('counter')+ 1
self.save(update_fields = ['counter'])

这里我使用的是 F表达式,以避免在增加计数器时出现竞争情况。通常,我永远都不想在 increment_counter 函数之外保存counter的值,因为那样可能会撤消从另一个线程或进程调用的增量。



所以问题是,在模型的保存功能中默认排除某些字段的最佳方法是什么?我尝试了以下操作

  def save(self,** kwargs):
如果update_fields不在kwargs中:
update_fields = set(self._meta.get_all_field_names())
update_fields.difference_update({
'counter',
})
kwargs ['update_fields'] =元组(update_fields)
super()。save(** kwargs)

但是结果在 ValueError中:以下字段在此模型中不存在,或者是m2m字段:id 。我当然可以在差异更新中添加 id 和任何m2m字段,但是那似乎开始变得难以维护,尤其是一旦其他模型开始引用该字段时,将会在 self._meta.get_all_field_names()中添加其他名称,这些名称需要从 update_fields 中排除。



对于它的价值,我最需要此功能来与django管理站点进行交互;代码中的每个其他位置都可以相对轻松调用具有正确的 update_fields model_obj.save() c>。

解决方案

我最终使用以下命令:



<$ p从django.db中导入$ p> 导入模型

类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)
如果self.default_save_fields为None:
#仅在第一个加载的对象上调用此块
default_save_fields = {
f.name为self._meta.get_fields()中的f如果是f.concrete而不是f.many_to_many而不是f.auto_created
}
default_save_fields.difference_update({
'counter',
})
self .__ class __。default_save_fields =元组(default_save_fields)

def增量计数器(self):
self.counter = models.F('counter')+ 1
self.save (update_fields = ['counter'])

def save(self,** kwargs):
如果self.id不为None并且'update_fields'不在kwargs中:
#如果self.id为None(表示该对象尚未保存)
#然后对所有字段进行常规更新。
#否则,请确保`update_fields`在kwargs中。
kwargs ['update_fields'] = self.default_save_fields
super()。save(** kwargs)

这似乎对我更复杂的模型(在其他模型中称为ForeignKey)起作用,尽管可能存在一些无法涵盖的极端情况。


I've got a fairly complicated Django model that includes some fields that should only be saved under certain circumstances. As a simple example,

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'])

Here I'm using F expressions to avoid race conditions while incrementing the counter. I'll generally never want to save the value of counter outside of the increment_counter function, as that would potentially undo an increment called from another thread or process.

So the question is, what's the best way to exclude certain fields by default in the model's save function? I've tried the following

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)

but that results in ValueError: The following fields do not exist in this model or are m2m fields: id. I could of course just add id and any m2m fields in the difference update, but that then starts to seem like an unmaintainable mess, especially once other models start to reference this one, which will add additional names in self._meta.get_all_field_names() that need to be excluded from update_fields.

For what it's worth, I mostly need this functionality for interacting with the django admin site; every other place in the code could relatively easily call model_obj.save() with the correct update_fields.

解决方案

I ended up using the following:

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)

This seems to work for my more complicated model which is referenced in other models as a ForeignKey, although there might be some edge cases that it doesn't cover.

这篇关于保存期间如何排除Django模型字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆