1

I have a class-based view in my Django application and it works fine. But I guess it is not coded well, because it violates the DRY principle. Specifically, I have two absolutely similar declarations of the posts_list variable inside get() and post() methods:

class TopicView(View):
    def get(self, request, topic_id):
        post_form = PostForm()
        posts_list = Post.objects.filter(topic_id=self.kwargs['topic_id']).order_by('-creation_date')
        return render(request, 'discussions/topic.html', {'posts_list': posts_list,
                                                          'post_form': post_form,
                                                          'topic_id': topic_id})

    def post(self, request, topic_id):
        post_form = PostForm(request.POST)

        if post_form.is_valid():
            post = post_form.save(commit=False)
            post.author = request.user
            post.topic = Topic.objects.get(pk=topic_id)
            post.save()
            return redirect('discussions:topic', topic_id=topic_id)

        posts_list = Post.objects.filter(topic_id=self.kwargs['topic_id']).order_by('-creation_date')
        return render(request, 'discussions/topic.html', {'posts_list': posts_list,
                                                          'post_form': post_form,
                                                          'topic_id': topic_id})

Is there a way how I can declare this variable as a class attribute instead of a simple variable inside each of the methods? When I declaring it, I use topic_id as a filter for objects, and I extract topic_id from the URL (self.kwargs object, self is passed to both get() and post() as an input parameter). This is the main issue.

1 Answer 1

1

There are a few ways you can reorganize this. But one simple way is to put the common code in its own method, then call it when you need it.. like so:

class TopicView(View):
    def get(self, request, topic_id):
        return self.post_list_response(PostForm())

    def post(self, request, topic_id):
        post_form = PostForm(request.POST)

        if post_form.is_valid():
            post = post_form.save(commit=False)
            post.author = request.user
            post.topic = Topic.objects.get(pk=topic_id)
            post.save()
            return redirect('discussions:topic', topic_id=topic_id)

        return self.post_list_response(post_form)

    def post_list_response(self, post_form):
        topic_id = self.kwargs['topic_id']
        posts_list = Post.objects.filter(topic_id=topic_id).order_by('-creation_date')
        return render(request, 'discussions/topic.html', {
            'posts_list': posts_list,
            'post_form': post_form,
            'topic_id': topic_id
        })

You could also structure this slightly better/differently by using Django's FormView class rather than just plain View.

Sign up to request clarification or add additional context in comments.

3 Comments

Can I pass additional parameters to the template when using FormView? Namely, I mean posts_list variable which I need to have in my topic.html template.
Yes, with FormView, you can override FormView's get_context_data method to add your own context variables. You don't call render yourself, FormView does that for you.
Let me add.. with a FormView you would not implement get or post methods you just need a form_valid method which contains the code you want to run when the form is valid.. and then you would extend get_context_data to add the code for your list

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.