python – Django does not display errors from the custom validator, and instead resets the form

I recently started learning Django and can’t solve one problem. I created my validator for the form, but instead of showing an error window, it just resets the form.

Here is the code models.py:

from django.db import models
from django.urls import reverse_lazy
from django.core.exceptions import ValidationError

class News(models.Model):
    title = models.CharField(max_length=150, verbose_name="Title")
    content = models.TextField(blank=True, verbose_name="Content")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Date of publication")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Update")
    photo = models.ImageField(upload_to='photos/%Y%m/%d/', verbose_name="Photo", blank=True)
    is_published = models.BooleanField(default=True, verbose_name="Is_published ")
    category = models.ForeignKey('Category', on_delete=models.PROTECT, null=True, verbose_name="Category")

    def get_absolute_url(self):
        return reverse_lazy('read', kwargs={'news_id' : self.pk})

    def __str__(self):
        return self.title

    class Meta:
        verbose_name="new"
        verbose_name_plural="news"
        ordering = ['-created_at', 'title']



class Category(models.Model):
    title = models.CharField(max_length=150, db_index=True, verbose_name="Title of category")

    def get_absolute_url(self):
        return reverse_lazy('category', kwargs={'pk' : self.pk})

    def __str__(self):
        return self.title

    class Meta:
        verbose_name="category"
        verbose_name_plural="categories"
        ordering = ['-title']

This is the code forms.py:

from django import forms
from .models import News, Category
from django.core.exceptions import ValidationError
import re

class NewsForm(forms.Form):
    title = forms.CharField(max_length=150, min_length=1, label="Title", widget=forms.TextInput(attrs={'class' : 'form-control'}))
    content = forms.CharField(label="Text", required=False, widget=forms.Textarea(attrs={'class' : 'form-control', 'rows' : 15}))
    # photo = forms.ImageField(upload_to='photos/%Y%m/%d/')
    is_published = forms.BooleanField(label="To publish", initial=True)
    category = forms.ModelChoiceField(label="Category", queryset=Category.objects.all(), empty_label="Select a category", widget=forms.Select(attrs={'class' : 'form-control'}))

    def clean_title(self):
        raise ValidationError('Error!')



# class NewsForm(forms.ModelForm):
#     class Meta:
#         model = News
#         fields = ['title', 'content', 'is_published', 'category']
#         widgets = {
#             'title' : forms.TextInput(attrs={'class' : 'form-control'}),
#             'content' : forms.Textarea(attrs={'class' : 'form-control', 'rows' : 15}),
#             'category' : forms.Select(attrs={'class' : 'form-control'})
#         }

And here is the code views.py:

from django.shortcuts import render, get_object_or_404, redirect
from .models import News, Category
from .forms import NewsForm


def news(request):
    news = News.objects.all()

    return render(request, 'news/news.html', {'news': news, 'title': 'List of news:'})


def get_category(request, pk):
    news = News.objects.filter(category_id=pk)

    return render(request, 'news/category.html',
                  {'news': news, 'title': f'List of news in the {str(Category.objects.get(pk=pk))} category :'})

def view_news(request, news_id):
    back = request.GET.get('back')
    new = get_object_or_404(News, pk=news_id)

    return render(request, 'news/read_news.html', {'new' : new, 'back' : back})

def add_news(request):
    if request.method == 'POST':
        form = NewsForm(request.POST)
        if form.is_valid():
            new = News.objects.create(**form.cleaned_data)
            # new = form.save()
            return redirect(str(new.get_absolute_url())+'?back=False')

    return render(request, 'news/add_news.html', {'form' : NewsForm() })

Here is the code urls.py:

from django.urls import path

from .views import *

urlpatterns = [
    path('news/', news, name="news_all"),
    path('news/category/<int:pk>/', get_category, name="category"),
    path('news/read/<int:news_id>/', view_news, name="read"),
    path('news/add-news/', add_news, name="add"),
]

Code news_tags.py:

from django import template

from ..models import Category

register = template.Library()

@register.simple_tag(name="g_cat")
def get_categories():
    return Category.objects.all()

@register.inclusion_tag('news/tags/news_tag.html', name="news_tag")
def news_tag(news, link=False):
    return {'news' : news, 'link' : link}

Code news_tag.html:

<div class="col-md-9">
    {% for item in news %}
        <div class="card mb-3">
            <div class="card-header">
                {% if link %}
                    Category: <a href="{{ item.category.get_absolute_url }}">{{ item.category.title }}</a>
                {% else %}
                    Category: {{ item.category.title }}
                {% endif %}
            </div>
            <div class="card-body">
                <div>
                    {% if item.photo %}
                        <img src="{{ item.photo.url }}" alt="" class="news-media-photo">
                    {% endif %}
                    <div class="media-body">
                        <h5 class="card-title" style="color: {% cycle 'red' 'green' %}">{{ forloop.revcounter }}. {{ item.title }}</h5>
                        <p class="card-text">{{ item.content|linebreaks|truncatewords:50 }}</p>
                        <a href="{{ item.get_absolute_url }}" class="btn btn-primary">Читать дальше...</a>
                    </div>
                </div>
            </div>
            <div class="card-footer text-muted">
                {{ item.created_at|timesince }} назад...
            </div>
        </div>
    {% empty %}
        <h2>Ooops...</h2>
    {% endfor %}
</div>

Here is the code base.html:

<!doctype html>
{% load static %}
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">

    <title>{% block title %}News:{% endblock %}</title>
</head>
<body>

{% block navbar %}
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="/">Navbar</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
                aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
    
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item"><a class="nav-link" href="{% url 'news_all' %}">Главная</a></li>
                <li class="nav-item"><a class="nav-link" href="{% url 'add' %}">Add new</a></li>
            </ul>
        </div>
    </nav>
{% endblock %}

<div class="container mt-3">
    <h2>{% block list_title %}News:{% endblock %}</h2>
    <div class="row">
        {% block sidebar %}
            <div class="col-md-3">
                {% load news_tags %}
                <div class="list-group">
                    {% g_cat as g_cat %}
                    <a href="{% url 'news_all' %}" class="list-group-item list-group-item-action">Все</a>
                    {% for i in g_cat %}
                    <a href="{{ i.get_absolute_url }}" class="list-group-item list-group-item-action">
                        {{ i.title }}
                    </a>
                    {% endfor %}
                </div>
            </div>
        {% endblock %}

        {% block content %}
        {% endblock %}
    </div>
</div>

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
        integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js"
        integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
        crossorigin="anonymous"></script>
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
</body>
</html>

Here is the code add_news.html:

{% extends 'base.html' %}

{% block title %}Add new{% endblock %}

{% block list_title %}
<p align="center">{{ new.title }}</p>
{% endblock %}

{% block navbar %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="/">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item"><a class="nav-link" href="{% url 'news_all' %}">Home</a></li>
            <li class="nav-item"><a class="nav-link" href="#" onclick="history.back();return false;">Cancel</a></li>
        </ul>
    </div>
</nav>
{% endblock %}

{% block sidebar %}{% endblock %}

{% block content %}
<div class="col-md-12">
    <h1>Add new</h1>
    <form method="post">
        {% csrf_token %}


        {{ form.as_p }}
        {% comment %}
        {{ form.non_field.errors  }}
        {% for i in form %}
        <div class="form-group">
            {{ i.label_tag }}
            {{ i }}
            <div class="invalid-feedback">
                {{ i.errors }}
            </div>
        </div>
        {% endfor %}


        {{ form.non_field.errors  }}
        <div class="form-group">
            <label for="{{ form.title.id_for_label }}">Title</label>
            {{ form.title }}
            <div class="invalid-feedback">
                {{ form.title.errors }}
            </div>
        </div>

        <div class="form-group">
            <label for="{{ form.content.id_for_label }}">Text</label>
            {{ form.content }}
            <div class="invalid-feedback">
                {{ form.content.errors }}
            </div>
        </div>

        <div class="form-group">
            <label for="{{ form.is_published.id_for_label }}">To publish</label>
            {{ form.is_published }}
            <div class="invalid-feedback">
                {{ form.is_published.errors }}
            </div>
        </div>

        <div class="form-group">
            <label for="{{ form.category.id_for_label }}">Category</label>
            {{ form.category }}
            <div class="invalid-feedback">
                {{ form.category.errors }}
            </div>
        </div>
        {% endcomment %}

        <button type="submit" class="btn btn-primary btn-block">Add new</button>
    </form>
</div>
{% endblock %}

Code read_news.html:

{% extends 'base.html' %}

{% block title %}Новость - {{ new.title }}{% endblock %}

{% block list_title %}
    <p align="center">{{ new.title }}</p>
{% endblock %}

{% block navbar %}
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="/">Navbar</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
                aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item"><a class="nav-link" href="{% url 'news_all' %}">Главная</a></li>
                {%if back is None %}
                    <li class="nav-item"><a class="nav-link" href="#" onclick="history.back();return false;">Back</a></li>
                {% elif back == False %}
                    <li class="nav-item"><a class="nav-link" href="/news/news/">Back</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>
{% endblock %}

{% block sidebar %}{% endblock %}

{% block content %}
<div class="col-md-12">
    <div class="card mb-3">
        <div class="card-header">
            Category: {{ new.category.title }}
        </div>
        <div class="card-body">
            <div>
                {% if new.photo %}
                    <img src="{{ new.photo.url }}" alt="" height="250" class="news-media-photo">
                {% endif %}
                <div class="media-body">
                    <p class="card-text">{{ new.content|linebreaks }}</p>
                </div>
            </div>
        </div>
        <div class="card-footer text-muted">
            {{ new.created_at|timesince }} ago...
        </div>
    </div>
</div>
{% endblock %}

Here is the code news.html:

{% extends 'base.html' %}

{% block title %}{{ title }}{% endblock %}

{% block list_title %}
    {{ title }}
{% endblock %}

{% block content %}
{% load news_tags %}
{% news_tag news True %}
{% endblock %}

Here is the code category.html:

{% extends 'base.html' %}

{% block title %}
    {{ title }}
{% endblock %}

{% block list_title %}
    {{ title }}
{% endblock %}

{% block content %}
{% load news_tags %}
{% news_tag news %}
{% endblock %}

Leave a Comment