Django 5, Python 3.12,
I’m developing a forum app, its structure roughly is “forum → subforum → topic → comment”. The number of subforum limited by “choices”, inside every subforum only related topics are seen and available, also there was implemented a filter system for topics (filtering inside every subforum separately).
Now it’s time to implement a creation of topics function, so I’ve made corresponding view (AddTopic), template (addtopic.html) and URL (“add_topic/”). I’ve decided to put the link to “add_topic/” to subforum template ("subforum.html), where the list of related topics is displayed, right under the list. But when I run test server and try to click on the “Create new topic” button, it displays an error (production - is one of subforums, available via slug):
Page not found (404)
No Topic found matching the query
Request Method: GET
Request URL: http://127.0.0.1:8000/forum/production/add_topic/
Raised by: forum.views.ShowTopic
Using the URLconf defined in django_project.urls, Django tried these URL patterns, in this order:
admin/
[name='home']
about [name='about']
characters/ [name='characters']
episodes/ [name='episodes']
posts/<slug:post_slug> [name='posts']
characters/<slug:char_slug> [name='chars']
news/
users/
user_page/
forum/ [name='forum']
forum/ <slug:subforum_slug>/ [name='subforum']
forum/ <slug:subforum_slug>/<slug:topic_slug>/ [name='topic']
The current path, forum/production/add_topic/, matched the last one.
I thought I did all according to the tutorial, so I’m struggling to find what I have done wrong. So I ask the community’s help and advice.
views.py:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django_filters.views import FilterView
from core.views import menu
from .filters import TopicFilter
from .forms import AddTopicForm, AddCommentForm
from .models import Subforum, Topic, Comment, Profile
from .utils import DataMixin
class SubForumListView(ListView):
model = Subforum
context_object_name = 'subforum_list'
template_name = "forum/forum.html"
def get_context_data(self, **kwargs):
subforums = Subforum.objects.all()
context = {'subforums': subforums}
return context
class TopicListView(FilterView):
model = Topic
template_name = "forum/subforum.html"
slug_url_kwarg = 'subforum_slug'
context_object_name = 'topics'
filterset_class = TopicFilter
def get_queryset(self):
qs = self.model.objects.all()
if self.kwargs.get('subforum_slug'):
qs = qs.filter(subforum__slug=self.kwargs['subforum_slug'])
return qs
class ShowTopic(DetailView):
model = Topic
template_name = "forum/topic.html"
slug_url_kwarg = 'topic_slug'
context_object_name = 'topic'
def get_context_data(self, **kwargs):
topic = get_object_or_404(Topic, slug=self.kwargs['topic_slug'])
comments = Comment.objects.filter(topic=topic)
comments_number = len(Comment.objects.filter(topic=topic))
context = {'menu': menu,
'topic': topic,
'comments': comments,
'comm_num': comments_number}
return context
class AddTopic(DataMixin, CreateView):
form_class = AddTopicForm
template_name = 'forum/addtopic.html'
page_title = 'Create a new topic'
class AddComment(LoginRequiredMixin, DataMixin, CreateView):
form_class = AddCommentForm
template_name = 'forum/addcomment.html'
page_title = 'Оставить комментарий'
success_url = reverse_lazy('topic')
class UpdateComment(LoginRequiredMixin, DataMixin, UpdateView):
form_class = AddCommentForm
template_name = 'forum/addcomment.html'
page_title = 'Редактировать комментарий'
success_url = reverse_lazy('topic')
subforum.html:
{% extends 'base.html' %}
{% block content %}
<h1>{{ subforum.title }}</h1>
{% block sidebar %}
<div class="container">
<div class="container-posts">
<form method="get">
{% csrf_token %}
{{ filter.form.as_p }}
<button type="submit">Search</button>
</form>
</div>
</div>
{% endblock %}
<div class="container-posts">
<h3>Обсуждения</h3>
<div class="row">
{% for t in topics %}
<div class="card-body">
<a href="{{ t.get_absolute_url }}">{{ t.subject }}</a>
</div>
{% endfor %}
</div>
</div>
<div class="container-posts">
<a href="add_topic"><button>Create a new topic</button></a>
</div>
{% endblock %}
addtopic.html:
{% extends 'base.html' %}
{% block content %}
<h1>New topic</h1>
<form action="" method="post">
{% csrf token %}
<div class="form-error">{{ form.non_field_errors }}</div>
{% for f in form %}
<p><label class="form-label" for="{{ f.id_for_label }}">{{ f.label }}</label>{{ f }}</p>
<div class="form-error">{{ f.errors }}</div>
{% endfor %}
<p><button type="submit">Create topic</button></p>
</form>
{% endblock %}
models.py:
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.text import slugify
from .consts import *
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
surname = models.CharField(max_length=32, default='')
name = models.CharField(max_length=32, default='')
email = models.EmailField(max_length=254, blank=True, unique=True)
bio = models.TextField(max_length=500, default="Write a couple of words about yourself")
avatar = models.ImageField(default=None, blank=True, max_length=255)
status = models.CharField(max_length=25, blank=True, default='')
slug = models.SlugField()
age = models.IntegerField(verbose_name='Возраст', null=True, blank=True)
gender = models.CharField(verbose_name='Пол', max_length=32, choices=Genders.GENDER_CHOICES, default="H", blank=True)
reputation = models.IntegerField(verbose_name='Репутация', default=0)
def __str__(self):
return f'{self.user} profile'
def get_absolute_url(self):
return reverse('forum:user_profile', kwargs={'profile_slug': self.slug})
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.user.username)
return super(Profile, self).save(*args, **kwargs)
class Subforum(models.Model):
title = models.CharField(verbose_name='Название', max_length=32, choices=Theme.THEME_CHOICES, default=1)
slug = models.SlugField(default='News')
objects = models.Manager()
class Meta:
ordering = ['title']
verbose_name = 'Разделы форума'
verbose_name_plural = 'Разделы форума'
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
return super(Subforum, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('forum:subforum', kwargs={'subforum_slug': self.slug})
class Topic(models.Model):
subject = models.CharField(verbose_name='Заголовок', max_length=255, unique=True)
first_comment = models.TextField(verbose_name='Сообщение', max_length=2000, default='')
slug = models.SlugField(default='', unique=True, max_length=25, editable=False)
subforum = models.ForeignKey('Subforum',
verbose_name='Раздел',
on_delete=models.CASCADE,
related_name='subforum')
creator = models.ForeignKey(User,
verbose_name='Создатель темы',
on_delete=models.SET('deleted'),
related_name='creator')
created = models.DateTimeField(auto_now_add=True)
closed = models.BooleanField(default=False)
objects = models.Manager()
class Meta:
ordering = ['id']
verbose_name = 'Обсуждения'
verbose_name_plural = 'Обсуждения'
def __str__(self):
return self.subject
def save(self, *args, **kwargs):
if not self.id:
self.slug = f'topic-{slugify(self.subject)}'
return super(Topic, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('forum:topic', kwargs={'topic_slug': self.slug, 'subforum_slug': self.subforum.slug})
class Comment(models.Model):
topic = models.ForeignKey('Topic',
verbose_name='Тема',
on_delete=models.CASCADE,
related_name='comments')
author = models.ForeignKey(User,
verbose_name='Комментатор',
on_delete=models.SET('deleted'),
related_name='author')
content = models.TextField(verbose_name='Текст', max_length=2000)
created = models.DateTimeField(verbose_name='Дата публикации', auto_now_add=True)
updated = models.DateTimeField(verbose_name='Дата изменения', auto_now=True)
objects = models.Manager()
class Meta:
ordering = ['created']
verbose_name = 'Комментарии'
verbose_name_plural = 'Комментарии'
def __str__(self):
return f'Post of {self.topic.subject} is posted by {self.author.username}.'
forum/urls.py:
from django.urls import path
from forum.views import *
app_name = 'forum'
urlpatterns = [
path('', SubForumListView.as_view(), name='forum'),
path('<slug:subforum_slug>/', TopicListView.as_view(), name='subforum'),
path('<slug:subforum_slug>/<slug:topic_slug>/', ShowTopic.as_view(), name='topic'),
path('<slug:subforum_slug>/add_topic/', AddTopic.as_view(), name="add_topic"),
path('<slug:subforum_slug>/<slug:topic_slug>/add-comment/', AddComment.as_view(), name="add_comment"),
path('<slug:subforum_slug>/<slug:topic_slug>/edit/<int:id>/', UpdateComment.as_view(), name="edit_comment"),
]
project/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('core.urls')),
path('news/', include('core.urls')),
path('users/', include('users.urls', namespace="users")),
path('user_page/', include('users.urls')),
path('forum/', include('forum.urls', namespace="forum")),
]
If any additional info is required, I’m ready to provide it.