简体   繁体   中英

Django urls and views properly defined, still showing 404 error

I have a blog which was running without any error. Different users can register, login, and post articles. Users can write/edit/delete comments on the articles. Previously, the URL of the articles were decided using 'article id'. Later, I read this tutorial and added slug along with id in the URL for every article's Detail View. Then I deleted my old articles since they didn't have any slugs.

After this change, a user can create a comment successfully, but cannot 'edit' or 'delete' any comment. Whenever 'edit' or 'delete' button is pressed, 404 error is shown. See the error image by pressing this link .

Page not found (404)

Request Method: GET

Request URL: http://localhost:8000/articles/33/comment_edit/

Raised by: articles.views.ArticleDetailView

No article found matching the query

My corresponding files are listed below:

urls.py (inside Articles App directory)

from django.urls import path
from .views import (ArticleListView, ArticleUpdateView, ArticleDetailView, ArticleDeleteView, ArticleCreateView, 
                    CommentCreateView, CommentUpdateView, CommentDeleteView)

urlpatterns = [
    path('', ArticleListView.as_view(), name='article_list'),
    path('<int:pk>/<str:slug>/edit/', ArticleUpdateView.as_view(), name='article_edit'),
    path('<int:pk>/<str:slug>/', ArticleDetailView.as_view(), name='article_detail'),
    path('<int:pk>/<str:slug>/delete/', ArticleDeleteView.as_view(), name='article_delete'),
    path('new/', ArticleCreateView.as_view(), name='article_new'),
    #Comments below
    path('<int:pk>/<str:slug>/comment/', CommentCreateView.as_view(), name='comment_new'),
    path('<int:pk>/comment_edit/', CommentUpdateView.as_view(), name='comment_edit'),
    path('<int:pk>/comment_delete/', CommentDeleteView.as_view(), name='comment_delete'),
]

I have tried and run the website by commenting out the url for comments, ie, the last three lines above. The error shown is the same. This is strange.

urls.py (inside Project directory)

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', include('users.urls')),
    path('users/', include('django.contrib.auth.urls')),
    path('', include('pages.urls')),
    path('articles/', include('articles.urls')),
]

views.py (inside Articles App directory)

from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, DeleteView, CreateView
from django.urls import reverse_lazy
from .models import Article, Comment

# Create your views here.
class ArticleListView(ListView):
    model = Article
    template_name = 'article_list.html'
    ordering = ['-date']

class ArticleDetailView(DetailView):
    model = Article
    template_name = 'article_detail.html'
    query_pk_and_slug = True

class ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, SuccessMessageMixin, UpdateView):
    model = Article
    fields = ('title', 'body',)
    template_name = 'article_edit.html'
    login_url = 'login'
    #def test_func for UserPassesTestMixin
    def test_func(self):
        obj = self.get_object()
        return obj.author == self.request.user
    success_message = 'Article was updated successfully!'        

class ArticleDeleteView(LoginRequiredMixin, UserPassesTestMixin, SuccessMessageMixin, DeleteView):
    model = Article
    template_name = 'article_delete.html'
    success_url = reverse_lazy('article_list')
    login_url = 'login'
    def test_func(self):
        obj = self.get_object()
        return obj.author == self.request.user
    success_message = 'Article was deleted successfully!'

class ArticleCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
    model = Article
    fields = ('title', 'body')  
    template_name = 'article_new.html'
    login_url = 'login' #for LoginRequiredMixin, overriding default login url at accounts/login
    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)
    success_message = 'Article was created successfully!'    

#Now for the feature of adding comments on listview and detailview of articles
class CommentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
    model = Comment
    fields = ('comment',)
    template_name = 'comment_new.html'
    login_url = 'login'
    query_pk_and_slug = True
    def form_valid(self, form):
        form.instance.author = self.request.user
        form.instance.article = Article.objects.get(pk=self.kwargs['pk'])
        return super().form_valid(form)
    success_message = 'Comment was posted successfully!'    

class CommentUpdateView(LoginRequiredMixin, UserPassesTestMixin, SuccessMessageMixin, UpdateView):
    model = Comment
    fields = ('comment',)
    template_name = 'comment_edit.html'
    login_url = 'login'
    #def test_func for UserPassesTestMixin
    def test_func(self):
        obj = self.get_object()
        return obj.author == self.request.user
    success_message = 'Comment was updated successfully!'


class CommentDeleteView(LoginRequiredMixin, UserPassesTestMixin, SuccessMessageMixin, DeleteView):
    model = Comment
    template_name = 'comment_delete.html'
    success_url = reverse_lazy('article_list')
    login_url = 'login'
    def test_func(self):
        obj = self.get_object()
        return obj.author == self.request.user
    success_message = 'Comment was deleted successfully!'    

models.py (inside Articles App directory)

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse
from django.utils.text import slugify

User = get_user_model()
# Create your models here.
class Article(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField(
        default='',
        editable=False,
        max_length=255,
    )
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
    )
    def __str__(self):
        return self.title

    def get_absolute_url(self):
        kwargs = {
            'pk': self.id,
            'slug': self.slug
        }
        return reverse("article_detail", kwargs=kwargs)
    def save(self, *args, **kwargs):
        value = self.title
        self.slug = slugify(value, allow_unicode=True)
        super().save(*args, **kwargs)


class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
    comment = models.TextField()
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
    )
    def __str__(self):
        return self.comment

    def get_absolute_url(self):
        kwargs = {
            'pk': self.article.id,
            'slug': self.article.slug
        }
        return reverse("article_detail", kwargs=kwargs)

The links for creating a comment, editing a comment, and deleting a comment in any html file looks like this:

<a href="{% url 'comment_new' article.pk article.slug %}">Add Comment</a>
<a href="{% url 'comment_edit' comment.pk %}">Edit Comment</a>
<a href="{% url 'comment_delete' comment.pk %}">Delete Comment</a>

In my settings.py file, ALLOWED_HOSTS = ['*'] .

Kindly help me with this problem. I have searched online but didn't find any solution.

The important part of the error message is

Raised by: articles.views.ArticleDetailView

You are trying to edit a comment at /articles/33/comment_edit/ , but it is matching the URL pattern:

path('<int:pk>/<str:slug>/', ArticleDetailView.as_view(), name='article_detail'),

This gives a 404, because you don't have an article with pk=33 and slug='comment_edit'.

You could fix the issue by moving the CommentUpdateView above ArticleDetailView so that it matches first.

Have you added your app name inside settings.py ? Make sure you have added the name in settings.py which resides under the project folder.

Like so,

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app_name',] # <--- WhatEver is called 

And also make sure you are using your routes you have defined to access the correct page

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM