自定义查询集和管理器没有中断DRY? [英] Custom QuerySet and Manager without breaking DRY?
问题描述
我试图找到一种实现自定义 QuerySet
和自定义管理器
而不中断DRY的方法。这是我到目前为止:
class MyInquiryManager(models.Manager):
/ pre>
def for_user(self,user )
return self.get_query_set()。filter(
Q(assigned_to_user = user)|
Q(assigned_to_group__in = user.groups.all())
)
类查询(models.Model):
ts = models.DateTimeField(auto_now_add = True)
status = models.ForeignKey(InquiryStatus)
assigned_to_user = models.ForeignKey ,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
。
有没有办法做到这一点,没有重新定义我的所有方法在$ $ $ c> QuerySet 和
管理器
子类?解决方案Django已更改!在使用2009年写的答案之前,请务必查看其余的答案和Django文档,看看是否有更合适的解决方案。
我实现这个的方法是添加实际
get_active_for_account
作为自定义QuerySet
的方法。然后,为了使经理工作,你可以简单地陷入__ getattr __
并相应地返回
这种模式可重用,我已经将
管理器
位提取到单独的模型管理器中:
django.db导入模型
从django.db.models
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ .query import QuerySet
class CustomQuerySetManager(models.Manager):
可重用的Manager访问自定义QuerySet
def __getattr __(self,attr ,* args):
try:
return getattr(self .__ class__,attr,* args)
除了AttributeError:
#不要将内部方法委派给queryset
如果attr.startswith('__')和attr.endswith('__'):
raise
return getattr(self.get_query_set(),attr,* args)
def get_query_set(self):
return self.model.Qu erySet(self.model,using = self._db)
一旦你有了,在你的您需要做的所有模型是将 QuerySet
定义为自定义内部类,并将管理器设置为自定义管理器:
your_app / models.py
from custom_queryset.models import CustomQuerySetManager
from django .db.models.query import QuerySet
类查询(models.Model):
objects = CustomQuerySetManager()
class QuerySet(QuerySet):
def active_for_account(self,account,* args,** kwargs):
return self.filter(account = account,deleted = False,* args,** kwargs)
使用这种模式,任何一种都可以正常工作:
>>> Inquiry.objects.active_for_account(user)
>>> Inquiry.objects.all()。active_for_account(user)
>>> Inquiry.objects.filter(first_name ='John')。active_for_account(user)
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?
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 custom QuerySet
. Then, to make it work off the manager, you can simply trap the __getattr__
and return it accordingly
To make this pattern re-usable, I've extracted out the Manager
bits to a separate model manager:
custom_queryset/models.py
from django.db import models
from django.db.models.query import QuerySet
class CustomQuerySetManager(models.Manager):
"""A re-usable Manager to access a custom QuerySet"""
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
# don't delegate internal methods to the queryset
if attr.startswith('__') and attr.endswith('__'):
raise
return getattr(self.get_query_set(), attr, *args)
def get_query_set(self):
return self.model.QuerySet(self.model, using=self._db)
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
from custom_queryset.models import CustomQuerySetManager
from django.db.models.query import QuerySet
class Inquiry(models.Model):
objects = CustomQuerySetManager()
class QuerySet(QuerySet):
def active_for_account(self, account, *args, **kwargs):
return self.filter(account=account, deleted=False, *args, **kwargs)
With this pattern, any of these will work:
>>> Inquiry.objects.active_for_account(user)
>>> Inquiry.objects.all().active_for_account(user)
>>> Inquiry.objects.filter(first_name='John').active_for_account(user)
这篇关于自定义查询集和管理器没有中断DRY?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!