如何在Django中查询仅属于Post模型的数据,排除子类AdvertisePost?
嘿,这个问题我太熟悉了!Django的多表继承默认会让父类的QuerySet把所有子类实例也包含进来,不过咱们有几种靠谱的方法能只拿到Post本身的数据:
方法1:通过模型名称过滤
每个Django模型实例都带有_meta.model_name属性,父类Post的这个值是'post',而子类AdvertisePost的是'advertisepost'(默认是类名小写)。我们可以直接用这个属性过滤:
posts = Post.objects.filter(_meta__model_name='post')
这个方法简单直观,适合快速验证,不过要注意如果后续修改了模型名称,这里也要同步调整。
方法2:用Exists子查询高效排除子类
因为多表继承的机制是:子类AdvertisePost会生成一张单独的表,并且通过post_ptr_id字段和父表Post做一对一关联。也就是说,所有属于AdvertisePost的父类记录,在子类表中都有对应的条目。我们可以用Django的Exists子查询来高效过滤掉这些记录:
from django.db.models import Exists, OuterRef posts = Post.objects.filter(~Exists( AdvertisePost.objects.filter(post_ptr_id=OuterRef('id')) ))
这个方法在数据库层面直接完成过滤,不会额外生成大的IN子句,数据量大的时候性能表现最好,非常推荐用这个。
方法3:通过主键列表排除
如果你的数据量不大,也可以先获取所有子类关联的父类主键,再从父类QuerySet中排除这些主键:
advertise_post_ids = AdvertisePost.objects.values_list('post_ptr_id', flat=True) posts = Post.objects.exclude(pk__in=advertise_post_ids)
这个写法更直白,但数据量大的时候,IN子句可能会影响查询性能,所以优先选方法2。
补充说明
Django多表继承的设计就是父类QuerySet会包含所有子类实例,因为它们本质上也是父类的实例(继承关系)。所以我们需要主动过滤掉那些被子类关联的父类记录,才能拿到纯Post模型的数据。
内容的提问来源于stack exchange,提问作者Zorgan
相关产品推荐
相关产品推荐

