【问题标题】:When should I use a custom Manager versus a custom QuerySet in Django?什么时候应该在 Django 中使用自定义管理器而不是自定义查询集?
【发布时间】:2015-06-30 03:27:25
【问题描述】:

在 Django 中,自定义管理器是组织可重用查询逻辑的好方法。 Django documentation on Custom managers 说:

您可能希望自定义Manager 的原因有两个:添加额外的Manager 方法,和/或修改初始QuerySet Manager 返回。

但是,it goes on to describe 也可以创建自定义 QuerySet 类,并且可以使用 QuerySet.as_manager() 直接从数据模型作为管理器访问这些类:

QuerySet.as_manager() 创建的Manager 实例与上一个示例中的PersonManager 几乎相同。

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

【问题讨论】:

    标签: python django orm django-managers


    【解决方案1】:

    主要是为了便于查询组合。通常,如果您希望能够在查询集调用链中对现有查询集执行某些操作,您可以使用QuerySet

    例如,假设您有一个 Image 模型,其中包含 widthheight 字段:

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

    你可以编写一些自定义的QuerySet 方法:

    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)
    

    现在您可以轻松创建动态查询集:

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

    从逻辑上讲,这些函数应该主要关注对查询集模型的现有查询集的分区或重新定义。对于您不在现有查询集上操作的情况,您根本不想返回查询集,或者您可能必须执行一些不涉及此特定模型的相关逻辑,而不是更适合的模型管理器。

    【讨论】:

    • 从 Django 1.7 开始,您只需将 objects 设置为 YourCustomQuerySet.as_manager()。然后你只需要创建查询集类。见docs.djangoproject.com/en/dev/topics/db/managers/…
    • 您是否需要将objects = ImageManager() 添加到Image 模型才能使其工作?
    • @Brodan 很确定,是的
    • 所以主要的用法是Custom QuerySet。以前使用Custom Manager只是因为它在Model/QuerySet的中间?
    【解决方案2】:

    我一直在自学什么是 ManagerQuerySet,所以,我想我最好在这里写,所以下次我想知道时会更容易。

    Manager 是附加到您的模型并返回 QuerySet 实例的类,objects 是默认管理器。大多数管理器方法,例如。 all(), filter() 返回查询集实例。

    更详细地说,当您执行YourModel.objects.filter(..) 时,您将获得一个查询集实例。当您想再次过滤它时,您可以链接另一个 .filter(..) 方法,因为它也可用于 QuerySet 类。这就是你想要的......在管理器和它返回的查询集上都有你的方法。

    如果filter 不是管理器方法,您必须执行YourModel.objects.all() 来获取查询集,然后然后从那里添加filter 方法。

    为了简单起见,Django 在 QuerySet 类上定义了一个 as_manager() 方法,该方法将其变成了一个,嗯..,一个经理 [docs]。因此,您在查询集上定义所有自定义方法,并将其转换为管理器,并附加到您的模型,因此您可以第一次调用它(作为管理器方法)并根据需要多次链接它(如查询集方法)。

    写这个答案时,我想知道 Django 附带的管理器方法是否不是查询集方法,我首先想到的是 get_or_create 方法,因为它似乎不需要查询集。但猜猜怎么了?这也被证明是在 QuerySet 类上定义的。

    长话短说,您几乎总是希望编写 QuerySet 方法,并通过 as_manager() 将它们放在管理器上。

    【讨论】:

    • 您能否提供使用该方法的 Django 应用程序(定义自定义 QuerySet 并将其用作 .as_manager() 的管理器)?我在我的项目依赖代码中找不到任何自定义 QuerySet。相反,我发现很多应用程序都定义了他们的自定义 Manager(例如:allauth.accountallauth.socialaccountdjango.contrib.admindjango.contrib.authregistrationdjango-registration-redux 等)
    • 使用QuerySet.as_manager()时,大部分QuerySet的方法会被复制到Manager。所以这些方法既是QuerySet的方法,也是Manager的方法
    • @hashlash 我认为带有名称的自定义管理器用于区分各种数据使用情况,而自定义查询集是 DRY
    • @hashash 所有这些应用程序可能早在你可以使用QuerySet.as_manager 之前就已经创建了。
    猜你喜欢
    • 1970-01-01
    • 2019-05-26
    • 2016-07-28
    • 2022-10-19
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多