遍历抽象Django模型的所有外键相关子类

3 投票
1 回答
1520 浏览
提问于 2025-04-19 20:37

我正在尝试遍历一个特定的 Django 模型所有相关的外键模型。可能有 13 个不同的模型与之有外键关系,但它们都继承自同一个抽象基类。我想在一个循环中遍历它们,以修改属于父类的一个字段。我的代码看起来是这样的:

class ClusteringRecord(models.Model):
    """
    An abstract class to hold data that is repeated from model to model
    """

    wip = models.ForeignKey(
        ClusteringWIP, null=True, on_delete = models.SET_NULL)
    cluster_comment = models.CharField(max_length=1000, null=True, blank=True)

    class Meta:
        abstract = True


class ClusteringNPIRecord(ClusteringRecord):
    """Django Model object representing an NPI record that was not able to be
    clustered by automatic clustering logic in the database.
    """

    npi_id = models.CharField(max_length=1000, null=True)
    npi_number = models.CharField(max_length=1000, null=True)


class ClusteringDEARecord(ClusteringRecord):
    """Django Model object representing a DEA record that was not able to be
    clustered by automatic clustering logic in the database.
    """

    dea_id = models.CharField(max_length=1000, null=True)
    dea_number = models.CharField(max_length=1000, null=True)

我想用的代码是这样的:

def cancel_and_return(request, list_type, wip_id):
    """
    destroys lock object and returns user to most recent version of worklist
    :param request:
    :param wip_id: pk of current ClusteringWIP
    :return: HTTP redirect to worklist home
    """
    wip = ClusteringWIP.objects.select_related().get(pk=wip_id)
    for record in wip.clusteringrecord_set.all():
        record.cluster_comment = None
        record.save()
    wip.delete()

但是它告诉我 clusteringrecord_set 是无效的。有没有办法遍历与这个类相关的所有子类?否则我现在用 13 个不同的 for 循环来完成这个任务,这样就违反了 DRY(不要重复自己)原则。

实际上,这个基类适用于所有可能使用 ClusteringWIP 作为外键的 13 个模型,所以简单地遍历所有相关的模型(不管是什么类)就能达到同样的效果。如果这样做是最好的方法,请告诉我。不过,我还是很想知道上面那个问题的答案,以备将来使用。

另外,在寻找答案的过程中,我发现了一个 Django 的 pre_delete 信号,似乎更符合我想要做的事情(也就是在删除 ClusteringWIP 时,将任何相关模型的 'cluster_comment' 字段置为空)。如果有人能给我一个示例,告诉我如何使用这个信号来完成我的任务,我将非常感激。

谢谢。

1 个回答

4

你可以使用ClusteringRecord的subclass()方法。比如说:

@classmethod
def related_set(cls, wip):
  classes = cls.__subclasses__()
  return sum([c.objects.filter(wip=wip).all() for c in classes], [])

然后用这个方法来遍历你的对象。

对于pre_delete信号,你需要在你的应用程序中有一个signals.py文件,可能看起来像这样:

from django.db.models.signals import pre_delete, post_delete
from django.dispatch import receiver

from myapp.models import ClusteringWIP

@receiver(pre_delete, sender=ClusteringWIP)
def on_instance_delete(sender, instance, **kwargs):
    instance.on_pre_delete()

使用ClusteringWIP::on_pre_delete方法,像这样:

def on_pre_delete(self):
  for record in ClusteringRecord.related_set(self):
    record.cluster_comment = None
    record.save()

撰写回答