在 Django 中,什么时候应该使用自定义管理器,什么时候应该使用自定义 QuerySet?

在 Django 中,自定义管理器是组织可重用查询逻辑的一个很好的方法:

您可能需要定制 Manager有两个原因: 添加额外的 Manager方法,和/或修改 Manager返回的初始 QuerySet

但是,它继续描述也可以创建定制的 QuerySet类,而且这些类可以作为管理器使用 QuerySet.as_manager()从数据模型直接访问:

QuerySet.as_manager()创建的 Manager实例实际上与前面示例中的 PersonManager完全相同。

在如何组织定制 Manager和/或定制 QuerySet类之间的逻辑方面,似乎有很大的灵活性。我应该根据什么原则来决定何时使用一种方法和另一种方法?

15727 次浏览

Mainly to allow for easy composition of queries. Generally if you want to be able perform some operation on an existing queryset in a chain of queryset calls you can use a QuerySet.

For example, say you have an Image model that has a width, height fields:

class Image(models.Model):
width = ...  # Width in pixels
height = ... # Height in pixels

you could write some custom QuerySet methods:

class ImageQuerySet(models.QuerySet):
def landscapes(self):
return self.filter(width__gte=models.F('height'))


def portraits(self):
return self.filter(width__lte=models.F('height'))


def small(self):
return self.filter(width__lte=1200)


def large(self):
return self.filter(width__gte=1200)


class ImageManager(models.Manager):
def get_queryset(self):
return ImageQuerySet(self.model, using=self._db)

now you can easily create dynamic querysets:

Image.objects.all().portraits().small()
Image.objects.all().large().portraits()

Logically, these functions should be concerned primarily with partitioning or redefining of existing querysets of the queryset's model. For situations where you aren't operating on existing querysets, you don't want to return a queryset at all, or you might have to perform some related logic that doesn't involve this particular model, than a model manager it better suited.

I kept reteaching myself what is a Manager vs a QuerySet so, I thought I better write here, so make it easier next time I wonder.

A Manager is the class that is attached to your model and returns a QuerySet instance, objects being the default manager. Most manager methods, ex. all(), filter() return queryset instances.

In more detail, when you do YourModel.objects.filter(..) you get a queryset instance. When you want to filter it again, you can chain another .filter(..) method only because it is also available on the QuerySet class too. That is what you want.. have your methods on both the manager and the queryset it returns.

If filter was not also a manager method, you would have to do YourModel.objects.all() to get the queryset, and then add the filter method(s) from there.

To make things easy, Django defines a as_manager() method on the QuerySet class which turns it into a, well.., a manager [docs]. Therefore, you define all your custom methods on your queryset, and turn it into a manager, and attach to your model, so you can call it the first time (as a manager method) and chain it as many times as you want (as queryset methods).

Writing this answer, I wondered if there are any manager methods shipped with Django that aren't queryset methods, and the first that came to my mind was the get_or_create method since it does not seem to need a queryset. But guess what? that also turned out to be defined on the QuerySet class.

Long story short, you almost always want to write QuerySet methods and have them on the manager too via the as_manager().