基于Django实现单页餐厅数据库下拉查询功能求助
Hey there! Let's get that single-page restaurant search sorted out for you. Here's a step-by-step approach to add those dropdown filters right in restaurants.html without needing a separate page:
1. Create a Search Form
First, add a new form class to your forms.py (you already have ReviewForm, so just append this):
from django import forms from .models import Restaurant class RestaurantSearchForm(forms.Form): # Reuse the choices from your Restaurant model for food and city food = forms.ChoiceField(choices=Restaurant.FOOD, required=False, label="Food Type") city = forms.ChoiceField(choices=Restaurant.CITY, required=False, label="City") # Add a dropdown for minimum average rating (since it's a calculated value) average_rating = forms.ChoiceField( choices=[('', 'Any'), (1, '1 Star & Up'), (2, '2 Stars & Up'), (3, '3 Stars & Up'), (4, '4 Stars & Up')], required=False, label="Minimum Average Rating" )
2. Update the restaurant_list View
Modify your views.py to handle form submissions and filter the restaurant results:
from .forms import ReviewForm, RestaurantSearchForm # Import the new form def restaurant_list(request): # Initialize form with any GET parameters from the request form = RestaurantSearchForm(request.GET) # Start with all restaurants, ordered by name restaurant_list = Restaurant.objects.order_by('-name') # Apply filters if form data is valid if form.is_valid(): food_filter = form.cleaned_data.get('food') city_filter = form.cleaned_data.get('city') min_rating = form.cleaned_data.get('average_rating') if food_filter: restaurant_list = restaurant_list.filter(food=food_filter) if city_filter: restaurant_list = restaurant_list.filter(city=city_filter) if min_rating: # Since average_rating is a model method, we filter via list comprehension # For large datasets, consider storing average rating as a database field restaurant_list = [r for r in restaurant_list if r.average_rating() >= int(min_rating)] context = { 'restaurant_list': restaurant_list, 'form': form # Pass the form to the template } return render(request, 'restaurants.html', context)
Note: Since average_rating is a computed method, we can't use Django's ORM filter() directly on it. The list comprehension works well for small datasets—if you have hundreds of restaurants, you’ll want to add a database field to store precomputed average ratings and update it whenever a review is added.
3. Update restaurants.html with Bootstrap-Styled Filters
Replace your existing search section with this Bootstrap 4-friendly code, and tweak the results table to show human-readable values:
{% extends 'base.html' %} {% load staticfiles %} {% load custom_filters %} <!-- Add this if you use the add_class filter below --> {% block content %} <div class="container"> <div class="row justify-content-center"> <div class="col-md-6"> <h3 class="text-center">Search Criteria</h3> <hr> <!-- Use GET method to keep filters in the URL --> <form method="get" class="form-inline justify-content-center"> {{ form.food|add_class:"form-control mr-2" }} {{ form.city|add_class:"form-control mr-2" }} {{ form.average_rating|add_class:"form-control mr-2" }} <button type="submit" class="btn btn-primary">Search</button> </form> </div> </div> </div> <div class="container mt-4"> <div class="row justify-content-center"> <div class="col-md-8"> <h3 class="text-center">Search Results</h3> <hr> {% if restaurant_list %} <div class="table-responsive"> <table class="table table-bordered table-hover text-center"> <caption class="text-center">Restaurants matching your criteria</caption> <thead> <tr> <th>Name</th> <th>Description</th> <th>Food Type</th> <th>City</th> <th>Average Rating</th> </tr> </thead> <tbody> {% for restaurant in restaurant_list %} <tr> <td class="text-left"> <a href="{% url 'restaurants:restaurant_detail' restaurant.id %}"> <strong>{{ restaurant.name }}</strong> </a> </td> <td class="text-left">{{ restaurant.desc }}</td> <!-- Show human-readable food type instead of integer --> <td>{{ restaurant.get_food_display }}</td> <td>{{ restaurant.get_city_display }}</td> <td> {% with avg_rating=restaurant.average_rating %} {% if avg_rating %} {{ avg_rating|floatformat:1 }} Stars {% else %} No Reviews {% endif %} {% endwith %} </td> </tr> {% endfor %} </tbody> </table> </div> {% else %} <p class="text-center">No restaurants match your search criteria.</p> {% endif %} </div> </div> </div> {% endblock %}
4. Add the add_class Template Filter (Optional but Recommended)
To apply Bootstrap's form-control class to form fields easily, create a templatetags folder in your app, add __init__.py and custom_filters.py:
from django import template register = template.Library() @register.filter(name='add_class') def add_class(field, css_class): return field.as_widget(attrs={'class': css_class})
That's all! Now your restaurants.html will have three functional dropdown filters, handle searches on the same page, and display results with clean Bootstrap styling. You’ll see human-readable labels for food types and cities instead of raw integers, and the average rating will show up nicely (or note if there are no reviews).
内容的提问来源于stack exchange,提问作者MSM

