Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/powerbi/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django中相关模型的自定义查询集筛选_Django_Django Queryset - Fatal编程技术网

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'])
    


    因此,如果有人提出了更优雅的解决方案,请提出建议。非常感谢。

    我不确定这是否有帮助,但在将这些查询集连接到相应的模型后,尝试一下这正是我所指的。但我不认为使用该管理器过滤另一个相关模型的实例有什么帮助。也许我遗漏了什么?你没有遗漏什么,我真的认为这是个好问题,我不认为你会从盒子里找到什么。您可以查看并为自己编写一些代码。