1

I am trying to show related posts, relatedness is based on category. Post model has a foreign key to category model.

Is there any better way of doing this. Currently I am sending category_name from single_post_detail_view using the session to custom context_processor function, which then returns the posts related to that category.

views.py

class PostDetailView(DetailView):
    def get(self, request, slug):
        post = Post.objects.get(slug=self.kwargs['slug'])
        context = {
            'post' : post
        }
        request.session['category'] = post.category.name
        return render(request, 'blog/post_detail.html', context)

context_processors.py

from blog.models import Category

def related_posts(request):
    if request.session['category']:
        category = request.session['category']
    return {
        'related_posts': Category.objects.get(name=category).posts.all()
    }

and then in HTML

{% for post in related_posts %}
   <a href="post.get_absolute_url()">{{post.title}}</a>
{% endfor %}
Anshul M
  • 99
  • 1
  • 12
  • Why are you doing this at all? If it depends on something from the view, it definitely shouldn't be a context processor. Why is it not directly in the view, or as a template tag? – Daniel Roseman Apr 25 '19 at 10:44
  • Because related posts are used in many pages. – Anshul M Apr 25 '19 at 10:46
  • Note that you shouldn't usually override `get` for generic views like `DetailView`. You end up losing functionality, or duplicating code. In your case, I would either write a simple function-based-view `def post_detail(request, slug):`, or use `DetailView` and override `get_context_data` instead of `get` to add `related_posts` to the context. – Alasdair Apr 25 '19 at 10:50
  • Things seems to get on my head now, thanks everyone. – Anshul M Apr 25 '19 at 10:52
  • Also, your current context processor will raise `KeyError` if `category` is not in the session, and then the `Category.objects.get(...)` will raise an error if `category` is not set. – Alasdair Apr 25 '19 at 10:52
  • Oh yeah I'm aware of that its just for not making things lengthy I don't wrote it in the question. – Anshul M Apr 25 '19 at 10:54

1 Answers1

1

A context processor is meant to run for every request. If you need to pass information to it, it's a sign that you shouldn't be using a context processor.

You could use a helper function,

def related_posts(category):
    return category.posts.all()

then manually add the posts to the context in the view:

    context = {
        'post': post,
        'related_posts': related_posts(post.category)
    }

Or you could write a custom template tag.

A simple tag would let you do:

{% related_posts post.category as related_posts %}
{% for post in related_posts %}
    ...
{% endfor %}

Or perhaps you could use an inclusion tag to render the links:

{% related_posts post.category %}
Alasdair
  • 298,606
  • 55
  • 578
  • 516