在已过滤的查询集上使用 Django 自定义管理器功能

Posted

技术标签:

【中文标题】在已过滤的查询集上使用 Django 自定义管理器功能【英文标题】:Using Django custom manager function on an already filtered queryset 【发布时间】:2011-08-14 13:05:04 【问题描述】:

考虑以下情况:

class MyModelManager(models.Manager):
    def my_filter(self):
     return [some code here].filter(field_A__gt=3)

class MyModel(models.Model):
    # some fields
    objects = MyModelManager()


# The way I'd like to use it:
qs = MyModel.objects.filter(field_B__lt=1)
# some code
qs = qs.my_filter()

请注意,我想在已过滤的查询集上使用自定义 my_filter() 函数。我应该在上面的[some code here] 中放置什么代码?整体结构是否正确?

【问题讨论】:

另见问题 #2163151:***.com/questions/2163151/… 【参考方案1】:
class MyModelManager(models.Manager):
    def my_filter(self, qs=None):
        if qs is None:
            qs = [however you got it before]
        return qs.filter(field_A__gt=3)

然后将您的查询集传递给它。

【讨论】:

这不是一种解决方法吗?代码将类似于: qs = MyModel.objects.my_filter(qs) 这比我预期的要少 djangoic 您还希望如何获得查询集? filter() 是一个QuerySet 方法。 那么我需要重写QuerySet并添加my_function,而不是添加到管理器吗? 如果你希望它是一个QuerySet 方法,当然。【参考方案2】:

我想没有办法做到这一点。 MyModel.objects.filter(field_B__lt=1) 将返回 QuerySet 对象,而不是 MyModelManager。 您可以像 Ignacio Vazquez-Abrams 提到的那样将它传递给 my_filter 方法,或者首先将 my_filter 应用到您的经理,这将返回 QuerySet 对象,您可以进一步过滤

【讨论】:

MyModel.objects.filter(...).filter(...) 怎么会起作用? 所以Manager和QuerySet都有filter()? 来自django源码Manager类方法:def get_query_set(self): """返回一个新的QuerySet对象。子类可以重写这个方法来轻松自定义Manager的行为。""" return QuerySet( self.model, using=self._db) def filter(self, *args, **kwargs): return self.get_query_set().filter(*args, **kwargs) 所以Manager创建新的QuerySet并对其应用过滤器 【参考方案3】:

您的方法是错误的,因为您只能在从数据库中检索数据时使用 Model.Manager,因此使用两个管理器或尝试在查询集上使用一个管理器会导致错误。

data = SomeModel.objects.my_manager.all()

data = SomeModel.objects.all()
data = data.my_manager.filter(...)

这是错误的,因为您不能使用两个管理器...

一种可能的方法是定义一个函数,该函数将查询集作为参数并返回过滤后的查询集...

def extra_filter(queryset):
    queryset = queryset.filter(...)

但我不确定这是否有帮助。

最好的方法是定义另一个模型管理器,并在需要时将其用于对象...

class SomeManager(models.Manager):
    def get_query_set(self):
        return super(SomeManager, self).get_query_set().filter(<filteer criteria>)

class MyModel(models.Model):
    # some fields
    objects = models.Manager()
    mymanager = SomeManager()

data = MyModel.mymanager.all() 

只用一个...

更新:在 django 中,查询集是惰性的。这意味着,您可以应用任意数量的过滤器,除非您尝试从过滤器中获取特定记录或尝试对其进行切片,否则不会从数据库中检索数据...Documentation is here

所以,没有区别

qs = MyModel.objects.filter(field_B__lt=1)
qs = qs.filter(field_A__gt=3)

qs = MyModel.objects.filter(field_A__gt=3)
qs = qs.filter(field_B__lt=1)

所以定义一个经理来应用一个特定的过滤是使用经理的主要原因...

你定义你的经理:

class SomeManager(models.Manager):
    def get_query_set(self):
        return super(SomeManager, self).get_query_set().filter(field_A__gt=3)

然后你用其他过滤条件调用它:

Somemodel.mymodelmanager.filter(field_B__lt=1, ....)

django 将添加所有过滤器并在您希望查询时评估查询。

所以如果是你经常使用的过滤器,manager i 是最好的选择……

举个例子,我的大多数数据库模型中都有 valid_ 字段和一个过滤无效条目的 val 管理器。因此,当我希望过滤所有有效数据(90% 的时间)时,我使用

Somemodel.val.filter(...)

在我需要所有数据的情况下,我只使用基本的 django 管理器:

Somemodel.objects.filter(...)

【讨论】:

我正在尝试在已经定期过滤的查询集上使用自定义过滤功能,不知道定义另一个经理将如何实现这一目标

以上是关于在已过滤的查询集上使用 Django 自定义管理器功能的主要内容,如果未能解决你的问题,请参考以下文章

Django:根据自定义函数过滤查询

在 Django 查询集上使用 iterator()

Django 管理员添加自定义过滤器

Django:在过滤器和搜索中使用 JSONField 属性

在 Django admin 中按自定义日期范围过滤

具有过滤查询集的组权限的自定义表单