1

I have a function-based view I'm trying to convert to a class-based view(DetailView). The function-based view is the equivalent of DetailView in CBV. How do I go about this?

This is the function based-view

def show_post(request, post):
    """A View to display a single post.
    The post variable here is the slug to a post.
    The slug to a post may possibly be a duplicate. So we filter all posts by 
    the slug (which is the 1st variable here)
    And then select the first post returned by the filter queryset.
    The first post returned is, of course, the post we are trying to view.
    """
    post = Post.objects.filter(slug=post)
    if post.exists():
        post = post[0]
    else:
        return HttpResponseRedirect('/blog/')
     count_visits = None
    unique_views = set()

    if request.user.is_authenticated:
        post_views = PostView.objects.filter(post=post)
        count_visits = post_views.count()
        for post_view in post_views:
            unique_views.add(post_view.ip)
    else:
        post_view = PostView(post=post, ip=request.META.get('REMOTE_ADDR',''),
                      http_host=request.META.get('HTTP_HOST', ''),
                      http_referrer=request.META.get('HTTP_REFERER',''),                    
                      http_user_agent=request.META.get('HTTP_USER_AGENT', ''),
                             remote_host=request.META.get('REMOTE_HOST', ''))
        post_view.save()

    c = {
        'post': post,
        'comments': comments,
        'new_comment': new_comment,
        'similar_posts': similar_posts,
        'count_visits': count_visits,
        'unique_views': unique_views,
        'comments_count': comments_count,
        'message': message,
    }
    return render(request, 'blog/posts/post.html', c)

Then I tried to convert it to a class-based view using this:

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post.html'
    context_object_name = 'post'

    def get_context_data(self, **kwargs):
        self.slug = get_object_or_404(Post, slug=self.kwargs['slug'])        
        p = Post.objects.filter(slug=self.slug).first()
        if p.exists():
            p = p[0]
        else:
            return HttpResponseRedirect('/blog/')
        count_visits = None
        unique_views = set()

        if self.request.user.is_authenticated:
            post_views = PostView.objects.filter(post=p)
            count_visits = post_views.count()
            for post_view in post_views:
                unique_views.add(post_view.ip)

        else:
            post_view = PostView(ip=self.request.META.get('REMOTE_ADDR', ''),
                 http_host=self.request.META.get('HTTP_HOST', ''),
                 http_referrer=self.request.META.get('HTTP_REFERER', ''),                               
                 http_user_agent=self.request.META.get('HTTP_USER_AGENT', ''),                                 
                 remote_host=self.request.META.get('REMOTE_HOST', ''))

            post_view.save()

        c = {
            'count_visits': count_visits,
            'unique_views': unique_views,
        }

        return render(self.request, self.template_name, c)

I'm not sure this is the right way to go about this. Kindly assist me.

Many thanks.

Toluwalemi
  • 391
  • 5
  • 16

1 Answers1

3

A DetailView, by default, should take care of the pk/slug passed on onto your url, therefore you don't need to do the queryset your doing on the first 2 lines inside your get_context_data. A believe a clearer way of doing that would look something like this:

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post.html'
    context_object_name = 'post'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.object is None:
            return HttpResponseRedirect(reverse('blog'))

        count_visits = None
        unique_views = set()

        if self.request.user.is_authenticated:
            post_views = PostView.objects.filter(post=self.object)
            count_visits = post_views.count()
            for post_view in post_views:
                unique_views.add(post_view.ip)
        else:
            post_view = PostView.objects.create(
                ip=self.request.META.get('REMOTE_ADDR', ''),
                http_host=self.request.META.get('HTTP_HOST', ''),
                http_referrer=self.request.META.get('HTTP_REFERER', ''),
                http_user_agent=self.request.META.get('HTTP_USER_AGENT', ''),
                remote_host=self.request.META.get('REMOTE_HOST', '')
            )

        context.update({
            'count_visits': count_visits,
            'unique_views': unique_views,
        })

        return context
Higor Rossato
  • 1,996
  • 1
  • 10
  • 15
  • 2
    FYI it's good practice to use `reverse` names for urls. @Toluwalemi you can replace `return HttpResponseRedirect('/blog/')` with `return HttpResponseRedirect(reverse('blog')` if the url name `blog` has pattern `/blog/`. I hope you got the idea – xxbinxx May 24 '19 at 10:03
  • Nice catch. Thanks @xxbinxx. I'm gonna edit my answer. – Higor Rossato May 24 '19 at 10:05
  • 1
    Just don't forget to explain every change you made otherwise the questioner won't understand properly. – xxbinxx May 24 '19 at 10:20