Django ORM, 通过模型继承按类型过滤对象

1 投票
4 回答
2932 浏览
提问于 2025-04-15 14:26

我有两个模型……

一个是父类(Parent),一个是子类(Child)。

子类是从父类扩展出来的。

当我执行

Parent.objects.all()时,我得到了父类和子类的所有对象。

但我只想要父类的对象。

有没有什么方法,比如使用 Parent.objects.filter(),可以让我只获取父类的对象,而不包括那些扩展自父类的对象呢?

4 个回答

2

你确定继承是这里的正确解决方案吗?那这样呢?

class MyModel(models.Model):
    foo = models.IntegerField()
    parent = models.ForeignKey("self", null=True)

这样你就可以查询所有作为父类的对象了:

parents = MyModel.objects.filter(parent__isnull=True)
children = MyModel.objects.filter(parent__isnull=False)

@Alex:根据类型过滤是行不通的。Django的继承模型其实并没有那么复杂。例如,使用这些模型:

class Parent(models.Model):
    foo = models.CharField(max_length=5)

class Child(Parent):
    bar = models.CharField(max_length=5)

你会得到这样的行为:

In [1]: from myexample.models import Parent, Child

In [2]: p = Parent(foo='x')

In [3]: p.save()

In [4]: p2 = Parent(foo='y')

In [5]: p2.save()

In [6]: c1 = Child(bar='1', foo='a')

In [7]: c1.save()

In [8]: c2 = Child(bar='2', foo='b')

In [9]: c2.save()

In [10]: len(Parent.objects.all())
Out[10]: 4

In [11]: len([p for p in Parent.objects.all() if type(p) is Parent])
Out[11]: 4

In [12]: len(Child.objects.all())
Out[12]: 2
13

我找到了一种更好的解决办法,使用django的ORM(对象关系映射),而且不需要对你的模型做任何改动(比如不需要使用抽象基类ABC):

class Parent(models.Model):
    field1 = models.IntegerField()
    field2 = models.IntegerField()

class Child(Parent):
    field3 = models.IntegerField()

#Return all Parent objects that aren't also Child objects:
Parent.objects.filter(child=None)

这样会生成一个查询(概念上是这样的,实际的查询可能会有所不同):

SELECT "ap_parent"."field1","ap_parent"."field2" FROM "ap_parent" INNER JOIN "ap_child" ON ("parent"."parent_ptr_id" = "ap_child"."parent_ptr_id") WHERE "ap_child"."parent_ptr_id" IS NULL

4

也许在这里使用一个抽象基类会比直接用继承更好。这个抽象基类包含了你所有类中共有的字段。所以在你的情况下,你可以创建一个抽象基类,它的定义大致和你现在的父类相同,然后再有两个类从这个抽象基类继承,这两个类分别对应你的父类和子类。

class ABC(models.Model):
    field1 = models.CharField(max_length=200)
    field2 = models.CharField(max_length=100)
    ....

    class Meta:
        abstract = True

class Parent(ABC):
    ....

class Child(ABC):
    parent = models.ForeignKey(Parent)

想了解更多信息,可以查看这里:模型继承和抽象基类

撰写回答