在Django中,可以向querysets添加方法吗?

37 投票
7 回答
20758 浏览
提问于 2025-04-16 09:17

在Django中,如果我有一个模型类,比如说:

from django.db import models

class Transaction(models.Model):
    ...

那么如果我想给这个模型添加一些方法,比如存储一些比较复杂的筛选条件,我可以添加一个自定义的模型管理器,比如:

class TransactionManager(models.Manager):

    def reasonably_complex_filter(self):
        return self.get_query_set().filter(...)


class Transaction(models.Model):
    objects = TransactionManager()

然后我就可以这样做:

>>> Transaction.objects.reasonably_complex_filter()

有没有什么方法可以让我添加一个自定义的方法,这个方法可以接在模型的查询集后面?

也就是说,我想以这样的方式添加自定义方法,这样我就可以这样做:

>>> Transaction.objects.filter(...).reasonably_complex_filter()

7 个回答

16

这是一个完整的解决方案,适用于Django 1.3,感谢Zach Smith和Ben的贡献。

class Entry(models.Model):
    objects = EntryManager() # don't forget this

    is_public = models.BooleanField()
    owner = models.ForeignKey(User)


class EntryManager(models.Manager):
    '''Use this class to define methods just on Entry.objects.'''
    def get_query_set(self):
        return EntryQuerySet(self.model)

    def __getattr__(self, name, *args):
        if name.startswith("_"): 
            raise AttributeError
        return getattr(self.get_query_set(), name, *args) 

    def get_stats(self):
        '''A sample custom Manager method.'''
        return { 'public_count': self.get_query_set().public().count() }


class EntryQuerySet(models.query.QuerySet):
    '''Use this class to define methods on queryset itself.'''
    def public(self):
        return self.filter(is_public=True)

    def by(self, owner):
        return self.filter(owner=owner)


stats = Entry.objects.get_stats()    
my_entries = Entry.objects.by(request.user).public()

注意:在Django 1.6中,get_query_set()这个方法已经不再推荐使用了;在这种情况下,应该使用get_queryset()

46

从Django 1.7开始,新增了一个功能,就是可以用查询集作为管理器

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')),
                                                   ('E', _('Editor'))))
    people = PersonQuerySet.as_manager()

这就产生了以下效果:

Person.people.authors(last_name='Dahl')

另外,还新增了一个功能,可以添加自定义查找

4

你需要给最终得到的 QuerySet 添加一些方法。所以,你需要创建一个 QuerySet 的子类,并在你想要这个功能的地方使用它,这样就能包含你定义的方法。

我找到了一篇教程,里面解释了怎么做以及你可能想这么做的原因:

https://web.archive.org/web/20160329131857/http://adam.gomaa.us/blog/2009/feb/16/subclassing-django-querysets/index.html

撰写回答