使用 AND 和 OR 动态连接 Django Q 对象
Posted
技术标签:
【中文标题】使用 AND 和 OR 动态连接 Django Q 对象【英文标题】:dynamically connecting Django Q objects with AND and OR 【发布时间】:2012-12-16 13:43:07 【问题描述】:我希望用户能够通过几个不同的参数(姓名、年份等)查询我的数据库,动态添加更多字段,并使用布尔运算符将它们连接起来;最终结果将类似于“年份 = 1900 AND 名称 = 鸡 AND 位置 = 旧金山”。我认为我做错了什么,因为它没有返回任何内容,即使我只尝试一个我知道与某些数据匹配的值的字段(例如,当我从 Django shell 使用 .filter() 时,我可以取回对象)。有人知道我该如何解决吗?
相关视图(忽略草率的缩进,我不想通过并修复所有它,但它在我的实际代码中是正确的):
class BaseSearchFormSet(BaseFormSet):
def clean(self):
if any(self.errors):
return self.errors
queries = []
valid_courses = ["appetizer","main","dessert"]
valid_period = re.compile(r'\d\d\d0-\d\d\d5|\d\d\d5-\d\d\d0')
valid_year = re.compile(r'\d4')
multi_rows = ["year","period","course"]
for x in xrange(0,self.total_form_count()):
form = self.forms[x]
query = form.cleaned_data.get("query")
row = form.cleaned_data.get("row")
if query in queries and row not in multi_rows:
raise forms.ValidationError("You're already searching for %s.")
queries.append(query)
if row == "course" and query.lower() not in valid_courses:
raise forms.ValidationError("%s is not a valid course option."%(form.cleaned_data["query"]))
if row == "period" and not re.match(valid_period,query):
raise forms.ValidationError("%s is not a properly formatted period. Valid five-year periods span either the first or second half of a decade. For example: 1910-1915, 1925-1930."%(form.cleaned_data["query"]))
if row == "year" and not re.match(valid_year,query):
raise forms.ValidationError("Please enter a four-digit year.")
def search(request):
errors = []
searchFormSet = formset_factory(F.SearchForm, extra=1,formset=BaseSearchFormSet)
if request.GET:
formset = searchFormSet(request.GET)
forms = []
if formset.is_valid():
for x in xrange(0,formset.total_form_count()):
form =
form["row"]= formset[x].cleaned_data.get("row",None)
form["query"] = formset[x].cleaned_data.get("query",None)
form["bools"] = formset[x].cleaned_data.get("bools",None)
if form["query"]:
q = form["query"]
else:
errors.append("no query found")
if form["row"]:
row = form["row"]
else:
errors.append("no row found")
filter_keys = "dish_name":Q(dish__name__icontains=q),
"regex":Q(dish__full_name__regex=r'%s'%(q)),
"course":Q(dish__classification=q.lower()),
"year":Q(page__menu_id__year__exact=q),
"period":Q(page__menu_id__period__exact=q),
"location":Q(page__menu_id__location__icontains=q),
"restaurant":Q(page__menu_id__restaurant__icontains=q)
forms.append(form)
final_query=Q()
def var_reduce(op,slice):
if op == "and":
return reduce(lambda x,y: x & y,slice)
elif op == "or":
return reduce(lambda x,y: x | y,slice)
for x in xrange(len(forms)):
try:
try:
if final_query:
slice = [final_query,filter_keys[forms[x]["row"]],filter_keys[forms[x+1]["row"]]]
else:
slice = [filter_keys[forms[x]["row"]],filter_keys[forms[x+1]["row"]]]
final_query = var_reduce(forms[x]["bools"],slice)
except IndexError:
if final_query:
slice = [final_query,filter_keys[forms[x]["row"]]]
else:
slice = [filter_keys[forms[x]["row"]]]
final_query = var_reduce(forms[x]["bools"],slice)
items = MenuItem.objects.filter(final_query)
return render_to_response("search_results.html","items":items,"formset":formset)
except KeyError as e:
errors.append(e)
formset = searchFormSet(None)
return render_to_response("search_page.html","errors":errors,"formset":formset)
else:
formset = searchFormSet(None)
return render_to_response("search_page.html","errors":errors,"formset":formset)
else:
formset = searchFormSet(None)
return render_to_response("search_page.html","formset":formset)
型号:
from django.db import models
class MenuItem(models.Model):
def format_price(self):
return "$0:0<4,.2f".format(float(self.price))
def __unicode__(self):
return self.dish
dish=models.OneToOneField('Dish',to_field='mk')
price=models.CharField(max_length=5,blank=True)
page=models.OneToOneField('MenuPage')
mk=models.CharField(max_length=10,unique=True)
formatted_price = property(format_price)
class Menu(models.Model):
def period(self):#adapted from http://***.com/questions/2272149/round-to-5or-other-number-in-python
try:
p=int(10*round(float(int(self.year))/10))
if p < self.year:
return "%s-%s"%(p,p+5)
else:
return "%s-%s"%(p-5,p)
except (ValueError,TypeError):
return ""
def __unicode__(self):
if self.restaurant:
return self.restaurant
else:
return self.mk
restaurant=models.TextField(unique=False,blank=True,null=True)
year=models.CharField(max_length=4,unique=False,blank=True,null=True)
location=models.TextField(unique=False,blank=True,null=True)
status=models.CharField(unique=False,max_length=20)
mk=models.CharField(max_length=8,unique=True,primary_key=True)
period=property(period)
language = models.CharField(unique=False,max_length=30)
#objects=MenuManager()
class MenuPage(models.Model):
mk=models.CharField(max_length=10,unique=True)
menu_id=models.OneToOneField("Menu",to_field='mk')
#objects=MenuPageManager()
class Dish(models.Model):
def __unicode__(self):
return self.name
full_name = models.TextField()
name=models.CharField(unique=True,max_length=255)
mk=models.CharField(max_length=10,unique=True)
class Classification(models.Model):
def __unicode__(self):
if self.classification:
return self.classification
else:
return "none"
dish=models.OneToOneField('dish',to_field='name')
classification=models.CharField(unique=False,max_length=9)
mk=models.CharField(max_length=10,primary_key=True)
我的搜索页面的html:
% extends "base.html" %
% block style %
<link rel="stylesheet" type="text/css" href="/static/search_style.css" />
% endblock %
% block java %
<script type="text/javascript" src="/static/searches.js"></script>
% endblock %
% block title %Search% endblock %
% block head %Search% endblock %
% block content %
% autoescape off %
<div id="searches">
<form id="search" action="" method="get">
<table border="0" cellpadding="0" cellspace="0">
<tbody class="search">
% for form in formset.forms %
<tr>
<td class="row"> form.row </td>
<td class="query"> form.query </td>
<td class="bool"> form.bools </td>
</tr>
% endfor %
</tbody>
</table>
formset.management_form
<input type="submit" value="Submit" id="submit">
</form>
</div>
% if formset.errors or errors %
<div id="errors">
<h3>The following errors were encountered while trying to submit your search:</h3>
% for x,y in formset.errors.items %
<p> x : y </p>
% endfor %
errors
</div>
% endif %
<div id="notes">
<p>Searching by dish names, locations, and restaurants is case-insensitive.</p>
<p>Searching by course uses case-insensitive exact matching. Valid courses are Appetizer, Main, and Dessert.</p>
<p>Years should be entered YYYY. Five-year periods span either the first or second half of a decade, and should be entered YYYY-YYYY. Example valid five-year periods are 1900-1905, 1995-2000, etc.</p>
<p>Regular expression search follows mysql regular expression syntax, as described <a href="http://dev.mysql.com/doc/refman/5.1/en/regexp.html" target="_blank">here</a>.</p>
</div>
% endautoescape %
<br /><br /><br /><br /> <br /><br /><br />
% endblock %
% block footer %
<div id="warning">
<p>NOTE: This site and the information it contains are still in development. Some information may be missing or inaccurate.</p>
</div>
<div class="credits">
Created and maintained by <a href="/about#sam">Sam Raker</a> and <a href="/about#rachel">Rachel Rakov</a>
<br />
Data graciously provided by <a href="http://menus.nypl.org" target="_blank">What's on the Menu?</a>
</div>
% endblock %
【问题讨论】:
lambda x,y: x & y
是operator.and_
。
【参考方案1】:
对不起,我原来的帖子超出了您的需要。澄清一下,您需要做的就是:
Q(year__icontains=year_input_variable) | Q(city__icontains=city_input_variable) & Q(name__icontains=name_input_variable)
将 & 用于和,|或。
我之前发布的内容是,如果查询包含多个单词,它会检查是否所有单词都使用 operator.and 匹配,或者是否有任何单词使用 operator 匹配。 或。
【讨论】:
我喜欢它,但我想做的是显示结果,例如,匹配城市和年份或名称,这在你的解决方案中是不可能的,我不'不思考。 使用 & 对比 | docs.djangoproject.com/en/dev/topics/db/queries/…以上是关于使用 AND 和 OR 动态连接 Django Q 对象的主要内容,如果未能解决你的问题,请参考以下文章
在 Django 中使用 Q() 动态构建复杂查询 [关闭]
Django SQL OR via filter() & Q(): 动态?