8. Output from models

Robert Crowther Feb 2022
Last Modified: Feb 2023

PrevNext

A Django model instance can’t ‘generate a webpage’—there is no ‘make_html()’ function. The model, and it’s data, must to be pushed into a template. The template both lays out the HTML, and pulls appropriate data from the model (and maybe formats the data also). This is a super‐flexible system used by every web‐framework I can think of (off‐the‐cuff). On the other hand, it’s more work than a CMS.

The previous steps 6 Static Files, and 7 Base Template, gave us the resources needed to generate webpages throughout the new website. Let’s look again at the app built in Step 4 New Apps, and set it up to generate webpages.

Setup

Base template

As constructed in the last step, these examples use a base template. If you do not have a base template, delete the blocks, or modify to comments,

{% extends "base.html" %}

To,

{# extends "base.html" #}

Template folder

Navigate into the ‘reviews’ app (or whatever app you were building). Even if you generated the app using ‘startapp’, you will not have a template folder. This is because some apps do not use or need templates.

Run this. If the name of your app is different, use that. Yes, the command creates two directories, one called ‘templates’, and one inside named after the app. I will not explain much—the nested directories are for namespacing, and are standard Django configuration,

mkdir -p templates/reviews

Webpages for details

View

Right, let’s make a view. Views wire Django model instances to URLs (and can also gather extra information, and break up a model). Django calls views and webpages that display data from a model instance a ‘Detail’.

We’ll start with one of Django’s convenience views. In reviews/views.py, like this,

from django.views.generic import DetailView
from reviews.models import Review

class ReviewDetailView(DetailView):
    # Change the name of the object in the template
    # context_object_name = 'review'
    # Allow all objects to be shown
    model = Review
    # Allow some objects to be shown, or re-ordered etc.
    #queryset = Review.objects.all()

Template

The view passes information to a template. For the view we have constructed, with a DetailView, Django will search for a template that is in a pre‐configured place, with a preconfigured name.

The instance of the model is passed wholesale, and the the template code can break it up. Create /reviews/templates/reviews/review_detail.html, and add,

{% extends "base.html" %}

{% block content %}
    <article class="reviewdetail-typeblock">
        <h1>{{review.human_title}}</h1>
        <date class="create_date">{{ review.date }}</date>
        <div class="price">{{ review.price }}</div>
        <div class="teaser">{{ review.teaser }}</div>
        <div class="review">{{ review.review }}</div>
        <div class="summary">{{ review.sumry }}</div>
    </article>
{% endblock %}

Note: We use the ‘human_title’ method on the object which constructs a title from ‘make’ and ‘model’ fields. May often be a simple ‘title’ field.

URL

Now configure to call the view from URLs in siteName/urls.py. For slug indexing, like this,

from django.urls import path
from reviews.views import ReviewDetailView

urlpatterns = [
    path('review/<slug:slug>',  ReviewDetailView.as_view(), name='review_detail'),
]

Or, for id indexing, like this,

urlpatterns = [
    path('review/id/<int:pk>',  ReviewDetailView.as_view(), name='review_detail_id'),
]

Note that the path element must be called ‘pk’ (‘primary key’). That’s because that name in the URL definition is given to the DetailView, which will then look for that information when it tries to find the requested review. The code in DetailView can search for keys with the name ‘slug’ or ‘pk’—any other name will not work.

Test

For slugs you will need to know the slug name of any existing review,

http://127.0.0.1:8000/review/washing-machine

For id/pks, you can guess a number,

http://127.0.0.1:8000/review/1

Refs

Django tutorial on generic classes, https://docs.djangoproject.com/en/3.2/topics/class-based-views/generic-display/

Webpages for lists

View

Right, let’s make a view and template to display a list of the available model instances.

Another lump of code convenience, with a more common name ‘ListView’. In /review/views.py, make like this,

from django.views.generic import ListView
from review.models import Review

class ReviewListView(ListView):
    # Change the name of the object in the template
    # context_object_name = 'review'
    # Allow all objects to be shown
    model = Review
    # Allow some objects to be shown, or re-ordered etc.
    #queryset = Review.objects.all()

Template

In /reviews/templates/reviews/review_list.html,

{% extends "base.html" %}

{% block content %}
    <h2>Reviews</h2>
    <ul>
        {% for obj in review_list %}
            <li><a href="{{ obj.get_absolute_url }}">{{ obj.human_title }}</a></li>
        {% endfor %}
    </ul>
{% endblock %}

Probably the most important conceptual note is this—remember when we made the model, we provided a method ‘get_absolute_url’? This is a trick in Django—Django code goes and looks which URL is used to launch model instance pages—the ‘DetailView’s. The code can then write those URLs into the weblinks. It’s a concept‐bending idea but, right now, I’d not think about it and bash in the code (at some point you’ll realize what is happening). The important thing is, we are going to generate links that join to all the available model instance webpages.

Smaller notes: the code uses the method on the object which constructs a title from ‘make’ and ‘model’ fields. Could easily use a ‘title’ field. And also, I’m using the name ‘review_list’ to access the data, but the generic data name ‘object_list’ will work also.

URL

In siteName/urls.py, like this,

from django.urls import path
from review.views import ReviewListView

urlpatterns = [
    path('reviews/', ReviewListView.as_view()),
]

Test

http://127.0.0.1:8000/reviews/

If that worked, now you do not need guess what name or id your model instance pages use. You have a list of weblinks and can jump to them.

Refs

Django tutorial on generic classes, https://docs.djangoproject.com/en/3.2/topics/class-based-views/generic-display/