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