python - Django form, view, and forms.py not working together

My first foray into Django, allows the user to input a day of the week, and searches my database for restaurants open on that given day. Right now, the restaurant objects (Resto) have a days_open attribute, with each day separated by a comma (Monday, Tuesday, etc...).

When I input a day, the resulting page only displays the title and '[]' and I can't seem to get it to return the list of Resto objects. What is displaying the square brackets, and how do I go about fixing it to display the results of the search?

The code is below- my apologies if I neglected to include any relevant bits.

my forms.py:

from django import forms
from .models import Resto

class RestoSearch(forms.ModelForm):
    class Meta: 
        model = Resto
        fields = ('title', 'description', 'opening_hour', 'closing_hour')

models.py:

from django.db import models

class Resto(models.Model):
    title = models.CharField(max_length=300)
    description = models.TextField()
    opening_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
    closing_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
    days_open = models.TextField(blank=True)

views.py:

from django.shortcuts import render
from django.http import Http404
from django.shortcuts import HttpResponse
from belize.models import Resto
from django.core.exceptions import *
from .forms import RestoSearch

def index(request):
    return render(request, 'form.html')

def search(request):

    form = RestoSearch()

    if request.method == 'POST':
        search_id=request.POST.get('textfield', None)
        try:
            #I think this is where my problem is

            available = Resto.objects.filter(days_open = search_id)

            html = ("<H1>Hello</H1>", available)
            return HttpResponse(html)
        except Resto.DoesNotExist:
            return HttpResponse("Please try another day")  
        else:
            return render(request, 'belize/form.html')


def restaurant_detail(request, id):
    try:
        restaurant = Resto.objects.get(id=id)
    except Resto.DoesNotExist:
        raise Http404('This restaurant does not exist')
    return render(request, 'belize/restaurant_detail.html', {
        'restaurant': restaurant,
        })

template form.html:

<form method="POST" action="/search/"> 
{% csrf_token %}
<input type="text" name="textfield">

<button type="submit">Enter a day of the week</button>
</form>

2 Answers

  1. Lorin- Reply

    2019-11-16

    The [] means that your .filter() returned no results, its not surprising as you have a few issues with your code, lets start from the top:

    1. You are declaring a form that you never use.
    2. You are trying to catch an exception that is never raised by .filter()
    3. You filter condition will only work for exact matches.

    I've annotated your code as well:

    def search(request):
        form = RestoSearch()  # You aren't using this form anywhere in your code?
    
        if request.method == 'POST':
            # Where is 'textfield' coming from?
            search_id = request.POST.get('textfield', None)
    
            try:
                # If search id is "Tuesday", and a restaurant is
                # open on monday and tuesday, so it has "Monday,Tuesday"
                # in the days_open field, then this search will not
                # return any results, because its looking for an exact
                # match
                available = Resto.objects.filter(days_open=search_id)
                html = ('<H1>Hello World</H1>', available)
                return HttpResponse(html)
            except Resto.DoesNotExist:
                # This exception is not raised by .filter(),
                # .filter() will return an empty list, [] if no results are found
                # so this entire try/except is not doing anything
                return HttpResponse("Please try another day")
    
         else:  # in your code, this else is not indented correctly
             return render(request, 'belize/form.html')
    

    So there is a lot going on here, lets try something simple, starting with the template:

    {% if results %}
        {% for restaurant in results %}
           {{ restaurant }}
        {% endfor %}
    {% else %}
        Sorry, no results for your search. Try again.
    {% endif %}
    
    <form>
       {{ form }}
       <input type="submit" name="Search" />
    </form>
    

    Next, the search form:

    class SearchForm(forms.Form):
        search_field = forms.CharField('Search', strip=True)
    

    Finally the view:

    from django.db.models import Q
    
    def search(request):
        form = SearchForm(request.GET)
        results = []  # Set results to an empty list
        if form.is_valid():
           needle = form.cleaned_data['search_field'].capitalize()
           results = Resto.objects.filter(Q(days_open__startswith='{},'.format(needle)) |
                     Q(days_open__endswith=',{}'.format(needle)) |
                     Q(days_open__contains=',{},'.format(needle)) |
                     Q(days_open='{},'.format(needle)) |
                     Q(days_open='{}'.format(needle)))
        return render(request, 'search.html', {'results': results, 'form': form})
    

    Lets assume the user entered 'Thursday' as a search field. In this view you are searching for all restaurants whose days_open field:

    1. Either starts with Thursday, or
    2. Ends with ,Thursday or
    3. Contains ,Thursday, in the middle or
    4. Has the value Thursday, or
    5. Has the value Thursday

    Your template will then only show the results if there are any values to display; since an empty list [] is false, then the {% if results %} condition will fail, so on empty lists the template will display the error notice instead.

    In your view, you only do the database check if someone enters something in the search field, that's what if form.is_valid(): does. In django, by default all form fields are required - so a blank form will return an error. Using this trick, we make sure we only search if someone enters a value in the search box.

    The main action happens with all the Q() calls. A Q object is a way to do multiple queries and chain them together. It is used whenever you want to do an "or" or "and" type query. Our search is an "or" type query, because we want to return any results if the value in days_open matches any number of conditions.

  2. Luke- Reply

    2019-11-16

    I presume what you are trying to show is the RestoForm in that case the index method is not correct. It should be

    def index(request):
        form = RestoForm()
        return render(request, 'form.html', {'form': form })
    

    And then your template should change as

    <form method="POST" action="/search/"> 
    {% csrf_token %}
    {{ form }}
    
    <button type="submit">Enter a day of the week</button>
    </form>
    

    For additional details please see the examples at https://docs.djangoproject.com/en/1.9/topics/forms/#the-template

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>