Django Tutorial Part 7: Creating Views

Disclaimer: Your support helps keep JovialGuide running! Our content is reader-supported. This means if you click on some of our links, we may earn a commission.

In part 5 of this complete beginners guide to Django, we talked about working with Django admin site – how to create a superuser, how to login to the Django admin site, etc. And in part 6, we showed you how to make queries with the Django ORM.

Django views is responsible for handling the requests to the server and sending back responses to the user. It is like the center of a web application that takes in a request and returns a response. In this complete beginners guide to Django part 7, we will show you how to create views – creating the list, detail, edit and the delete views. This is a complete beginners guide to Django, so if you haven’t read part 6, then see part 6: making queries (Django ORM) or see part 1 – introduction to Django to get started.

What are Django Views?

Django views are Python functions or classes that receive a request and returns a response. A response can be a 404 error page, an HTML document (Template), a redirect, etc., while a request could be to display all articles, show user profile, show and image, etc. Just like models, views are an essential part of a Django application, because it can create, retrieve, update and delete from models. Views can also return an HTML template which is the User Interface (UI) users will interact with.

What are Django Views Used for?

Django views are used for:

  • Retrieving objects and queryset from a database (see part 6: Django ORM)
  • Returning responses back to the user
  • Returning HTML documents
  • Returning a redirection to another page
  • etc

Creating Homepage Views

Django supports two types of views:

  • Function Based Views (FBV)
  • Class Based Views (CBV)

For this complete beginners guide to Django, we will use Function Based Views (FBV).

We will start by creating the list views for our blog. The homepage views will display 10 recently published articles and categories, and will serve as our blog homepage.

Update the homepage views in the blog’s views.py file with the following lines of code:

from django.shortcuts import render, get_object_or_404, redirect

from blog.models import Article, Category

def homepage(request):
  articles = Article.objects.all(status='published')[:10]
  categories = Category.objects.all(status='published')[:10]

  context = {
    'articles':articles,
    'categories':categories
  }
  return render(request, 'blog/homepage.html', context)
  • We imported render from django.shortcuts. We will need it to assign a template to a views and to pass context to the template. get_object_or_404 will be used to redirect to an error page if an object is not found. While redirect will be used for redirecting users to another page.
  • For us to retrieve all queryset in the model, we start by importing each model. We imported the Article and the Category models.
  • We retrieved all articles – articles = Article.objects.all() and categories – categories = Category.objects.all() and stored them in a variable so that we can send them to the template as context, which is a Python dictionary. After that, we added [:10] to the end of the queryset, which means give me only 10 most recent result.
  • Since we want to be able to access and display all articles and categories that we retrieve from the models in the template, we created a dictionary of context that holds each of the values we want to display on the frontend (template).
  • We returned the render function we imported earlier. The render function accepts the same request object that was passed to the views as the first argument, the template to render (will be created soon!), since we will create an app specific template directory for this blog app, then our template path is blog/homepage.html (notice it starts with the app name), and the context variable (dictionary datatype) we want to display on the template.

Mapping Homepage Views to a URL

We have created the list view which is responsible for displaying the 10 recently published articles and categories, now we have to map it to a URL.

Open the django_project/blog/urls.py file and update it with:

from django.urls import path

from blog import views

urlpatterns = [
  # Homepage
  path('', views.homepage, name='homepage'),
]

If your development server is not running, run:

python manage.py runserver

After that, visit your development server, which is usually 127.0.0.1:8000/. You should see:

Django - Template Does Not Exist Error

If you are greeted with the error message like the screenshot above, then congratulations on your first Django error!

  • The TemplateDoesNotExist error is raised when the template assigned to a view is not found. Remember we returned blog/homepage.html in the return function. The homepage.html file is the template which is not found, basically because we haven’t created it yet.

Creating Homepage Template File

We will create the template file which is the interface for user interaction. Templates are basically HTMLs in Django. It is the T in MVT – Model Views Template.

When using Django for web development, Django encourages you to create modules independently. This means that each app should be independent of each other.

In the case of a template, Django also recommends you create a template directory that is only specific to the app (app specific templates) not the entire Django project. This allows you easily copy or move an app from a project without breaking it. For this complete beginners guide to Django, we will follow the Django standard of creating an app specific template directory.

To create an app specific template directory in Django, inside your app directory, create a folder called templates. And inside the templates folder that you just created, create another folder with your app name. Finally, create the HTML template files that you will use.

In a simple sentence, open the blog app, create a folder called templates. Inside the templates folder, create another folder called blog which is the name of our app. After that, create two template files base.html and homepage.html. Your directory structure should look like this:

django_project/
--blog/
----templates/
------blog/
--------base.html
--------homepage.html
  • base.html – this will serve as a base template where other templates will inherit from. This allows us have less, maintainable and single code base which are not repeated (DRY – Don’t Repeat Yourself).
  • homepage.html – this will serve as our homepage.html template that is returned from the homepage views. This template will display the 10 recently published articles and categories.

Copy and paste the following lines of code to the base.html template file:

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
  <title>{% block title %}{% endblock %}</title>
</head>

<body>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
      <a class="navbar-brand" href="#">JovialGuide</a>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
        aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
          <li class="nav-item">
            <a class="nav-link" aria-current="page" href="#">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">Blog</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">Categories</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container mt-5">
    {% block content %}{% endblock %}
  </div>

  <div class="bg-dark p-3 mt-5 text-center text-white">
    <div class="container">
      <p>&copy; 2022 <a href="https://www.jovialguide.com/" title="JovialGuide">JovialGuide</a></p>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
</body>

</html>

Open the homepage.html template, copy and paste the following lines of code:

{% extends 'blog/base.html' %}
{% block title %}Blog Homepage{% endblock %}

{% block content %}
<h2 class="text-center">Latest Posts</h2>
<div class="row">
  {% for article in articles %}
  <div class="cols-12 col-md-4 col-lg-3 mb-3">
    <div class="card">
      <img src="{{ article.featured_img.url }}" class="card-img-top" alt="{{ article.title }}">
      <div class="card-body">
        <h5 class="card-title"><a href="{{ article.get_absolute_url }}" title="{{ article.title }}">{{ article.title }}</a></h5>
        <p class="card-text">{{ article.content|truncatewords:20 }}</p>
        <a href="{{ article.get_absolute_url }}" title="{{ article.title}}"
          class="btn btn-sm btn-primary">Read more</a>
      </div>
    </div>
  </div>
  {% empty %}
  <p>There are no articles yet!</p>
  {% endfor %}
</div>
<div class="text-end">
  <a href="#" class="btn btn-sm btn-primary">More Posts</a>
</div>
<h2 class="text-center mt-3">Blog Categories</h2>
<ul>
  {% for category in categories %}
  <li class="mb-3"><a href="{{ category.get_absolute_url }}" title="{{ category.title }}">{{ category.title }}</a></li>
  {% empty %}
  <li>There are no categories yet!</li>
  {% endfor %}
</ul>
<div class="text-end">
  <a href="#" class="btn btn-sm btn-primary">More Categories</a>
</div>
{% endblock %}
  • The syntax you see in the base.html and homepage.html templates are the Django Template Language (DTL) syntax.
  • We use {{ article.featured_img.url }}. .url returns the URL of the image.
  • Remember the get_absolute_url() model method we defined in the Article and Category models? We are using them here. The get_absolute_url() model method is responsible for returning the full form of a dynamic URL.
  • We use the truncatewords:20 filter to display just 20 words from the article content. This is to serve as a post excerpt
  • On the article section, if there are no articles it displays There are no articles yet!. And on the category section, if there are no categories, it displays There are no categories yet!. This is the work of the empty block in the loop.
  • Replace JovialGuide with your own name.
  • We are using Bootstrap, a very popular CSS framework for styling
  • Some links are not working. We will fix them later
  • Note – When you retrieve a queryset, you will have to loop through it to get the individual values. But for objects, there is no need to loop through it since the values can be accessed by calling the key

Adding App Template Directory to the Template List

This is the final part. This part requires us to add the template path to the DIRS‘ list in the settings.py file of our Django project (django_project).

Open the django_project/settings.py file in the django_project project. At the top, you will see:

from pathlib import Path

Add one more import as follows:

from pathlib import Path
import os

After that, find the TEMPLATES‘ list. It Looks like this:

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',
      ],
    },
  },
]

Update it with the following lines of code by adding os.path.join(BASE_DIR, ‘blog’, ‘templates’), to the ‘DIRS’: list as follows:

TEMPLATES = [
{
‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’,
‘DIRS’: [
os.path.join(BASE_DIR, ‘blog’, ‘templates’),
],
‘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’,
],
},
},
]
[/code]

Once you finish, start the development server by running the following command in the terminal:

python manage.py runserver

After that, visit your development server, which is usually 127.0.0.1:8000/. If your result looks like the screenshot below, then congratulations!:

Django Blog Homepage

Creating Article and Category Views – List Views

These views will show all of the categories and articles with pagination.

  • A list view is a view that is responsible for displaying all queryset in a model

For pagination, add the following imports to the top of the blog’s views.py file:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

Add the following views to the blog’s views.py file:


def blog(request):
  articles = Article.objects.all(status='published')
  page = request.GET.get('page', 1)
  paginator = Paginator(articles, 10)
  try:
    articles = paginator.page(page)
  except PageNotAnInteger:
    articles = paginator.page(1)
  except EmptyPage:
    articles = paginator.page(paginator.num_pages)

  context = {
    'articles':articles,
  }
  return render(request, 'blog/blog.html', context)


def categories(request):
  categories = Category.objects.all(status='published')
  page = request.GET.get('page', 1)
  paginator = Paginator(categories, 10)
  try:
    categories = paginator.page(page)
  except PageNotAnInteger:
    categories = paginator.page(1)
  except EmptyPage:
    categories = paginator.page(paginator.num_pages)

  context = {
    'categories':categories,
  }
  return render(request, 'blog/categories.html', context)


Mapping the List Views to a URL

Update the blog urls.py file with the following:

from django.urls import path

from blog import views

urlpatterns = [
  # Homepage
  path('', views.homepage, name='homepage'),

  # List view URLS
  path('/blog', views.blog, name='blog'),
  path('/categories, views.categories, name='categories),
]

Creating the List Views Templates

Create two HTML templates in the blog template directory (where others are):

  • blog.html
  • categories.html

Update blog.html with the following:

{% extends 'blog/base.html' %}
{% block title %}Our Blog{% endblock %}

{% block content %}
<h2 class="text-center">Blog</h2>
<div class="row">
  {% for article in articles %}
  <div class="cols-12 col-md-4 col-lg-3 mb-3">
    <div class="card">
      <img src="{{ article.featured_img.url }}" class="card-img-top" alt="{{ article.title }}">
      <div class="card-body">
        <h5 class="card-title"><a href="{{ article.get_absolute_url }}" title="{{ article.title }}">{{ article.title }}</a></h5>
        <p class="card-text">{{ article.content|truncatewords:20 }}</p>
        <a href="{{ article.get_absolute_url }}" title="{{ article.get_absolute_url }}"
          class="btn btn-sm btn-primary">Read more</a>
      </div>
    </div>
  </div>
  {% empty %}
  <p>There are no articles yet!</p>
  {% endfor %}
</div>
{% endblock %}

Update categories.html with the following:

{% extends 'blog/base.html' %}
{% block title %}Blog Categories{% endblock %}

{% block content %}
<h2 class="text-center">Blog Categories</h2>
<ul>
  {% for category in categories %}
  <li><a href="{{ category.get_absolute_url }}" title="{{ category.title }}">{{ category.title }}</a></li>
  {% empty %}
  <li>There are no categories yet!</li>
  {% endfor %}
</ul>
{% endblock %}

Creating Article & Category Views – Detail Views

We will create the article and category detail views. These views are responsible for showing details about a single article and all the articles in a category.

Add the following lines of code to the blog’s views.py file:

def article_detail(request, article_id):
  article = get_object_or_404(Article, status='published', id=article_id)

  context = {
    'article':article,
  }
  return render(request, 'blog/article-detail.html', context)


def category_detail(request, category_id):
  category = get_object_or_404(Category, status='published', id=category_id)

  category_articles = Article.objects.filter(status='published', category=category)

  context = {
    'category':category,
    'category_articles':category_articles
  }
  return render(request, 'blog/category-detail.html', context)

For the article_detail views which returns a single record (article object):

  • We passed article_id as argument to the article_detail view because we want to use the id field of our model to retrieve an article that matches the id number that will be passed to the URL that is mapped to this views. You can use any field on the model to retrieve a record. In this complete beginners guide to Django, we will use the id field to retrieve an object because it is unique. When you are retrieving an object, always retrieve using a field that has unique=True (the id field is unique by default), this is to avoid the MultipleObjectReturned exception.
  • You can pass any text (it doesn’t matter) as argument to your view function. Here, we are using article_id. See part 4: creating models for more
  • To avoid getting the DoesNotExist exception, we use article = get_object_or_404(Article, status=’published’, id=article_id) – get_object_or_404() which redirects to an error page when an object you are trying to retrieve does not exist. get_object_or_404 is used when retrieving objects alone. The Article argument is the model you want to query. The DoesNotExist exception is raised when you try to retrieve an object that does not exist. See part 6: making queries with the Django ORM for more.
  • Finally we return render, passed the request object, the template to use and the context that is created

The category_detail view returns a single record (category object), then retrieves a queryset of articles that are related to that particular category. This view is the same as the article_detail view except for a few differences:

  • Like the article_detail view, we passed category_id to the category_detail view because we want to retrieve a category object that matches a query.
  • Since the Article model has a ForeignKey relationship to the Category model, then we also retrieved all of the published articles that belong to this category using the filter() method.
  • Note – the get() or get_object_or_404() ORM method is used for retrieving an object, which is equivalent to a single record, while the filter() method is used for retrieving multiple records. The filter() method returns a queryset – multiple results (queryset), while the get() or get_object_or_404() method returns an object – single result (object).

Mapping Article & Category Detail Views to URLs

Update the blog’s urls.py file with the following path:

urlpatterns = [
  # Homepage
  path('', views.homepage, name='homepage'),

  # List view URLS
  path('/blog', views.blog, name='blog'),
  path('/categories, views.categories, name='categories),

  # Detail view URLS
  path('/blog/<int:article_id>/', views.article_detail, name='article_detail'),
  path('/categories/<int:category_id>/', views.category_detail, name='category_detail'),
]

  • Because we have /blog// as the article detail view URL and /categories// as the category detail view URL, it means that we will have to navigate to 127.0.0.1:8000/blog/1/ to access an article detail and 127.0.0.1:8000/categories/1/ to access a category detail.
  • Next we have for article detail and for category_detail. This is called URL capture because it collects or captures what is passed to the URL. Example: if you passed 200 to the URL, then the result will be 127.0.0.1:8000/categories/200/. This is the work of a URL capture, which are and in our case.
  • Inside of the URL capture, there is int: which is a converter. This simply means that whatever data that is coming must be converted to an integer (number). This is very useful in our case since the field we want to retrieve record from is an integer (number) field. There are many other URL captures in Django. Some examples are: str, slug, uuid, etc.
  • Notice the forward slash / at the end of the article_detail and the category_detail URLs. You can decide to omit it.
  • The URL capture must be written exactly the same way as the argument passed to the article_detail view function and category_id URL capture must be written exactly the same way as the argument passed to the category_detail view function.

Note:

  • Anything between two angle brackets is a dynamic path, while anything outside of it is a static path like /blog/ and /categories/.
  • Be sure that each path() you add ends with a comma (,).

If you click on any category or article, you will be greeted again with the TemplateDoesNotExist exception. This is because we have not yet created the article_detail.html and the category_detail.html templates.

Creating Article & Category Detail Templates

The article_detail template is responsible for displaying the detail view of an article, while the category_detail template is responsible for displaying the detail view of a category, as well as the list view, which is a list of articles in that category.

Open the django_project/blog/templates/blog/ folder and create two files:

  • article_detail.html
  • category_detail.html

Your directory structure should look like this:

django_project/
--blog/
----templates/
------blog/
--------article_detail.html
--------base.html
--------blog.html
--------category_detail.html
--------categories.html
--------homepage.html

Copy and paste the following lines of code to the article_detail template:

{% extends 'blog/base.html' %}
{% block title %}{{ article.title }} - Blog{% endblock %}

{% block content %}
<h2 class="text-center">{{ article.title }}</h2>
<div class="row d-flex justify-content-center">
  <div class="cols-8 mb-3">
    <div class="card p-4">
      <p>Created on {{ article.created_at }} updated on {{ article.updated_at }} by {{ article.author.user.username }} category - {{ article.category.title }}</p>
      <img src="{{ article.featured_img.url }}" class="card-img-top" alt="{{ article.title }}"> <br >
      <p>{{ article.content }}</p>
    </div>
  </div>
</div>
{% endblock %}
  • Since we have a base template, then we extend from it as the first thing.
  • We have the title, created_at, updated_at, etc., fields in the Article model. We use the double curly brace syntax – {{ variable_name.field_name }} to display a variable. The status field is not needed to be displayed here because it is an admin status. Notice we looped through the values we had in the homepage.html template and we didn’t here. This is because the homepage view retrieved a queryset while the article_detail view retrieved an object.
  • We added a delete button. This will be used later for performing a delete action.

Add the following lines of code to the category_detail template:

{% extends 'blog/base.html' %}
{% block title %}{{ category.title }} - Blog{% endblock %}

{% block content %}
<h2>Posts in {{ category.title }}</h2>
<div class="row">
  {% for category_article in category_articles %}
  <div class="cols-12 col-md-4 col-lg-3 mb-3">
    <div class="card">
      <img src="{{ category_article.featured_img.url }}" class="card-img-top" alt="{{ category_article.title }}">
      <div class="card-body">
        <h5 class="card-title"><a href="{{ category_article.get_absolute_url }}" title="{{ category_article.title }}">{{ category_article.title }}</a></h5>
        <p class="card-text">{{ category_article.content|truncatewords:20 }}</p>
        <a href="{{ category_article.get_absolute_url }}" title="{{ category_article.title }}"
          class="btn btn-sm btn-primary">Read more</a>
      </div>
    </div>
  </div>
  {% empty %}
  <p>There are no articles yet!</p>
  {% endfor %}
</div>
{% endblock %}
  • Remember the category_detail view retrieved a single category (object stored in the category variable) and all the articles associated with it from the Article model (queryset stored in the article variable). Since the category variable passed to the category_detail.html template is an object, we access the fields using {{ category.field_name }}. For the articles related to this category, we loop through it because it is a queryset.

If your development server is not running, run:

python manage.py runserver

Visit 127.0.0.1:8000/ (if your development server port is deferent from 8000, then replace and continue), click on any article and you will be taken to a detailed page. Also click on any category on the homepage and you will be taken to the category detail page, which shows the title of the category and the articles in that category.

The article detail view template looks like this:

Django - Article Detail

While the category detail view template looks like this:

Django - Category Detail

Creating Article & Category Views – Delete Views

The delete views are responsible for deleting an article or category.

Add the following view functions to the views.py file in the blog app:

def delete_article(request, article_id):
  article = get_object_or_404(Article, status='published', id=article_id)

  if request.method == 'POST':
    article.delete()
    return redirect('homepage')

  context = {
    'article':article,
  }
  return render(request, 'blog/delete-article.html', context)


def delete_category(request, category_id):
  category = get_object_or_404(Category, status='published', id=category_id)

  if request.method == 'POST':
    category.delete()
    return redirect('homepage')

  context = {
    'category':category,
  }
  return render(request, 'blog/delete-category.html', context)
  • We retrieve the article or category we want to delete.
  • After that we check if there is a POST request. This means that a confirmation button will have to be clicked to make a POST request. If there is a POST request then use the Django ORM delete() method to delete the category or article, then redirect to the homepage. POST is an HTTP request method like GET.
  • We are only able to delete articles or categories with the status of published.

Mapping Article & Category Delete Views to URLs

Update the urls.py in the blog app with the following:

urlpatterns = [
  # Homepage
  path('', views.homepage, name='homepage'),

  # List view URLS
  path('/blog', views.blog, name='blog'),
  path('/categories, views.categories, name='categories),

  # Detail view URLS
  path('/blog/<int:article_id>/', views.article_detail, name='article_detail'),
  path('/categories/<int:category_id>/', views.category_detail, name='category_detail'),

  # Delete view URLS
  path('/blog/<int:article_id>/delete/', views.delete_article, name='delete-article'),
  path('/categories/<int:category_id>/delete/', views.delete_category, name='delete-category'),
]

Creating Article & Category Delete Templates

In the blog’s template directory (where other templates are), create two templates:

  • delete-article.html
  • delete-category.html

These templates will serve as a warning before an article or category is deleted.

Add the following lines of code to the delete-article template:

{% extends 'blog/base.html' %}
{% block title %}Deleting {{ article.title }} (Article){% endblock %}

{% block content %}
<div class="row d-flex justify-content-center">
  <div class="cols-8 mb-3">
    <div class="card p-4">
      <h2 class="mb-5">Are you sure you want to delete the {{ article.title }} article?</h2>
      <form method="POST">
        <input type="submit" value="Delete article" class="btn btn-danger w-100">
      </form>
      <div class="mt-3">
        <a href="{% url 'homepage' %}" class="btn btn-outline-secondary w-100">Cancel</a>
      </div>
    </div>
  </div>
</div>
{% endblock %}

Copy and paste the following lines of code to the delete-category template:

{% extends 'blog/base.html' %}
{% block title %}Deleting {{ category.title }} (Category){% endblock %}

{% block content %}
<div class="row d-flex justify-content-center">
  <div class="cols-8 mb-3">
    <div class="card p-4">
      <h2 class="mb-5">Are you sure you want to delete the {{ category.title }} category?</h2>
      <form method="POST">
        <input type="submit" value="Delete category" class="btn btn-danger w-100">
      </form>
      <div class="mt-3">
        <a href="{% url 'homepage' %}" class="btn btn-outline-secondary w-100">Cancel</a>
      </div>
    </div>
  </div>
</div>
{% endblock %}
  • The code contains a form element, a submit button that makes a POST request and a cancel button that takes us back to the homepage without performing any delete action.

Start your development server if it is not running with the following command:

python manage.py runserver

Goto 127.0.0.1:8000/. Click any article or category, then try deleting it.

This is the article delete confirmation page:

Django - Article Delete Confirmation

Category delete confirmation page:

Django - Category Delete Confirmation

Adding a Delete Button to the Articles and Categories Section on the Homepage

We will update the homepage template by adding a delete button to the articles and categories section.

Find the articles for loop section:

{% for article in articles %}
<div class="cols-12 col-md-4 col-lg-3 mb-3">
  <div class="card">
    <img src="{{ article.featured_img.url }}" class="card-img-top" alt="{{ article.title }}">
    <div class="card-body">
      <h5 class="card-title"><a href="{{ article.get_absolute_url }}" title="{{ article.title }}">{{ article.title }}</a></h5>
      <p class="card-text">{{ article.content|truncatewords:20 }}</p>
      <a href="{{ article.get_absolute_url }}" title="{{ article.title}}"
        class="btn btn-sm btn-primary">Read more</a>
    </div>
  </div>
</div>
{% empty %}
<p>There are no articles yet!</p>
{% endfor %}

Update with the following:

{% for article in articles %}
  <div class="cols-12 col-md-4 col-lg-3 mb-3">
    <div class="card">
      <img src="{{ article.featured_img.url }}" class="card-img-top" alt="{{ article.title }}">
      <div class="card-body">
        <h5 class="card-title"><a href="{{ article.get_absolute_url }}" title="{{ article.title }}">{{ article.title }}</a></h5>
        <p class="card-text">{{ article.content|truncatewords:20 }}</p>
        <a href="{{ article.get_absolute_url }}" title="{{ article.title}}"
          class="btn btn-sm btn-primary">Read more</a> | <a href="{{ article.get_delete_url }}" title="{{ article.title}}"
          class="btn btn-sm btn-danger">Delete</a>
      </div>
    </div>
  </div>
  {% empty %}
  <p>There are no articles yet!</p>
  {% endfor %}

For categories, find the section that begins the for loop for categories:

<ul>
  {% for category in categories %}
  <li class="mb-3"><a href="{{ category.get_absolute_url }}" title="{{ category.title }}">{{ category.title }}</a></li>
  {% empty %}
  <li>There are no categories yet!</li>
  {% endfor %}
</ul>

Update it with the following:

<ul>
  {% for category in categories %}
  <li class="mb-3"><a href="{{ category.get_absolute_url }}" title="{{ category.title }}">{{ category.title }}</a> | <a href="{{ category.get_delete_url }}" title="{{ category.title}}"
    class="btn btn-sm btn-danger">Delete</li>
  {% empty %}
  <li>There are no categories yet!</li>
  {% endfor %}
</ul>

Start your development server if it is not running with the following command:

python manage.py runserver

Visit the homepage via 127.0.0.1:8000/. This is what it looks like:

Django Homepage with Delete Button

Creating New Article & New Category Views – Create Views

The new_article and the new_category views are responsible for adding or creating new articles and categories.

Since we will use forms to create articles and categories, then we will create the views with no logic. In the next series (part 8), we will update the views with the complete code after creating forms.

Update views.py with the following lines of code (article and category create views):

def new_article(request):

  context = {
  }
  return render(request, 'blog/new-article.html', context)

  
def new_category(request):

  context = {
  }
  return render(request, 'blog/new-category.html', context)

Mapping Article and Category Create Views to URLs

Update the urls.py in the blog app with the following:

urlpatterns = [
  # Homepage
  path('', views.homepage, name='homepage'),

  # List view URLS
  path('/blog', views.blog, name='blog'),
  path('/categories, views.categories, name='categories),

  # Detail view URLS
  path('/blog/<int:article_id>/', views.article_detail, name='article_detail'),
  path('/categories/<int:category_id>/', views.category_detail, name='category_detail'),

  # Delete view URLS
  path('/blog/<int:article_id>/delete/', views.delete_article, name='delete-article'),
  path('/categories/<int:category_id>/delete/', views.delete_category, name='delete-category'),

  # Create view URLS
  path('/blog/<int:article_id>/new/', views.new_article, name='new-article'),
  path('/categories/<int:category_id>/new/', views.new_category, name='new-category'),
]

Creating Article and Category Create View Templates

In the blog’s template directory (where other templates are), create two templates:

  • new-article.html
  • new-category.html

These templates are responsible for displaying forms for creating articles and categories.

We will update these templates once we create forms in the next series.

Add the following lines of code to the new-article template:

{% extends 'blog/base.html' %}
{% block title %}New Article{% endblock %}

{% block content %}
<h2 class="text-center">New Article</h2>
<div class="row">
  <div class="cols-8 mb-3">
    <div class="card">
      <form method="POST">
        <input type="submit" value="New article" class="btn btn-sm btn-primary w-100">
      </form>
      <div class="mt-3">
        <a href="{% url 'homepage' %}" class="btn btn-outline-secondary w-100">Cancel</a>
      </div>
    </div>3
  </div>
</div>
{% endblock %}

Copy and paste for new-category template:

{% extends 'blog/base.html' %}
{% block title %}New Category{% endblock %}

{% block content %}
<h2 class="text-center">New Category</h2>
<div class="row">
  <div class="cols-8 mb-3">
    <div class="card">
      <form method="POST">
        <input type="submit" value="New category" class="btn btn-sm btn-primary w-100">
      </form>
      <div class="mt-3">
        <a href="{% url 'homepage' %}" class="btn btn-outline-secondary w-100">Cancel</a>
      </div>
    </div>
  </div>
</div>

{% endblock %}

Creating Edit Article & Category Views – Edit Views

The edit_article and the edit_category views will be used for displaying forms for editing articles and categories.

Since we will use forms to edit articles and categories, then we will create the views with no logic. In the next series (part 8), we will update the views with the complete code after creating forms.

Update views.py with the following lines of code (article and categories edit views):

def edit_article(request):

  context = {
  }
  return render(request, 'blog/edit-article.html', context)


def edit_category(request):

  context = {
  }
  return render(request, 'blog/edit-category.html', context)

Mapping Article & Category Edit Views to URLs

Update the urls.py in the blog app with the following:

urlpatterns = [
  # Homepage
  path('', views.homepage, name='homepage'),

  # List view URLS
  path('/blog', views.blog, name='blog'),
  path('/categories, views.categories, name='categories),
  
  # Detail view URLS
  path('/blog/<int:article_id>/', views.article_detail, name='article_detail'),
  path('/categories/<int:category_id>/', views.category_detail, name='category_detail'),

  # Delete view URLS
  path('/blog/<int:article_id>/delete/', views.delete_article, name='delete-article'),
  path('/categories/<int:category_id>/delete/', views.delete_category, name='delete-category'),

  # Create view URLS
  path('/new-article/', views.new_article, name='new-article'),
  path('/categories/new-category/', views.new_category, name='new-category'),

  # Edit view URLS
  path('/blog/<int:blog_id>/edit/', views.edit_article, name='edit-article'),
  path('/categories/<int:category_id>/edit/', views.edit_category, name='edit-category'),
]

Creating Article & Category Edit Templates

In the blog’s template directory (where other templates are), create two templates:

  • edit-article.html
  • edit-category.html

These templates are responsible for displaying forms for editing articles and categories.

We will update these templates once we create forms in the next series.

Add the following lines of code to the edit-article template:

{% extends 'blog/base.html' %}
{% block title %}Edit Article{% endblock %}

{% block content %}
<h2 class="text-center">Edit Article</h2>
<div class="row">
  <div class="cols-8 mb-3">
    <div class="card">
      <form method="POST">
        <input type="submit" value="Edit article" class="btn btn-sm btn-warning w-100">
      </form>
      <div class="mt-3">
        <a href="{% url 'homepage' %}" class="btn btn-outline-secondary w-100">Cancel</a>
      </div>
    </div>
  </div>
</div>

{% endblock %}

Copy and paste for the edit-category template:

{% extends 'blog/base.html' %}
{% block title %}Edit Category{% endblock %}

{% block content %}
<h2 class="text-center">Edit Category</h2>
<div class="row">
  <div class="cols-8 mb-3">
    <div class="card">
      <form method="POST">
        <input type="submit" value="Edit category class="btn btn-sm btn-warning w-100">
      </form>
      <div class="mt-3">
        <a href="{% url 'homepage' %}" class="btn btn-outline-secondary w-100">Cancel</a>
      </div>
    </div>
  </div>
</div>

{% endblock %}

Django Tutorial Series

In this complete beginners guide to Django, we have been able to cover part 1 – part 6. If you need to read them again, or have not read them yet, please see:

In the next complete beginners guide to Django, we will show you:

We hope this complete beginners guide to Django – creating with views (part 7) – creating the list, detail, edit and the delete views. This is a complete beginners guide to Django, so if you haven’t read part 6, then see part 6: making queries (Django ORM) or see part 1 – introduction to Django to get started.

See other of our Django tutorials for more.

You Might Also Like

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Shares