Elchinas
Elchinas

Reputation: 139

Combine Create and DetailView in django

I have a model Book and a model Review (with a ForeignKey to Book). I wanted to create a view where you have all the data related to a book (DetailView), and add the functionality of showing and creating reviews. I came up with this code, but don't know whether it is a good practice, maybe I should go for something different:

class BookDetailView(CreateView):
	template_name = 'books/book_detail.html'
	form_class = ReviewForm

	def get_context_data(self, *args, **kwargs):
		context = super().get_context_data(*args, **kwargs)

		slug = self.kwargs.get('slug')
		obj = Book.objects.get(slug__iexact=slug)

		if get_language() == 'es':
			context['reviews'] = obj.review_set.all().filter(language__iexact='es')
		else:
			context['reviews'] = obj.review_set.all().filter(language__iexact='en')

		if len(context['reviews']) == 0:
			context['is_empty'] = True

		context['object'] = obj
		return context

	def form_valid(self, form):
		obj = form.save(commit=False)

		return super().form_valid(form)

And the template:

{% extends "base.html" %}
{% load i18n %}

{% block content %}

<h1>{{object.title}}</h1>

<h2>{% trans "Reviews section" %}</h2>

{% for review in reviews %}
<b>{{review.title}}

{% endfor %}

<h2>Add a review!</h2>
{% include "form.html" with form=form %}

{% endblock content %}

And finally the url: url(r'^(?P[\w-]+)/$', ...)

What do you think?

Thanks for your time!

Upvotes: 2

Views: 767

Answers (2)

SerSergious
SerSergious

Reputation: 490

I know this post is a little bit old but it helps me a lot with my issue. Thank you @allcaps

First of all, I would like to mention that the "ReviewForm" should inherit from forms.ModelForm and than we will have a access to "instance" and "save()"

More about ModelForm you can read in django docs Django ModelForms

Upvotes: 0

allcaps
allcaps

Reputation: 11228

I did something similar once. But used the DetailView and just added the ReviewForm to the context and added a method to handle post data. Something like this:

class BookDetailView(DetailView):
    model = Book

    def get_context_data(self, *args, **kwargs):
        ctx = super().get_context_data(*args, **kwargs)
        language = get_language()
        ctx.update({
            'reviews': ctx['book'].review_set.all().filter(language__iexact=language),
            'form': ReviewForm()
        })
        return ctx

    def post(self, *args, **kwargs):
        self.object = self.get_object(self.get_queryset())
        form = ReviewForm(self.request.POST)
        if form.is_valid():
            form.instance.book = self.object
            form.save()
            return HttpResponseRedirect(self.object.get_absolute_url())
        else:
            ctx = self.get_context_data(**kwargs)
            ctx.update({'form': form})
            return self.render_to_response(ctx)

I guess this takes a little more code for handling the form, but the bonus is you can set the related book (the user can't fiddle with that form data).

Note that you don't have to specify template_name because it is automagical correct.

Upvotes: 4

Related Questions