Search code examples
pythondjangodjango-viewsdjango-class-based-viewsdjango-generic-views

Not good template loaded in Django generic list view when using filter


I have a strange behavior in my generic views. Below is the classic FBV scheme I want to reproduce in a CBV.

My FBV

def post_list(request, tag_name=None):

    if tag_name:
        # Filter post according to tag name if provided
        posts = Post.objects.filter(tag__tag_name=tag_name)
    else:
        posts = Post.objects.all()

    context = {"posts": posts}

    return render(request, "blog/post_list.html", context)


  def post_detail(request, post_id):

    post = Post.objects.get(pk=post_id)

    context = {"post": post}

    return render(request, "blog/post_detail.html", context)

My CBV

class PostList(ListView):
    model = Post
    context_object_name = "post_list"
    template_name = "blog/post_list.html"

    def get_queryset(self):

        if "tag_name" in self.kwargs:
            return Post.objects.filter(tag__tag_name=self.kwargs["tag_name"])
        else:
            return Post.objects.all()

class PostDetail(DetailView):
    model = Post
    context_object_name = "post_detail"
    template_name = "blog/post_detail.html"

Here are my models

from django.db import models

# Create your models here.
class Tag(models.Model):
    tag_name = models.CharField(max_length=100)

    def __str__(self):
        return self.tag_name

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    tag = models.ManyToManyField(Tag, blank=True)

    def __str__(self):
        return self.title

And here are my URLs

from django.urls import path
from .views import PostList, PostDetail

urlpatterns = [
    path("", PostList.as_view(), name="blog-index"),
    path("<tag_name>", PostList.as_view(), name="blog-index"),
    path("<int:pk>", PostDetail.as_view(), name="post-detail")
]

As you can see, I want to use the same generic view for the list of my posts with an optional tag provided in the URL. It is well filtering my articles when I provide a URL with a tag, something like this .../blog/my_tag, but problem is that the DetailView process does not work anymore. It always loads my blog/post_list.html with an empty list instead of my blog/detail_post.html template. The DetailView process works well when I remove the process to filter with tag.

What am I doing wrong?


Solution

  • The URL pattern is the problem here: All your detail view request are getting matched with Second path i.e: path("<tag_name>", PostList.as_view(), name="blog-index"), try to provide a path pattern with converters that they match as you want in your application.The sequence matters here as first match is first served.

    Instead of this:

    urlpatterns = [
        path("", PostList.as_view(), name="blog-index"),
        path("<tag_name>", PostList.as_view(), name="blog-index"),
        path("<int:pk>", PostDetail.as_view(), name="post-detail")
    ]
    

    You can differentiate by some prefixes and path converters so that they match the exact case as you want for example:

    urlpatterns = [
        path('postlist/', PostList.as_view(),name='blog-index'),
        path('postlist/<str:tag_name>/', PostList.as_view(),name='blog-index-tag'),
        path('postdetail/<int:pk>/', PostDetail.as_view(),name='post-detail'),
    
    ]