Django使用复杂的Q查找按字段获取下一个字段
在为Django模块创建前端时,我在Django内核中遇到了以下问题: 为了显示从模型查询到下一个/上一个对象的链接,我们可以使用模型实例的:get_next_by_FIELD()或get_previous_by_FIELD()。其中字段是DateField或DateTimeField类型的模型字段 让我们用一个例子来解释它 显示鞋的列表的视图,不包括尺寸等于4的鞋:Django使用复杂的Q查找按字段获取下一个字段,django,django-views,django-queryset,django-q,Django,Django Views,Django Queryset,Django Q,在为Django模块创建前端时,我在Django内核中遇到了以下问题: 为了显示从模型查询到下一个/上一个对象的链接,我们可以使用模型实例的:get_next_by_FIELD()或get_previous_by_FIELD()。其中字段是DateField或DateTimeField类型的模型字段 让我们用一个例子来解释它 显示鞋的列表的视图,不包括尺寸等于4的鞋: def list_shoes(request): shoes = Shoe.objects.exclude(size=4
def list_shoes(request):
shoes = Shoe.objects.exclude(size=4)
return render_to_response(request, {
'shoes': shoes
})
让下面的视图显示一只鞋和相应的
链接到上一双鞋和下一双鞋
def show_shoe(request, shoe_id):
shoe = Shoe.objects.get(pk=shoe_id)
prev_shoe = shoe.get_previous_by_created()
next_shoe = shoe.get_next_by_created()
return render_to_response('show_shoe.html', {
'shoe': shoe,
'prev_shoe': prev_shoe,
'next_shoe': next_shoe
})
现在我遇到了这样的情况:show_shoe视图显示指向上一个/下一个的链接,而不考虑鞋的尺寸。但事实上我只想要尺码不是4的鞋子。
因此,我尝试使用get_(previous | next)_by_created()方法的**kwargs参数来过滤掉不需要的鞋子,如文档所述:
这两种方法都将使用模型的默认管理器执行查询。如果您需要模拟自定义管理器使用的筛选,或者希望执行一次性自定义筛选,这两种方法都可以接受
可选关键字参数,应采用字段查找中描述的格式
编辑:注意“应该”这个词,因为这样(大小=4)也应该起作用,但它不起作用
实际问题
正在使用查找大小进行筛选
def show_shoe(request, shoe_id):
...
prev_shoe = shoe.get_previous_by_created(size__ne=4)
next_shoe = shoe.get_next_by_created(size__ne=4)
...
。。。不起作用,它抛出字段错误:无法将关键字“size\u ne”解析到字段中
然后我尝试使用一个使用Q对象的否定:
from django.db.models import Q
def show_shoe(request, shoe_id):
...
prev_shoe = shoe.get_previous_by_created(~Q(size=4))
next_shoe = shoe.get_next_by_created(~Q(size=4))
...
。。。也不起作用,抛出类型错误:_get_next_或_previous_by_FIELD()为参数“FIELD”获取了多个值
因为get_u(previous | next)by_创建的方法只接受**kwargs
def my_get_next_or_previous_by_FIELD(self, field, is_next, *args, **kwargs):
"""
Workaround to call get_next_or_previous_by_FIELD by using complext lookup queries using
Djangos Q Class. The only difference between this version and original version is that
positional arguments are also passed to the filter function.
"""
if not self.pk:
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
op = 'gt' if is_next else 'lt'
order = '' if is_next else '-'
param = force_text(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param})
q = q | Q(**{field.name: param, 'pk__%s' % op: self.pk})
qs = self.__class__._default_manager.using(self._state.db).filter(*args, **kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
try:
return qs[0]
except IndexError:
raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name)
实际解决方案
由于这些实例方法使用了,我将其更改为使用*args接受位置参数,并将它们传递给过滤器,如**kwargs
def my_get_next_or_previous_by_FIELD(self, field, is_next, *args, **kwargs):
"""
Workaround to call get_next_or_previous_by_FIELD by using complext lookup queries using
Djangos Q Class. The only difference between this version and original version is that
positional arguments are also passed to the filter function.
"""
if not self.pk:
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
op = 'gt' if is_next else 'lt'
order = '' if is_next else '-'
param = force_text(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param})
q = q | Q(**{field.name: param, 'pk__%s' % op: self.pk})
qs = self.__class__._default_manager.using(self._state.db).filter(*args, **kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
try:
return qs[0]
except IndexError:
raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name)
并称之为:
...
prev_shoe = shoe.my_get_next_or_previous_by_FIELD(Shoe._meta.get_field('created'), False, ~Q(state=4))
next_shoe = shoe.my_get_next_or_previous_by_FIELD(Shoe._meta.get_field('created'), True, ~Q(state=4))
...
终于做到了
现在我想问你一个问题
有没有更简单的方法来处理这个问题?是否应该让shoe.get_previous_by_created(size_une=4)按预期工作,或者我应该将此问题报告给Django的同事,希望他们接受我的_get_next_或_previous_by_FIELD()修复
环境:Django 1.7,尚未在1.9上对其进行测试,但_get_next_或_previous_by_FIELD()的代码保持不变
编辑:确实,使用Q对象的复杂查找不是“字段查找”的一部分,而是filter()和exclude()函数的一部分。当我认为get_next_by_字段也应该接受Q对象时,我可能是错的。但由于所涉及的更改非常少,并且使用Q对象的优势很高,所以我认为这些更改应该向上游进行
标记:django、复杂查找、查询、按字段获取下一个、按字段获取上一个
(在这里列出标记,因为我没有足够的声誉。)我怀疑您首先尝试的方法只对您下一个get_所基于的字段使用lookup arg。例如,这意味着您将无法从get_next_by_created()方法访问size字段 编辑:你的方法要有效得多,但要回答你关于Django问题的问题,我认为一切都按预期的方式进行。您可以提供其他方法,如您的方法,但现有的get_next_by_字段正在按照文档中所述工作 您已经用一种工作方法解决了这个问题,我想这是可以的,但是如果您想减少开销,您可以尝试一个简单的循环:
def get_next_by_field_filtered(obj, field=None, **kwargs):
next_obj = getattr(obj, 'get_next_by_{}'.format(field))()
for key in kwargs:
if not getattr(next_obj, str(key)) == kwargs[str(key)]:
return get_next_by_field_filtered(next_obj, field=field, **kwargs)
return next_obj
这不是很有效,但这是一种你想做的事情
希望这有帮助
关于,您可以创建并使用它:
.get_next_by_created(size__ne=4)
因为这是我的第一个问题,有什么改进的建议吗?是的,循环也可以,但我不太喜欢,但你的解决方案看起来更好。而且我不确定循环的计算/db开销是否比使用修改的my_get_next_或_previous_by_字段低,因为每次调用get_next_by_字段都需要访问数据库。这就是为什么我想使用size_ne或Q对象来解决这个问题,这样只需一次DB访问就可以获得所需的结果。是的,你完全正确,你的方法非常有效:)创建一个自己的查找工作!但为什么不能使用开箱即用的尺寸?因为它“应该”的行为类似于其他字段查找。因为默认情况下没有
ne
查找。