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.

1
  • 2
    get_context_data method should return context dictionary but not a Response instance. Just to return c Commented May 24, 2019 at 9:36

1 Answer 1

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
Sign up to request clarification or add additional context in comments.

3 Comments

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
Nice catch. Thanks @xxbinxx. I'm gonna edit my answer.
Just don't forget to explain every change you made otherwise the questioner won't understand properly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.