In the previous entry, we created a simple Django app that returned random Simpsons quotes. The application was not interactive; that is, it did not really process the request from the client. In this entry, we will modify the previous application so it is interactive. Specifically, we will create a very simple search engine that returns the Simpson quotes that have a specific word in them.
So, to begin with, we must plan ahead our application’s interface. For that, we must review two types of HTTP requests: the GET request, that is generally used to asks for a document; and the POST request, that is used for sending information from the client to the server. In the previous entry, our application only reacted to the GET requests. Now, we will add capabilities to it to process POST requests. The POST request will contain the search terms sent by the client. The GET request can be used to transmit information by using parameters in the URL; but we will leave that by the moment.
From the client’s point of view, the POST request is generated in an HTML form element. This element must be sent by the server, with a prior request by the client. The interchange of HTTP requests and replies is a little bit more complicated.
Let’s see first what that HTML document will look like, and leave for later how will we implement the protocol. The absolute minimum code is this:
<html> <head> <title>Search Simpsons quote</title> </head> <body> <form action='quotesearch' method='POST'> <input type='text' name='terms' /> <input type='submit' value='Search' /> </form> </body> </html>
So, the view we are designing will have to return this document on a GET request, and process and return the results on a POST request. The results will also be returned as an HTML document. The search process will consist on a filtering of quote contents, so that all the search terms are included. The resulting code for our view is this:
def quotesearch(request): if request.method == 'GET': # First user request return HttpResponse(htmlcode) else: # Second request searchterms = request.POST['terms'].split() if len(searchterms) >= 1: searchresults = SimpsonQuote.objects.filter(content__icontains = searchterms) for additional_term in searchterms[1:]: # Filter by any additional terms searchresults = searchresults.filter(content__icontains = additional_term) if len(searchresults) > 0: response = '<html><head><title>Search results</title></head><body>' for quote in searchresults: response += '<p>"' + quote.content + '" ' + quote.character + ' (S' + str(quote.season) + 'E' + str(quote.episode) + ')</p>' response += '</body></html>' return HttpResponse(response) else: return HttpResponse('No results found') else: # Do some error handling here if the client sends an empty request return HttpResponse('Please enter some search terms')
The view first determines the type of the request. If it is a POST request, it will extract the search terms. If there is at least one, it will filter the entries by the search term or terms. If results are present, they are returned to the client. In any other case, a message is returned to the client with information on why there are no results.
This code misses some important security features, so it won’t work with the default Django settings. We will have to change them to allow this insecure code to run. Specifically, we want to disable CSRF protection. In the next entry, when we refine our use of responses, we will be able to go back to the secure configuration. After this warning, let’s proceed. We edit the SQproject/settings.py and comment out the entry for the CSRF middleware:
MIDDLEWARE_CLASSES = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', #'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
You should never do this in a production environment. We are doing it in this entry to keep the code simple (Do as I say, not as I do).
Finally, we must add an entry in SQproject/urls.py:
from django.conf.urls import url from django.contrib import admin from HWapp.views import randomquote, quotesearch urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^randomquote/', randomquote), url(r'^search/', quotesearch), ]
Note that we have also added an import for quotesearch.
That’s all. We can now run the development server executing python manage.py runserver and accessing http://127.0.0.1:8000/search/. You can now search on your Simpsons quote collection. This example has covered a full (but simple) interactive application that serves data according to queries, not unlike a database. In doing that we have committed two sins: we have crippled the Django security configuration, and we have hardcoded HTML in the view file. Both problems will be solved in the next entry, where we will see how to use response templates.