在为Django模块创建前端时,我在Django核心内遇到了以下问题:
为了从模型查询显示指向下一个/上一个对象的链接,我们可以使用模型实例的extra-instance-methods: get_next_by_FIELD()或 get_previous_by_FIELD( )。其中FIELD是DateField或DateTimeField类型的模型字段。
from django.db import models
class Shoe(models.Model):
created = models.DateTimeField(auto_now_add=True, null=False)
size = models.IntegerField()
显示鞋子列表的视图,不包括大小等于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参数来过滤掉不需要的鞋子,如文档中所述:
这两种方法都将使用模型的默认管理器执行查询。如果您需要模拟自定义管理器使用的过滤,或者想要执行一次性自定义过滤,则两种方法也都接受 可选的关键字参数,应采用字段查找中描述的格式。
编辑:请密切关注“应该”这个词,因为那时(size_ne = 4)也应该有效,但事实并非如此。
使用lookup size__ne过滤...
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)
...
...无法正常工作,它会抛出 FieldError :无法将关键字'size_ne'解析为字段。
然后我尝试使用Q对象的否定complex lookup:
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))
...
...也不起作用,抛出 TypeError : _get_next_or_previous_by_FIELD()获得参数'field'的多个值
因为 get_(previous | next)_by_created 方法只接受** kwargs。
由于这些实例方法使用_get_next_or_previous_by_FIELD(self, field, is_next, **kwargs),我将其更改为使用* 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__ne = 4)按预期工作,还是应该向Django人报告此问题,希望他们能接受我的 _get_next_or_previous_by_FIELD()修复?
环境:Django 1.7,尚未在1.9上测试过,但 _get_next_or_previous_by_FIELD()的代码保持不变。
编辑:确实,使用Q对象的复杂查找不是“字段查找”的一部分,而是更多的filter()和exclude()函数的一部分。当我想get_next_by_FIELD也应该接受Q对象时,我可能错了。但由于所涉及的变化很小并且使用Q对象的优势很高,我认为这些变化应该上游。
标签:django,complex-lookup,query,get_next_by_FIELD,get_previous_by_FIELD
(在此列出标签,因为我没有足够的声誉。)
答案 0 :(得分:1)
您可以创建custom lookup ne
并使用它:
.get_next_by_created(size__ne=4)
答案 1 :(得分:0)
我怀疑你先尝试过的方法只对你所基于get_next的字段进行查询arg。这意味着您将无法从get_next_by_created()方法访问size字段,例如。
编辑:您的方法效率更高,但要回答关于Django问题的问题,我认为一切都按照预期的方式运行。您可以提供其他方法,例如您的方法,但现有的get_next_by_FIELD正如文档中所述那样工作。
你已经设法使用一种工作方法解决这个问题,我想这是可以的,但是如果你想减少开销,你可以尝试一个简单的循环:
show_admin_bar(false);
这不是很有效,但它是你想要的一种方式。
希望这有帮助!
此致