Python 3x Pandas Django

Django Views & Templates


This tutorial begins where the previous tutorial session left off. We’re continuing the polls application and will focus on creating the public interface – “views.”

A view is a “type” of Web page in your Django application that generally serves a specific function and has a specific template. For example, in our poll application, we’ll have the following four views:

• Question “index” page – displays the latest few questions.

• Question “detail” page – displays a question text, with no results but with a form to vote.

• Question “results” page – displays results for a particular question.

• Vote action – handles voting for a particular choice in a particular question.

In Django, web pages and other content are delivered by views. Each view is represented by a Python function. Django will choose a view by examining the URL that’s browser requested.

A URL pattern is the general form of a URL - for example: /polls/5/results/

To get from a URL to a view, Django uses what are known as ‘URLconfs’. A URLconf maps URL patterns to views. This tutorial provides basic instruction in the use of URLconfs.

Writing views

Now let’s add a few more views to polls/views.py. These views are slightly different, because they take an argument:

from django.http import HttpResponse

def index(request):
    return HttpResponse("You're looking at index page.")

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

To create a URLconf in the polls app directory, create a file called polls/urls.py or copy urls.py from project folder myfirstsite and remove all the lines in it.

Wire these new polls views into the polls.urls module by adding the following path() calls:

from django.urls import path

from . import views

urlpatterns = [
    # ex: http://127.0.0.1:8000/polls/
    path('', views.index, name='index'),
    # ex: http://127.0.0.1:8000/polls/5/
    path('/', views.detail, name='detail'),
    # ex: http://127.0.0.1:8000/polls/5/results/
    path('/results/', views.results, name='results'),
    # ex: http://127.0.0.1:8000/polls/5/vote/
    path('/vote/', views.vote, name='vote'),
]

The next step is to point the root URLconf at the polls.urls module. In myfirstsite/urls.py, add an include() in the urlpatterns list, so you have:

from django.contrib import admin
from django.urls import path, include

from helloworld import views

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('helloworld/', include('helloworld.urls')),
    path('admin/', admin.site.urls),
    path('', views.index, name='index'),
]

Take a look in your browser, at http://127.0.0.1:8000/polls/23/. It’ll run the detail() method and display whatever ID you provide in the URL. Try http://127.0.0.1:8000/polls/23/results/ and http://127.0.0.1:8000/polls/34/vote/ too – these will display the placeholder results and voting pages.

When somebody requests a page from your website – say, /polls/23/, Django will load the myfirstsite.urls Python module because it’s pointed to by the ROOT_URLCONF setting. It finds the variable named urlpatterns and traverses the patterns in order. After finding the match at polls/, it strips off the matching text "polls/" and sends the remaining text – 23/ – to the polls.urls URLconf for further processing. There it matches < int:question_id >/,

resulting in a call to the detail() view like so:

detail(request=, question_id=23)

The question_id=23 part comes from < int:question_id >. Using angle brackets “captures” part of the URL and sends it as a keyword argument to the view function. The question_id part of the string defines the name that will be used to identify the matched pattern, and the int part is a converter that determines what patterns should match this part of the URL path. The colon (:) separates the converter and pattern name.

Write views that actually do something for polls application

Each view is responsible for doing one of two things: either returning an HttpResponse object containing the content for the requested page or raising an exception such as Http404. The rest is up to you.

All Django wants is that HttpResponse. Or an exception.

Let’s update index method in polls/views.py by the following lines:

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    resp = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(resp)

Take a look in your browser, at http://127.0.0.1:8000/polls/. You will see the following output:

What’s your dream holiday destination?

There’s a problem here, though: the page’s design is hard-coded in the view. If you want to change the way the page looks, you’ll have to edit this Python code. So let’s use Django’s template system to separate the design from Python by creating a template that the view can use.

First, create a directory called templates in your polls directory. Django will look for templates in there.

Your project’s TEMPLATES setting describes how Django will load and render templates. The default settings file configures a DjangoTemplates backend whose APP_DIRS option is set to True. By convention DjangoTemplates looks for a templates subdirectory in each of the INSTALLED_APPS.

You can see the following lines in your project setting.py file,

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Within the templates directory you have just created, create another directory called polls, and within that create a file called index.html. In other words, your template should be at polls/templates/polls/index.html. Because of how the app_directories template loader works as described above, you can refer to this template within Django as polls/index.html.

Put the following code in index.html

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif % }

Jinja Templates:

Jinja is a Python templating engine, aimed at helping you to do dynamic things with your HTML like passing variables, running simple logic, and more! With Jinja, you will notice we are using {% %},this denotes logic. For variables, you will see {%{% }}. The use of block content and endlock is one of the two major ways of incorporating templates within templates.

This method is generally used for header/template combinations, but there is another you could use that I will show later.

You can find more example in this link https://docs.djangoproject.com/en/3.2/ref/templates/builtins/ and https://docs.djangoproject.com/en/3.2/topics/templates/

render() function:

The render() function takes the request object as its first argument, a template name as its second argument and a dictionary as its optional third argument. It returns an HttpResponse object of the given template rendered with the given context.

Now let’s update our index view in polls/views.py to use the template:

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

You can see the following output in your browser:

Image

NOTE: Notice hyperlink on each displaying questions because of the line <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> added in our index.html page.

As of now, we have one question in the database so it’s display one. You can try to add multiple questions and choice from our admin interface and see the output in /polls/ url.

get_object_or_404():

The get_object_or_404() function takes a Django model as its first argument and an arbitrary number of keyword arguments, which it passes to the get() function of the model’s manager. It raises Http404 if the object doesn’t exist.

Now, let’s update the question detail view – the page that displays the question text for a given poll. Here’s the view:

from django.shortcuts import render, get_object_or_404

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

NOTE: There’s also a get_list_or_404() function, which works just as get_object_or_404() – except using filter() instead of get(). It raises Http404 if the list is empty.

Create a new template file polls/detail.html:

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

You can see the following output in your browser:

Image

Removing hardcoded URLs in templates

Remember, when we wrote the link to a question in the polls/index.html template, the link was partially hardcoded like this:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

The problem with this hardcoded, tightly-coupled approach is that it becomes challenging to change URLs on projects with a lot of templates. However, since you defined the name argument in the path() functions in the polls.urls module, you can remove a reliance on specific URL paths defined in your url configurations by using the {% url % } template tag:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

The way this works is by looking up the URL definition as specified in the polls.urls module. You can see exactly where the URL name of ‘detail’ is defined below:

...
# the 'name' value as called by the 	{% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...

If you want to change the URL of the polls detail view to something else, perhaps to something like polls/specifics/12/ instead of doing it in the template (or templates) you would change it in polls/urls.py:

...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...

Namespacing URL names

The tutorial project has just one app, polls. In real Django projects, there might be five, ten, twenty apps or more. How does Django differentiate the URL names between them? For example, the polls app has a detail view, and so might an app on the same project that is for a blog. How does one make it so that Django knows which app view to create for a url when using the {% url %} template tag?

The answer is to add namespaces to your URLconf. In the polls/urls.py file, go ahead and add an app_name to set the application namespace:

Add the line app_name = 'polls' to your polls/urls.py file and your file looks like below,

from django.urls import path
from . import views

app_name = 'polls'

urlpatterns = [
    ……..
]

Now change your polls/index.html template from:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

to point at the namespaced detail view:

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

Again, take a look in your browser, at http://127.0.0.1:8000/polls/ and click on each questions and it will redirect to the selected detail page.

Congratulations! You have successfully completed Basic Django Views and Templates tutorial session. Let us move to the next topic basics about form processing and generic views.

If you have any doubts or queries related to this chapter, get them clarified from our Python Team experts on ibmmainframer Community!