Skip to main content
 首页 » 编程设计

python之Django之如何以子类形式而不是父类形式返回 QuerySet 中的所有对象

2024年11月24日21duanxz

我有一个 Set 类,它与 Item 具有多对多关系。我有很多“集合”对象,它们都包含很多“项目”。

但是,Item 类已被子类化以创建 Article、Podcast、Video 和 剧集。基本上,数据库中的所有内容最初都是一个项目。如果 my_set 是一个 Set 实例,包含 Items - 我如何创建一个 Queryset 以其子类形式返回这些对象?也就是说,我得到的不是一个充满 Item 实例的查询集,而是一个包含 Article、Episode、Video、Podcast< 的查询集 实例。

如何让“my_set.objects.all().as_subclass()”工作?

class Item(models.Model, AdminVideoMixin): 
   base_attributes = 'foo' 
 
     def as_episode(self): 
       return Episode.objects.get(slug=self.slug) 
 
class Video(Item):  
   class specific fields 
 
class Article(Item): 
   class specific fields 
 
class Podcast(Item): 
   class specific fields 
 
class Episode(Item): 
   class specific fields 
 
class Set(Item): 
    front_page = models.BooleanField(max_length=300, blank=False, default=False, null=False) 
    items = models.ManyToManyField(Item, related_name='in_sets', through='SetMeta', max_length=5000,) 
 
    def get_absolute_url(self): 
        return reverse('foo') 
 
    def ordered(self): 
        return self.items.all().order_by('-itemOrder__order') 
 
    def episodes(self): 
        episode_list = [] 
        for each_item in self.items.all(): 
           episode_list.append(each_item.as_episode()) 
        return episode_list 
 
 
    def __str__(self): 
        return self.title 
 

如您所见,我尝试了两种方法 - 在 Item() 上编写一个模型方法,它将自身作为 Episode 返回 - 但这仅适用于单个实例而不是查询集。因此,我在 Set 上写了一个方法,可以对 self 中的所有项目执行该方法,但这不会产生 Queryset,只是一个列表,感觉很乱?

请您参考如下方法:

更新:刚刚浏览了 django-polymorphic documentation再次,它似乎正是您想要的。所以我的其余答案可能不是很有用,除非你被禁止从 django-packages 中取出代码。

我认为 Django 没有提供一种方法来表达返回多个模型类型对象的查询集。查询集应该映射 inro SQL 查询,我不认为 SQL 可以从多个表中混合返回行。 (我不是 SQL 专家,所以我可能是错的)。但是,如果您不需要列表,Python 提供了一种获取查询集并将转换应用于它返回的每个 Item 实例的方法:生成器函数。因此,例如,您可以编写代码

 def items_as_subclasses(qs): 
     for instance in qs: 
         try: 
             yield instance.video 
             continue 
         except Video.DoesNotExist: 
             pass 
         try: 
             yield instance.article 
             continue 
         except Article.DoesNotExist: 
              pass 
 
         try: ... 
 
         raise ProbableCodingError( 
            f"Item instance id={instance.id} does not have a known subclass" 
         ) 

然后写

for item_subclass_instance in items_as_subclasses(queryset): 
    # whatever 

或者确实将 "items":items_as_subclasses(queryset) 传递到模板渲染中 上下文。

如果可能的子类列表很长,最好在基类中有一个 subclass_type 字段,并使用它直接转到唯一有效的子类字段。

从基类到它的特定子类有一个可为 null 的 OneToOne 链接,因此您可以编写查询子类的查询集。

或者您可以调查 django-polymorphic ,我曾经浏览过,我依稀记得是为了这种用法。