Search code examples
djangodjango-formsdjango-templatesdjango-widget-tweaks

Django form doesn't show error on template page


My code is exactly the same as https://simpleisbetterthancomplex.com/series/2017/09/18/a-complete-beginners-guide-to-django-part-3.html#rendering-bootstrap-forms this. But when I click on the "post" button in the template it shows me the same page without the errors like the field is required.

In my virtualenv, Python 3.7.4, Django 2.2.7 and I've installed Django-widgets-improved.

//view

def new_topic(request, pk):
    board = get_object_or_404(Board, pk=pk)
    user = User.objects.first()

    if request.method == 'POST':
        form = NewTopicForm(request.POST)
        if form.is_valid():
            topic = form.save(commit=False)
            topic.board = board
            topic.starter = user
            topic.save()

            post = Post.objects.create(
                message = form.cleaned_data.get('message'),
                topic = topic,
                created_by = user
            )
            return redirect('board_topics', pk = board.pk)

    form = NewTopicForm()
    return render(request, 'new_topic.html', {'board': board, 'form': form})

//form html

   <form method="post" novalidate>
            {% csrf_token %}
            {{ form.non_field_errors }}
            {{ form.errors }}
            {{ form.as_p }}

            <button type="submit" class="btn btn-success">Post</button>
        </form>

//models.py

from django.db import models
from django.contrib.auth.models import User


class Board(models.Model):
    name = models.CharField(max_length=25, unique=True)
    description = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Topic(models.Model):
    subject = models.CharField(max_length=255)
    last_updated = models.DateTimeField(auto_now_add=True)
    board = models.ForeignKey(Board, on_delete=models.CASCADE, related_name='topics')
    starter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='topics')


class Post(models.Model):
    message = models.TextField(max_length=4000)
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='posts')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(null=True)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    updated_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='+')

//forms.py

from django import forms
from .models import Topic


class NewTopicForm(forms.ModelForm):
    message = forms.CharField(
        widget=forms.Textarea(
            attrs={'rows':5, 'placeholder':'What is in your mind?'}
        ), 
        max_length=4000,
        help_text='The max length of the text is 4000.')

    class Meta:
        model = Topic
        fields = ['subject', 'message']

//main/urls.py

from django.contrib import admin
from django.urls import path
from boards import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home, name='home'),
    path('boards/<int:pk>/', views.board_topics, name='board_topics'),
    path('boards/<int:pk>/new/', views.new_topic, name='new_topic'),
]

Solution

  • Inital form form = NewTopicForm() must be declared before if request.method == 'POST': or in else: condition. In your case the clean form always arrives at the template.

    def new_topic(request, pk):
        board = get_object_or_404(Board, pk=pk)
        user = User.objects.first()
    
        form = NewTopicForm()  # <---
    
        if request.method == 'POST':
            form = NewTopicForm(request.POST)
            if form.is_valid():
                topic = form.save(commit=False)
                topic.board = board
                topic.starter = user
                topic.save()
    
                post = Post.objects.create(
                    message = form.cleaned_data.get('message'),
                    topic = topic,
                    created_by = user
                )
                return redirect('board_topics', pk = board.pk)
    
        return render(request, 'new_topic.html', {'board': board, 'form': form})
    

    or

    def new_topic(request, pk):
        board = get_object_or_404(Board, pk=pk)
        user = User.objects.first()
    
        if request.method == 'POST':
            form = NewTopicForm(request.POST)
            if form.is_valid():
                topic = form.save(commit=False)
                topic.board = board
                topic.starter = user
                topic.save()
    
                post = Post.objects.create(
                    message = form.cleaned_data.get('message'),
                    topic = topic,
                    created_by = user
                )
                return redirect('board_topics', pk = board.pk)
        else:
            form = NewTopicForm()  # <---
    
        return render(request, 'new_topic.html', {'board': board, 'form': form})