Building a Food Delivery App With Django and Python 3: Part 7 Menu View and Search Bar

LegionScript
3 min readDec 5, 2020

--

Video Tutorial

Code on Github

In this tutorial we are going to finish up the last page that we haven’t done yet, which is the menu page. We will list out all of the MenuItem objects and also give the user a search bar to search for specific items. Let’s start with building the basic part of the view for the search and the view for the menu.

In our customer/views.py:

class Menu(View):
def get(self, request, *args, **kwargs):
menu_items = MenuItem.objects.all()
context = {
'menu_items': menu_items,
}
return render(request, 'customer/menu.html', context)class MenuSearch(View):
pass

We will add some logic for the search in a little bit. Let’s add two urls for these views:

urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
path('restaurant/', include('restaurant.urls')),
path('', Index.as_view(), name='index'),
path('about/', About.as_view(), name='about'),
path('menu/', Menu.as_view(), name='menu'),
path('menu/search/', MenuSearch.as_view(), name='menu-search'),
path('order/', Order.as_view(), name='order'),
path('order-confirmation/<int:pk>/',
OrderConfirmation.as_view(), name='order-confirmation'),
path('payment-confirmation/', OrderPayConfirmation.as_view(),
name='payment-confirmation'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

With this, we can build out our menu.html, here is the entire file:

{% extends 'customer/base.html' %}{% block content %}
<div class="container">
<div class="row justify-content-center mt-5">
<div class="col-md-6 col-sm-12 text-center">
<h1>What's On Our Menu?</h1>
</div>
</div>
<div class="row justify-content-center mt-5">
<div class="col-md-8 col-sm-12 text-center">
<!-- Search form -->
<form method="GET" action="{% url 'menu-search' %}">
<div class="md-form mt-0 active-cyan-2">
<input class="form-control" name="q" type="text" placeholder="Search Our Menu" aria-label="Search" value="{{ request.GET.q }}">
</div>
</form>
</div>
</div>
<div class="row justify-content-center mt-5">
{% for item in menu_items %}
<div class="col-md-4 col-sm-12 text-center mb-5">
<img class="rounded" src="{{ item.image.url }}" width="350" height="300"/>
<h5 class="mt-3">{{ item.name }}</h5>
<p>Price: {{ item.price }}</p>
<p>{{ item.description }}</p>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}

Currently, since we grabbed all of our menu items, this will list out every item in the database. In our html template, there is a search bar. You will see that we are setting a name value of q on the input and we are submitting the form as a get request. This means that whatever we type into the search bar will be added to our url in this format:

...?q=search_query

we can get whatever q is set as to filter the results. We will add this logic to our MenuSearch view next. You will also see that the url for the form submission set in the action attribute is the menu search view. We need to rerender this template with the filtered results. Here is the updated menu serach view:

from django.db.models import Qclass MenuSearch(View):
def get(self, request, *args, **kwargs):
query = self.request.GET.get("q")
menu_items = MenuItem.objects.filter(
Q(name__icontains=query) |
Q(price__icontains=query) |
Q(description__icontains=query)
)
context = {
'menu_items': menu_items
}
return render(request, 'customer/menu.html', context)

We are using Q to add some advanced filters to our MenuItem.objects.filter(). We are matching the object if the search query matches any part of the name, price, or description. We can get the search query with self.request.GET.get(“q”). We are then passing that into the context in our template.

Now when we search for something, it should re render the menu page with a filter for whatever we searched.

With these changes, we should have a working menu page and a working search bar on the menu page. This is where we are going to stop this tutorial. This is all I had planned for this application so this will probably be the last addition to it unless I find something else that would be a good addition for it.

--

--

No responses yet