Django FormView Mixin Confusion
According to django documentation for using mixin, the FormMixin was supposed to handle POST request with an attached form body or at least that was the impression I’ve got from the example they used. Just to brieftly summary, the usecase was to code an url to display AuthorDetail. GET request will render the details for an author and a form to enter AuthorInterest which is basically a text field. One saves an AuthorInterest by POSTing the comment to that same URL.
class AuthorInterestForm(forms.Form):
message = forms.CharField()
class AuthorDisplay(DetailView):
model = Author
def get_context_data(self, **kwargs):
context = super(AuthorDisplay, self).get_context_data(**kwargs)
context['form'] = AuthorInterestForm()
return context
class AuthorInterest(SingleObjectMixin, FormView):
template_name = 'books/author_detail.html'
form_class = AuthorInterestForm
model = Author
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseForbidden()
self.object = self.get_object()
#What I used to debug
#import pdb; pdb.set_trace()
return super(AuthorInterest, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('author-detail', kwargs={'pk': self.object.pk})
def form_valid(self, form): #<-- What I ended up doing
...
if form.is_valid():
form.save()
...
return super(AuthorInterest, self).form_valid(self, form)
class AuthorDetail(View):
def get(self, request, *args, **kwargs):
view = AuthorDisplay.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = AuthorInterest.as_view()
return view(request, *args, **kwargs)
It was interesting to finally understand how this whole mixing thing works in django. Here’s my take:
AuthorDetail
will be the entry point (controller) for the intended URL.get
andpost
methods are to delegate the request toAuthorDisplay
andAuthorInterest
respectivelyAuthorDisplay
inherits fromDetailView
which is to handle displaying a specific object, so no surprise there. They override theget_context_data
to also querying associated interest with the author. One mistake I had there was NOT returning the context itself, which caused the detail page to go blank and blow up because one ofurl
reverse links had an empty param in it.- Now the interesting bit is in
AuthorInterest
class. It inherits fromSingleObjectMixin
which is (again) to handle a single object - it hasget_object()
,template_name
andmodel
to do that. It inherits from FormView to handle form posting, which is what we needed for theAuthorInterest
-form_class
,get_success_url()
are for that.
Now, the above code won’t work when creating an author interest. The reason was, in FormView
class, the form is not being saved anywhere! It took me quite sometimes to figure out, both by reading the source of FormView
and by using pdb
To use pdb
to debug django code, put
import pdb; pdb.set_trace()
wherever you want to debug, and then run
python -m pdb manage.py runserver
(Credit goes here for this piece of handy work)
In my application, I wanted to set the interest’s owner (which is the logged in user) and also the author this interest is for (which is the current author object). To do that, I overriden the form_valid
method as above. While it works, there’s something I’m still wondering:
- The above method is not so nice because it does the saving before its super class. Even though in this case the super class doesn’t save the form yet, it’s still kind of awkward
- Why doesn’t this case being handled automatically in the
FormView
class?
In this particular example, even though I finally see how these pieces fit together - the views, the mixin, pdb, method overriden; I feel django documentation was a bit lacking. It didn’t menion that the FormView
doesn’t actually do the saving. Hope that piece of text would be added later, or they would use a more realistic use case where this assumption would be demonstrated more clearly.