Django - ForeignKey:有可能让多种对象引用自身吗?

时间:2016-11-19 06:49:22

标签: django django-models

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()

但它仅适用于ModelAModelB所引用的ModelC,并非适用于所有models。(例如II称为modelB.delete(),它赢了'停用其子模型(如果ModelB有子模型)

我想制作general方法,以便我可以在任何models中使用它:

all_model_objects = ModelsReferencingMe.objects.filter(parent=self)

有没有办法实现这个?

1 个答案:

答案 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),等等。

当然,有一些警告:

  1. QuerySet.update不会调用.save或任何信号,因此如果您遇到问题,请将单个更新查询替换为单独的.save次来电。
  2. 除了BaseModel子类之外,上面的代码不期望任何其他相关模型。如果还有其他型号,请务必亲自检查。
  3. 维护deactivation_field取决于您,如果您在子类中忘记它,请指望错误。
  4. 请注意,您的这个想法需要使用嵌套循环进行深度递归,这是一场性能噩梦。