Django 3: ValidationError не отображается

Недавно я начал изучать фреймворк Django, и у меня возникли проблемы с проверкой переменных. Когда я пытаюсь вывести переменную field.errors в html-файле, вывода нет, хотя, когда я передаю переменную ошибки из метода создания, он выводит список «ошибок» (т.е. метод clean_number должен работать), и когда я их исправляю, список получает меньше (т.е. метод clean_number не работает). Пожалуйста, скажите мне, что я делаю неправильно. Заранее спасибо.

Файл create.html

<!doctype html>
<html lang = "ru">
<head>
    <meta charset = "UTF-8">
    <meta name = "viewport>
          content = "width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv = "X-UA-Compatible" content = "ie=edge">
    <title>Adding a train to the list</title>
    <link href = "https://getbootstrap.com/docs/5.3/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin = "anonymous">

  <style>
    .sidenav {
      height: 100%;
      width: 267px;
      position: fixed;
      z-index: 1;
      top: 0;
      left: 0;
      background-color: #f2f2f2;
      overflow-x: hidden;
      padding-top: 50px;
    }

    .container {
      max-width: 900px;
    }
  </style>
</head>
<body>
<body class = "bg-light">

<header class = "navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
  <a class = "navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href = "/">Home page</a>
  <button class = "navbar-toggler position-absolute d-md-none collapsed" type = "button" data-bs-toggle = "collapse" data-bs-target = "#sidebarMenu" aria-controls = "sidebarMenu" aria-expanded = "false" aria-label = "Toggle navigation">
    <span class = "navbar-toggler-icon"></span>
  </button>
  <input class = "form-control form-control-dark w-100 rounded-0 border-0" type = "text" placeholder = "Search" aria-label = "Search">
  <div class = "navbar-nav">
    <div class = "nav-item text-nowrap">
      <a class = "nav-link px-3"></a>
    </div>
  </div>
</header>

<div class = "container">
  <main>
    <div class = "row g-5">
      <div class = "py-5 text-center">
        <h2>Add a train</h2>
          <p class = "lead">On this page, you need to fill in all the fields and follow the instructions.</p>
            <form method = "post">
            <div class = "row g-3">
            {% csrf_token %}
            {% for field in form %}
              {{ field }}
                <div class = "alert alert-danger">
                  {{ field.errors }}
                  {% if !field.errors %}
                    {{ error }}
                  {% endif %}
                </div>
            {% endfor %}
            <hr class = "my-4">
              <button class = "w-100 btn btn-primary btn-lg" type = "submit">Save changes</button>
            </div>
            </form>
      </div>
    </div>
  </main>
</div>
</body>
</body>
</html>

Файл Forms.py

from .models import Train
from django.forms import ModelForm, NumberInput
from django.core.exceptions import ValidationError


class TrainForm(ModelForm):
    class Meta:
        model = Train
        fields = ["number", "amount_stops"]
        widgets = {
            "number": NumberInput(attrs = {
                'class': 'form-control',
                'placeholder': 'Enter the train number',
            }),
            "amount_stops": NumberInput(attrs = {
                'class': 'form-control',
                'placeholder': 'Enter the number of stops'
            })
        }

    def clean_number(self):
        number = self.cleaned_data['number']
        if number > 100:
            raise ValidationError('The number is over 100!')
        return number

    def clean_amount_stops(self):
        amount_stops = self.cleaned_data['amount_stops']
        if amount_stops > 100:
            raise ValidationError('The amount of stops is over 100!')
        return amount_stops

Файл views.py

from django.shortcuts import render, redirect
from .forms import TrainForm


def create(request):
    error = ''
    if request.method == 'POST':
        form = TrainForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('/')
        else:
            error = form.errors

    form = TrainForm()
    context = {
        'form': form,
        'error': error
    }
    return render(request, 'main/create.html', context)

Сменил шаблон, но все равно ничего не изменилось.

<!doctype html>
<html lang = "en">
<head>
    <meta charset = "UTF-8">
    <meta name = "viewport"
          content = "width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv = "X-UA-Compatible" content = "ie=edge">
    <title>Document</title>
</head>
<body>
<form method = "post">
    {% csrf_token %}
      {% for field in form %}
        {{ field }}<p>
        {{ field.errors }}<p>
      {% endfor %}
  <button type = "submit">Save</button>
</form>
</body>
</html>

============================

02.10.23: Пробовал пересоздать проект без использования PyCharm IDE, просто с помощью Windows PowerShell, но ничего не изменилось, так как не отображается и не показывает(. Создание моего проекта:

  1. Создание папки AppDjango
  2. В терминале PowerShell:
-> cd D:\AppDjango\ - Navigate to Project
-> PS D:\AppDjango> python -m venv virenv
-> PS D:\AppDjango> virenv\Scripts\Activate.ps1
-> (virenv) PS D:\AppDjango> python -m pip install Django
-> (virenv) PS D:\AppDjango> django-admin startproject AppDjango
-> (virenv) PS D:\AppDjango> cd AppDjango 
-> (virenv) PS D:\AppDjango\AppDjango> python manage.py runserver
-> (virenv) PS D:\AppDjango\AppDjango> ^C (Ctrl + C) - Finish process
-> (virenv) PS D:\AppDjango\AppDjango> python manage.py startapp MainApp
-> (virenv) PS D:\AppDjango\AppDjango> python manage.py migrate
  1. Далее я изменил конфигурацию файла settings.py:
import os                   <- Only added
...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'MainApp'               <- Only added
]
...
TEMPLATES = [
    {
        ...
        'DIRS': [os.path.join(BASE_DIR,'templates'),], <- Only changed
        ...
    },
]
  1. Я добавил таблицу в файл models.py:
from django.db import models


class Train(models.Model):
    number = models.IntegerField()
    amount_stops = models.IntegerField()

    def __str__(self):
        return str(self.number)
  1. Я создал файл forms.py и добавил код:
from .models import Train
from django.forms import ModelForm, NumberInput
from django.core.exceptions import ValidationError


class TrainForm(ModelForm):
    class Meta:
        model = Train
        fields = ["number", "amount_stops"]
        widgets = {
            "number": NumberInput(attrs = {
                'class': 'form-control',
                'placeholder': 'Enter the train number',
            }),
            "amount_stops": NumberInput(attrs = {
                'class': 'form-control',
                'placeholder': 'Enter the number of stops'
            })
        }

    def clean_number(self):
        number = self.cleaned_data['number']
        if number > 100:
            raise ValidationError('The number is over 100!')
        return number

    def clean_amount_stops(self):
        amount_stops = self.cleaned_data['amount_stops']
        if amount_stops > 100:
            raise ValidationError('The amount of stops is over 100!')
        return amount_stops
  1. Я добавил код в файл views.py:
from django.shortcuts import render, redirect
from .forms import TrainForm


def create(request):
    errors_Main = ''
    if request.method == 'POST':
        form = TrainForm(request.POST)
        if form.is_valid():
            form.save()
        else:
            errors_Main = form.errors
    form = TrainForm()
    context = {
        'form': form,
        'errors_Main': errors_Main
    }
    return render(request, 'MainApp/create.html', context)
  1. Я создал новый файл D:\AppDjango\AppDjango\MainApp\urls.py и добавил код:
from django.urls import path
from . import views


urlpatterns = [
    path('', views.create, name='create')
]
  1. Я также изменил файл D:\AppDjango\AppDjango\AppDjango\urls.py (тот, который изначально сгенерировал Django):
from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('MainApp.urls'))
]
  1. Я создал файл шаблона .\MainApp\templates\MainApp\create.html и добавил код:
<!doctype html>
<html lang = "en">
<head>
    <meta charset = "UTF-8">
    <meta name = "viewport"
          content = "width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv = "X-UA-Compatible" content = "ie=edge">
    <title>Document</title>
</head>
<body>
<form method = "post">
    {% csrf_token %}
      {% for field in form %}
        {{ field }}<p>
        {% for error in field.errors %} <- added
          {{ error }}
        {% endfor %}
      {% endfor %}
  <button type = "submit">Save</button>
</form>
</body>
</html>
  1. Последние изменения в терминале PowerShell:
-> (virenv) PS D:\AppDjango\AppDjango> python manage.py makemigrations
Migrations for 'MainApp':
  MainApp\migrations\0001_initial.py
    - Create model Train
-> (virenv) PS D:\AppDjango\AppDjango> python manage.py migrate
Operations to perform:
  Apply all migrations: MainApp, admin, auth, contenttypes, sessions
Running migrations:
  Applying MainApp.0001_initial... OK
-> (virenv) PS D:\AppDjango\AppDjango> python manage.py runserver

Результат: по-прежнему не отображает ошибки. Подскажите, пожалуйста, на каком этапе я накосячил?

🤔 А знаете ли вы, что...
Python подходит для начинающих программистов благодаря своей простоте и читаемости кода.


2
71
2

Ответы:

Попробуйте использовать циклический синтаксис для отображения ошибок, поэтому вместо только {{field.errors}} используйте его следующим образом:

{% for error in field.errors %}
  {{ error }}
{% endfor %}

Решено

Спасибо @Sunderam Dubey за помощь. Я нашел ответ на свой вопрос. Проблема была в методе create, я его переписал немного по другому (код ниже) и смог вывести текст ошибки:

def create(request):
    form = TrainForm()
    if request.method == "POST":
        form = TrainForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('/')
    return render(request, "main/create.html", {"form": form})