Django中相关模型的自定义查询集筛选
假设我有两个模型:Django中相关模型的自定义查询集筛选,django,django-queryset,Django,Django Queryset,假设我有两个模型:Book和Author class Author(models.Model): name = models.CharField() country = models.CharField() approved = models.BooleanField() class Book(models.Model): title = models.CharField() approved = models.BooleanField() a
Book
和Author
class Author(models.Model):
name = models.CharField()
country = models.CharField()
approved = models.BooleanField()
class Book(models.Model):
title = models.CharField()
approved = models.BooleanField()
author = models.ForeignKey(Author)
两个模型中的每一个都有一个approved
属性,该属性在网站上显示或隐藏对象。如果未批准书籍
,则该书籍将隐藏。如果作者
未获得批准,则其所有书籍都将被隐藏
为了以简洁的方式定义这些标准,创建自定义查询集看起来是一个完美的解决方案:
class AuthorQuerySet(models.query.QuerySet):
def for_site():
return self.filter(approved=True)
class BookQuerySet(models.query.QuerySet):
def for_site():
reuturn self.filter(approved=True).filter(author__approved=True)
将这些查询集连接到相应的模型后,可以像这样查询它们:Book.objects.for\u site()
,而无需每次硬编码所有筛选
尽管如此,这个解决方案仍然不完美。稍后,我可以决定向作者添加另一个过滤器:
class AuthorQuerySet(models.query.QuerySet):
def for_site():
return self.filter(approved=True).exclude(country='Problematic Country')
但是这个新过滤器只在Author.objects.for\u site()
中有效,而在Book.objects.for\u site()
中无效,因为它是硬编码的
因此,我的问题是:在对不同模型进行筛选时,是否可以应用相关模型的自定义查询集,使其看起来类似于此:
class BookQuerySet(models.query.QuerySet):
def for_site():
reuturn self.filter(approved=True).filter(author__for_site=True)
其中,
for_site
是作者
模型的自定义查询集。我想,我已经提出了一个基于Q
对象的解决方案,这些对象在。这肯定不是一个人能发明的最优雅的解决方案,但它确实有效。请参阅下面的代码
from django.db import models
from django.db.models import Q
######## Custom querysets
class QuerySetRelated(models.query.QuerySet):
"""Queryset that can be applied in filters on related models"""
@classmethod
def _qq(cls, q, related_name):
"""Returns a Q object or a QuerySet filtered with the Q object, prepending fields with the related_name if specified"""
if not related_name:
# Returning Q object without changes
return q
# Recursively updating keywords in this and nested Q objects
for i_child in range(len(q.children)):
child = q.children[i_child]
if isinstance(child, Q):
q.children[i_child] = cls._qq(child, related_name)
else:
q.children[i_child] = ('__'.join([related_name, child[0]]), child[1])
return q
class AuthorQuerySet(QuerySetRelated):
@classmethod
def for_site_q(cls, q_prefix=None):
q = Q(approved=True)
q = q & ~Q(country='Problematic Country')
return cls._qq(q, q_prefix)
def for_site(self):
return self.filter(self.for_site_q())
class BookQuerySet(QuerySetRelated):
@classmethod
def for_site_q(cls, q_prefix=None):
q = Q(approved=True) & AuthorQuerySet.for_site_q('author')
return cls._qq(q, q_prefix)
def for_site(self):
return self.filter(self.for_site_q())
######## Models
class Author(models.Model):
name = models.CharField(max_length=255)
country = models.CharField(max_length=255)
approved = models.BooleanField()
objects = AuthorQuerySet.as_manager()
class Book(models.Model):
title = models.CharField(max_length=255)
approved = models.BooleanField()
author = models.ForeignKey(Author)
objects = BookQuerySet.as_manager()
这样,每当更改AuthorQuerySet.for\u site\u q()
方法时,它将自动反映在BookQuerySet.for\u site()
方法中
这里,自定义的QuerySet
类通过组合不同的Q
对象在类级别执行选择,而不是在对象级别使用filter()
或exclude()
方法。拥有Q
对象允许3种不同的使用方式:
filter()
调用中,以就地过滤查询集李>
&(AND)
或|(or)
运算符将其与其他Q
对象组合李>
children
属性,动态更改Q
对象中使用的关键字的名称QuerySet
类中定义的\u qq()
方法负责将指定的相关的\u名称
前置到所有筛选键
如果我们有一个q=q(approved=True)
对象,那么我们可以有以下输出:
self.\u qq(q)
–相当于self.filter(approved=True)
李>
self.\u qq(q,'author')
–相当于self.filter(author\uu approved=True)
此解决方案仍存在严重缺陷:
QuerySet
类李>
filter\u q
(类方法)和filter
(实例方法)李>
更新:动态创建筛选方法可以部分减少缺陷
2.
:
# in class QuerySetRelated
@classmethod
def add_filters(cls, names):
for name in names:
method_q = getattr(cls, '{0:s}_q'.format(name))
def function(self, *args, **kwargs):
return self.filter(method_q(*args, **kwargs))
setattr(cls, name, function)
AuthorQuerySet.add_filters(['for_site'])
BookQuerySet.add_filters(['for_site'])
因此,如果有人提出了更优雅的解决方案,请提出建议。非常感谢。我不确定这是否有帮助,但在将这些查询集连接到相应的模型后,尝试一下这正是我所指的。但我不认为使用该管理器过滤另一个相关模型的实例有什么帮助。也许我遗漏了什么?你没有遗漏什么,我真的认为这是个好问题,我不认为你会从盒子里找到什么。您可以查看并为自己编写一些代码。