models.py
class BaseModel(TimeStampedModel):
is_active = models.BooleanField(default=True)
class Meta:
abstract = True
ordering = ('-created',)
def deactivate(self):
# Make all child objects is_active=False
class ModelA(BaseModel)
name = models.CharField(max_length=10)
class ModelB(BaseModel):
model_a = models.ForeignKey(ModelA)
name = models.CharField(max_length=20)
class ModelC(BaseModel):
model_a = models.ForeignKey(ModelA)
name = models.CharField(max_length=20)
如上所示,我想制作deactivate
方法,将所有儿童的is_active
字段设为False
。
有人可能会这么想:
def deactivate(self):
# Make all child objects is_active=False
b = ModelB.objects.filter(parent=self)
c = ModelC.objects.filter(parent=self)
for obj in b:
obj.is_active = False
obj.save()
for obj in c:
obj.is_active = False
obj.save()
但它仅适用于ModelA
,ModelB
所引用的ModelC
,并非适用于所有models
。(例如II称为modelB.delete()
,它赢了'停用其子模型(如果ModelB
有子模型)
我想制作general
方法,以便我可以在任何models
中使用它:
all_model_objects = ModelsReferencingMe.objects.filter(parent=self)
有没有办法实现这个?
答案 0 :(得分:0)
我认为您可以在此处使用Model._meta API。
class BaseModel(object):
deactivation_field = None # don't forget to override in subclasses
is_active = models.BooleanField(default=True)
class Meta:
abstract = True
ordering = ('-created',)
def deactivate(self):
related_models = [
f.related_model for f in self.__class__._meta.get_fields()
if (f.one_to_many or f.one_to_one)
and f.auto_created and not f.concrete
]
filter_clause = {self.deactivation_field: self}
for model in related_models:
items = model.objects.filter(**filter_clause)
items.update(is_active=False)
for item in items:
item.deactivate()
使用如下:
class ModelA(BaseModel)
deactivation_field = 'model_a'
name = models.CharField(max_length=10)
class ModelB(BaseModel):
deactivation_field = 'model_b'
model_a = models.ForeignKey(ModelA)
name = models.CharField(max_length=20)
说明:每次在某个模型实例上调用.deactivate()
时,它都会遍历模型,在单个SQL is_active
中将False
设置为UPDATE
(单个更新)当然,对于每个表,然后循环遍历所有给定的实例,为每个实例调用.deactivate()
。我添加deactivation_field
的目的是让deactivate
方法知道如何过滤子模型的项目。例如,如果ModelA
filter_clause
将产生.filter(model_a=self)
,则ModelB
- .filter(model_b=self)
,等等。
当然,有一些警告:
QuerySet.update
不会调用.save
或任何信号,因此如果您遇到问题,请将单个更新查询替换为单独的.save
次来电。BaseModel
子类之外,上面的代码不期望任何其他相关模型。如果还有其他型号,请务必亲自检查。deactivation_field
取决于您,如果您在子类中忘记它,请指望错误。