Django Tutorial #6: Templates

Please note that this post may contain affiliate links, and for every purchase you make, at no extra cost to you, a commission fee will be rewarded to me.

Introduction

Django template is the last part of the MTV structure. In Django, all of the templates are stored in the templates directory. Notice that the template files are named xxx.html. That is because templates are what you see in the browser just like regular HTML files.

But of course, things are more complicated than that. Django’s template system allows us to design templates without repeated coding. Read the official documentation for details.

In this tutorial, I will be using this template for our blog app as an example.

You can download the source code here:

Assuming you understand how HTML, CSS and JS work. This is the view structure I designed:

The layout.html contains the <head> section, the navigator, footer and necessary js files. I also separated the blog masonry and sidebar because they appear on multiple pages.

Layout

<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Blog Home - Start Bootstrap Template</title>
  {% load static %}
  <!-- Bootstrap core CSS -->
  <link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="{% static "css/style.css" %}" rel="stylesheet">

</head>

<body>

  <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
    <div class="container">
      <a class="navbar-brand" href="/blog">Django Blog</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item active">
            <a class="nav-link" href="/blog">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">Django Tutorial For Beginners</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="/admin">Admin</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>
{% block content %}

{% endblock %}
  <!-- Footer -->
  <footer class="py-5 bg-dark">
    <div class="container">
      <p class="m-0 text-center text-white">Copyright © Django Blog 2019</p>
    </div>
    <!-- /.container -->
  </footer>

  <!-- Bootstrap core JavaScript -->
  <script src="{% static "js/jquery/jquery.min.js" %}"></script>
  <script src="{% static "js/bootstrap.bundle.min.js" %}"></script>

</body>

</html>

Line 12-17, include all css files which are stored in blog/static/css.

Line 45-47, the block named “content” will load here.

Line 57-58, load all necessary js files.

Statics

First of all, all static files (css and js files) are stored in the blog/static directory.

Line 12, {% load static %}. As the code suggests, it loads all the static files.

Line 14, {% static "css/bootstrap.min.css" %} generates the absolute URL of static files.

Blog Masonry

{% for post in posts %}
        <!-- Blog Post -->
        <div class="card mb-4">
          <img class="card-img-top" src="{{post.featured_image.url}}" alt="{{post.title}}">
          <div class="card-body">
            <h2 class="card-title">{{post.title}}</h2>
            <p class="card-text">{{post.content|striptags|truncatewords_html:50}}</p>
            <a href="/blog/post/{{ post.slug }}" class="btn btn-primary">Read More →</a>
          </div>
          <div class="card-footer text-muted">
            Posted on {{post.created_at}} by
            <a href="#">{{post.user}}</a>
          </div>
        </div>
{% endfor %}

Line 1 and 15 form a for loop, this block of code will repeat for every post in all posts.

Line 4, {{post.featured_image.url}} gives the URL to the featured image of the post. {{post.title}} gives the title of the post.

Line 7, {{post.content|striptags|truncatewords_html:50}}. post.content gives the content of the post. striptags stripes the HTML tags. truncatewords_html:50 truncates a string if it is longer than the specified number of words.

Read the official document for all the other tags and filters you can use.

Sidebar

      <!-- Sidebar Widgets Column -->
      <div class="col-md-4">

        <!-- Search Widget -->
        <div class="card my-4">
          <h5 class="card-header">Search</h5>
          <div class="card-body">
            <div class="input-group">
              <input type="text" class="form-control" placeholder="Search for...">
              <span class="input-group-btn">
                <button class="btn btn-secondary" type="button">Go!</button>
              </span>
            </div>
          </div>
        </div>

        <!-- Categories Widget -->
        <div class="card my-4">
          <h5 class="card-header">Categories</h5>
          <div class="card-body">
            <div class="row">
              <div class="col-lg-6">
                <ul class="list-unstyled mb-0">
                    {% for category in categories %}
                    <li>
                    <a href="/blog/category/{{ category.slug }}">{{ category.name }}</a>
                    </li>
                    {% endfor %}
                </ul>
              </div>
            </div>
          </div>
        </div>

              <!-- Tags Widget -->
        <div class="card my-4">
          <h5 class="card-header">Tags</h5>
          <div class="card-body">
            <div class="row">
              <div class="col-lg-6">
                <ul class="list-unstyled mb-0">
                    {% for tag in tags %}
                    <li>
                    <a href="/blog/tag/{{ tag.slug }}">{{ tag.name }}</a>
                    </li>
                    {% endfor %}
                </ul>
              </div>
            </div>
          </div>
        </div>

        <!-- Side Widget -->
        <div class="card my-4">
          <h5 class="card-header">Side Widget</h5>
          <div class="card-body">
            You can put anything you want inside of these side widgets. They are easy to use, and feature the new Bootstrap 4 card containers!
          </div>
        </div>

      </div>

Home

{% extends "blog/layout.html" %}

{% block content %}

  <!-- Page Content -->
  <div class="container">

    <div class="row">

      <!-- Blog Entries Column -->
      <div class="col-md-8">

        <h1 class="my-4">Django Blog
          <small>Home Page</small>
        </h1>

          {% include 'blog/widgets/blog_masonry.html' %}

      </div>

        {% include 'blog/widgets/sidebar.html'%}

    </div>
    <!-- /.row -->

  </div>
  <!-- /.container -->

{% endblock %}

Line 1 tells Django that this template extends to the layout.

Line 3 and 29 define the “content” block, which we talked about earlier.

Line 17 and 21 include the blog masonry and sidebar.

Category

{% extends "blog/layout.html" %}

{% block content %}

  <!-- Page Content -->
  <div class="container">

    <div class="row">

      <!-- Blog Entries Column -->
      <div class="col-md-8">

        <h1 class="my-4">Django Blog
          <small>Category: {{ category.name }}</small>
        </h1>

          {% include 'blog/widgets/blog_masonry.html' %}

      </div>

        {% include 'blog/widgets/sidebar.html'%}

    </div>
    <!-- /.row -->

  </div>
  <!-- /.container -->

{% endblock %}

Tag

{% extends "blog/layout.html" %}

{% block content %}

  <!-- Page Content -->
  <div class="container">

    <div class="row">

      <!-- Blog Entries Column -->
      <div class="col-md-8">

        <h1 class="my-4">Django Blog
          <small>Tag: {{ tag.name }}</small>
        </h1>

          {% include 'blog/widgets/blog_masonry.html' %}

      </div>

        {% include 'blog/widgets/sidebar.html'%}

    </div>
    <!-- /.row -->

  </div>
  <!-- /.container -->

{% endblock %}

Post

{% extends "blog/layout.html" %}

{% block content %}

  <!-- Page Content -->
  <div class="container">

    <div class="row">

            <!-- Post Content Column -->
      <div class="col-lg-8">

        <!-- Title -->
        <h1 class="mt-4">{{post.title}}</h1>

        <!-- Author -->
        <p class="lead">
          by
          <a href="#">{{post.user}}</a>
        </p>

        <hr>

        <!-- Date/Time -->
        <p>Posted on {{ post.created_at }}</p>

        <hr>

        <!-- Preview Image -->
        <img class="img-fluid rounded" src="{{ post.featured_image.url }}" alt="">

        <hr>

        <!-- Post Content -->
        {{ post.content|safe }}
        <hr>

        <!-- Comments Form -->
        <div class="card my-4">
          <h5 class="card-header">Leave a Comment:</h5>
          <div class="card-body">
            <form>
              <div class="form-group">
                <textarea class="form-control" rows="3"></textarea>
              </div>
              <button type="submit" class="btn btn-primary">Submit</button>
            </form>
          </div>
        </div>

        <!-- Single Comment -->
        <div class="media mb-4">
          <img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
          <div class="media-body">
            <h5 class="mt-0">Commenter Name</h5>
            Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi vulputate fringilla. Donec lacinia congue felis in faucibus.
          </div>
        </div>

        <!-- Comment with nested comments -->
        <div class="media mb-4">
          <img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
          <div class="media-body">
            <h5 class="mt-0">Commenter Name</h5>
            Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi vulputate fringilla. Donec lacinia congue felis in faucibus.

            <div class="media mt-4">
              <img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
              <div class="media-body">
                <h5 class="mt-0">Commenter Name</h5>
                Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi vulputate fringilla. Donec lacinia congue felis in faucibus.
              </div>
            </div>

            <div class="media mt-4">
              <img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
              <div class="media-body">
                <h5 class="mt-0">Commenter Name</h5>
                Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi vulputate fringilla. Donec lacinia congue felis in faucibus.
              </div>
            </div>

          </div>
        </div>

      </div>

        {% include 'blog/widgets/sidebar.html'%}

    </div>
    <!-- /.row -->

  </div>
  <!-- /.container -->

{% endblock %}

At this point, we should have a working blogging system that is ready to deploy. We’ll talk about the deployment in the next post.

Next Post: Django Tutorial #7: Deploy

Tags:

Leave a Reply

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