2013-01-02 39 views
2

我希望用戶能夠通過幾個不同的參數(名稱,年份等)查詢我的數據庫,動態添加更多的字段,並用布爾運算符將它們連接起來;最終的結果就像「年= 1900,名稱=雞,地點=舊金山」。我認爲我做錯了什麼,因爲它沒有返回任何東西,即使我只用一個我知道匹配一些數據的值來嘗試一個字段(例如,當我使用Django shell中的.filter()時,我可以返回對象)。任何人都知道我可以修復它嗎?動態連接Django Q對象與AND和OR

相關視圖(忽略馬虎壓痕,我不想經歷和修復所有的,但它是正確的,我實際的代碼):

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'\d{4}') 
    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://stackoverflow.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 %} 
+0

'lambda x,y:x&y'是'operator.and_'。 –

回答

3

對不起,我原來的帖子超過了你的需要。爲了澄清,都應該需要做的是:

Q(year__icontains=year_input_variable) | Q(city__icontains=city_input_variable) & Q(name__icontains=name_input_variable) 

使用&並,|爲或。

我之前發佈的是如果一個查詢包含多個單詞,它會檢查是否所有的單詞匹配使用運算符。 或使用運算符匹配的任何單詞。

+0

我喜歡它,但我希望能夠做的是顯示結果,例如匹配城市和年份或名稱,這對於您的解決方案來說是不可能的,我不這麼認爲。 – swizzard

+0

使用&與|相對https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects – GetItDone