Post matching query does not exist

I am getting this error page only when I set the url to “posts/read-later”.

This is my urls.py: -

from django.urls import path
from . import views

urlpatterns = [
    path('', views.IndexView.as_view(), name="index"),
    path('posts/', views.AllPostsView.as_view(), name="posts"),
    path('posts/<slug:slug>', views.PostDetailView.as_view(), name="post-detail"),
    path('posts/read-later', views.ReadLaterView.as_view(), name="read-later"),
]

The screenshot of the error page is:-

If I change the url to “read-later”, then the error doesn’t happen.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.IndexView.as_view(), name="index"),
    path('posts/', views.AllPostsView.as_view(), name="posts"),
    path('posts/<slug:slug>', views.PostDetailView.as_view(), name="post-detail"),
    path('read-later', views.ReadLaterView.as_view(), name="read-later"),
]

views.py file:-

from typing import Any
from django.db.models.query import QuerySet
from django.shortcuts import render, get_object_or_404
from .models import Post
from django.views import View
from django.views.generic import ListView, DetailView
from .forms import CommentForm
from django.http import HttpResponseRedirect
from django.urls import reverse

# Create your views here.


# def index(request):
#     latest_posts = Post.objects.all().order_by("-date")[:3]
#     return render(request, "blog/index.html", {
#         "posts": latest_posts
#     })

class IndexView(ListView):
    template_name = "blog/index.html"
    model = Post
    context_object_name = "posts"
    ordering = ["-date"]

    def get_queryset(self):
        queryset = super().get_queryset()
        data = queryset[:3]
        return data


# def posts(request):
#     all_posts = Post.objects.all().order_by("-date")
#     return render(request, "blog/all-posts.html", {
#         "all_posts": all_posts
#     })

class AllPostsView(ListView):
    model = Post
    template_name = "blog/all-posts.html"
    ordering = ["-date"]
    context_object_name = "all_posts"


# def post_detail(request, slug):
#     identified_post = get_object_or_404(Post, slug=slug)
#     return render(request, "blog/post-detail.html", {
#         "identified_post": identified_post,
#         "post_tags": identified_post.tags.all()
#     })

class PostDetailView(View):
    def get(self, request, slug):
        post = Post.objects.get(slug=slug)
        context = {
            "post": post,
            "comment_form": CommentForm(),
            "comments": post.comments.all().order_by("-id")
        }
        return render(request, "blog/post-detail.html", context)

    def post(self, request, slug):
        comment_form = CommentForm(request.POST)
        post = Post.objects.get(slug=slug)
        
        if comment_form.is_valid():
            comment = comment_form.save(commit=False)
            comment.related_post = post
            comment.save()
            return HttpResponseRedirect(reverse("post-detail", args=[slug]))

        context = {
            "post": post,
            "comment_form": comment_form,
            "comments": post.comments.all().order_by("-id")
        }
        return render(request, "blog/post-detail.html", context)

class ReadLaterView(View):
    def post(self, request):
        stored_posts = request.session.get("stored_posts")

        if stored_posts is None:
            stored_posts = []
        
        if (post_id := request.POST["post_id"]) not in stored_posts:
            stored_posts.append(int(post_id))
        
        return HttpResponseRedirect("/")

Please help me and if any other file or code is required, please ask.

/posts/read-later I think matches the url whose name is post-detail

Several ideas, not in order:

Change def get in PostDetailView to:

def get(self, request, slug):
    try:
        post = Post.objects.get(slug=slug)
    except Post.DoesNotExist:
        post = None
        print('POST NOT FOUND!!!1!1!!1!!')
    <snip>

This is because the exception throw on your page indicates that django is indeed looking for a Post object with certain criteria (slug='read-later') … can not find it… and so throws a DoesNotExist exception.

You can also do this in your urls.py file:

urlpatterns = [
    path('', views.IndexView.as_view(), name="index"),
    path('posts/', views.AllPostsView.as_view(), name="posts"),
    path('posts/<slug:slug>', views.PostDetailView.as_view(), name="post-detail"),
    # change the url here so that it does not collide with posts/<slug:slug> just above
    path('posts-read-later', views.ReadLaterView.as_view(), name="read-later"), 
]

Assuming the above is your problem, and I posted stuff that help you track down your problem and fix it…

it all boils down to:

THE CLIENT IS THE ENEMY

You will not be able to prevent anyone (malicious or otherwise) to try stuff such as:

  • POST /posts/a-slug-that-DOES-NOt-ExistS
  • GET /posts/../../etc/shadow1 (or variants…)
  • HEAD/posts/the-slug?w=..\..\x0E\xA4\ls

or similar stuff that bots (or humans) will regularly throw at your sites/pages.

Now django does a fine job taking of some of the “not supported methods” (hence def post(), def get() etc.) and other things, but will not be able to do it for you and your data.

Which is why the try:... except Model.DoesNotExist:... should be a pattern you learn by heart, along with:

  • turn off DEBUG on production
  • work on a custom 404 page (and 500 too…)

Thanks, I got the solution. Django tries the url “posts/read-later” and thinks “read-later” as a slug. So, I changed the order of urls and put “posts/read-later” first. Now, it works perfectly. :heart: :heart: :heart:

what happens when one your Posts has a slug set to read-later?

I see. Can I do something, so that “read-later” as slug becomes invalid in the form itself?

You could try something like this It’s all about namespacing, tbh, to avoid collisions…

urlpatterns = [
    path('', views.IndexView.as_view(), name="index"),
    path('posts/', views.AllPostsView.as_view(), name="posts"),
    path('posts/details/<slug:slug>', views.PostDetailView.as_view(), name="post-detail"),
    # maybe add a slug here as well, so you don't have to rely on the session?
    path('posts/read-later', views.ReadLaterView.as_view(), name="read-later"), 
]