自定义查询集和管理器而不破坏 DRY?
我正在尝试找到一种方法来实现自定义 QuerySet
和自定义 Manager
而不破坏 DRY。这就是我到目前为止所拥有的:
class MyInquiryManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(
Q(assigned_to_user=user) |
Q(assigned_to_group__in=user.groups.all())
)
class Inquiry(models.Model):
ts = models.DateTimeField(auto_now_add=True)
status = models.ForeignKey(InquiryStatus)
assigned_to_user = models.ForeignKey(User, blank=True, null=True)
assigned_to_group = models.ForeignKey(Group, blank=True, null=True)
objects = MyInquiryManager()
这工作得很好,直到我做了这样的事情:
inquiries = Inquiry.objects.filter(status=some_status)
my_inquiry_count = inquiries.for_user(request.user).count()
这会立即破坏一切,因为 QuerySet
没有与 Manager
相同的方法。我尝试创建一个自定义 QuerySet
类,并在 MyInquiryManager
中实现它,但最终复制了所有方法定义。
我还发现 这个片段 有效,但我需要将额外的参数传递给for_user
,因此它会崩溃,因为它严重依赖于重新定义get_query_set
。
有没有一种方法可以做到这一点,而无需重新定义 QuerySet 和 Manager 子类中的所有方法?
I'm trying to find a way to implement both a custom QuerySet
and a custom Manager
without breaking DRY. This is what I have so far:
class MyInquiryManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(
Q(assigned_to_user=user) |
Q(assigned_to_group__in=user.groups.all())
)
class Inquiry(models.Model):
ts = models.DateTimeField(auto_now_add=True)
status = models.ForeignKey(InquiryStatus)
assigned_to_user = models.ForeignKey(User, blank=True, null=True)
assigned_to_group = models.ForeignKey(Group, blank=True, null=True)
objects = MyInquiryManager()
This works fine, until I do something like this:
inquiries = Inquiry.objects.filter(status=some_status)
my_inquiry_count = inquiries.for_user(request.user).count()
This promptly breaks everything because the QuerySet
doesn't have the same methods as the Manager
. I've tried creating a custom QuerySet
class, and implementing it in MyInquiryManager
, but I end up replicating all of my method definitions.
I also found this snippet which works, but I need to pass in the extra argument to for_user
so it breaks down because it relies heavily on redefining get_query_set
.
Is there a way to do this without redefining all of my methods in both the QuerySet
and the Manager
subclasses?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
Django 1.7 发布了一种新且简单的方法来创建组合查询集和模型管理器:
请参阅使用 QuerySet 方法创建管理器了解更多详细信息。
The Django 1.7 released a new and simple way to create combined queryset and model manager:
See Creating Manager with QuerySet methods for more details.
Django 已更改! 在使用此答案(写于 2009 年)中的代码之前,请务必查看其余答案和 Django 文档,看看是否有更合适的解决方案。
我实现这一点的方法是添加实际的
get_active_for_account
作为自定义QuerySet
的方法。然后,为了使其在管理器之外工作,您可以简单地捕获 __getattr__ 并相应地返回它。为了使该模式可重用,我提取了 Manager 位到单独的模型管理器:
custom_queryset/models.py
一旦你得到了它,在你的模型上你需要做的就是定义一个
QuerySet
作为自定义内部类和将管理器设置为您的自定义管理器:your_app/models.py
使用此模式,以下任何一个都可以工作:
UPD 如果您将其与自定义用户 (
AbstractUser
) 一起使用,你需要改变从
到
Django has changed! Before using the code in this answer, which was written in 2009, be sure to check out the rest of the answers and the Django documentation to see if there is a more appropriate solution.
The way I've implemented this is by adding the actual
get_active_for_account
as a method of a customQuerySet
. Then, to make it work off the manager, you can simply trap the__getattr__
and return it accordinglyTo make this pattern re-usable, I've extracted out the
Manager
bits to a separate model manager:custom_queryset/models.py
Once you've got that, on your models all you need to do is define a
QuerySet
as a custom inner class and set the manager to your custom manager:your_app/models.py
With this pattern, any of these will work:
UPD if you are using it with custom user(
AbstractUser
), you need to changefrom
to
您可以使用 mixin 在管理器和查询集上提供方法。
这也避免了使用
__getattr__()
方法。You can provide the methods on the manager and queryset using a mixin.
This also avoids the use of a
__getattr__()
approach.您现在可以使用 from_queryset() 方法您的经理更改其基本查询集。
定义一次查询集方法和管理器方法
这允许您从文档中
You can now use the from_queryset() method on you manager to change its base Queryset.
This allows you to define your Queryset methods and your manager methods only once
from the docs
基于
django 3.1.3
源代码,我找到了一个简单的解决方案based on
django 3.1.3
source code, i found a simple solutionT. Stone 方法的略微改进版本:
类装饰器使使用变得简单如下:
更新:支持非标准 Manager 和 QuerySet 基类,例如 @objects_extra(django.contrib.gis.db.models.GeoManager, django.contrib.gis .db.models.query.GeoQuerySet):
A slightly improved version of T. Stone’s approach:
Class decorators make usage as simple as:
Update: support for nonstandard Manager and QuerySet base classes, e. g. @objects_extra(django.contrib.gis.db.models.GeoManager, django.contrib.gis.db.models.query.GeoQuerySet):
在某些用例中,我们需要 从管理器调用自定义 QuerySet 方法,而不是使用 QuerySet 的
get_manager
方法。根据已接受的解决方案评论之一中发布的解决方案,混合就足够了。
例如,
通过上面的内容,我们可以像下面一样访问相关对象(Book),而无需在管理器中为每个查询集方法定义新方法。
There are use-cases where we need to call custom QuerySet methods from the manager instead of using the
get_manager
method of a QuerySet.A mixin would suffice based on the solution posted in one of the accepted solution comments.
For example,
With the above, we can access related objects (Book) like below without defining new methods in the manager for each queryset method.
以下内容对我有用。
这是默认管理器上的;所以我曾经做过类似的事情:
但是没有理由它不适合二级经理。
The following works for me.
This is on the default manager; so I used to do something like:
But there is no reason it should not work for a secondary manager.