Reputation: 347
I'm still a noob at mixins in general so I'm just trying to understand what happens in this code that uses Ajax to submit forms. I've been scouring the docs for a whole day to try to figure out what happens. The code seems to work but I just don't entirely understand why. I still have a lot of question marks on the whole process so if anyone can correct my thinking that would be amazing
The AjaxableResponseMixin extends the form_invalid() and form_valid() methods of the FormView to support Ajax requests
If the request is not Ajax it returns response
CreatePostView is a child class of the two parent classes that are passed in (AjaxableResponseMixin, FormView)
If the form is valid then a post gets created and super().form_valid() gets called which then redirects to the success url because thats what the FormView.form_valid() does
If the form is invalid it redirects to the url with the "create_post" name
views.py
class AjaxableResponseMixin(object):
"""
Mixin to add AJAX support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def form_invalid(self, form):
response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return JsonResponse(form.errors, status=400)
else:
return response
def form_valid(self, form):
response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
'pk': self.object.pk,
}
return JsonResponse(data)
else:
return response
class CreatePostView(AjaxableResponseMixin, FormView):
form_class = CreatePostForm
template_name = 'forum/create_post.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
user = self.request.user
form.create_post(user_obj=user)
messages.success(self.request, 'Your post was published')
return super().form_valid(form)
def form_invalid(self, form):
messages.error(self.request, 'Your post could not be published. Please try again')
return HttpResponseRedirect(reverse('create_post'))
Thank you so much to anybody who answers.
Upvotes: 0
Views: 706
Reputation: 20702
Most of your question relates to python multiple inheritance and MRO (method resolution order). Look it up, there are plenty of other resources that explain it in detail. But to help with your specific case, you have defined the inheritance in this order:
CreatePostView --> AjaxableResponseMixin ...> FormView
This is the order in which subclassed methods will be called. I've made the arrows different because the first is a subclass - parent class relationship, the second one isn't (CreatePostView
is a subclass of both FormView
and AjaxableResponseMixin
)
I'll explain what happens with form_valid
: So if you call the form_valid()
method on a CreatePostView
, its form_valid()
method is called. This runs all the code of that method.
The last line of that method is return super().form_valid(form)
telling python to call the parent's class method. With MRO, that is the AjaxableResponseMixin.form_valid
method. But the first line is response = super().form_valid(...)
, which is FormView.form_valid()
.
So here, because super()
is at the beginning, you effectively tell python to first go to the next in the MRO chain.
AjaxableResponseMixin
does nothing with the super()
response if the request is an ajax request (it returns JSON with the object pk as data), but if it's not an ajax request, it just returns the FormView
response. In this case it's a redirect to the success_url
, because that's what FormView
does in case of success. You can see that here.
Now you can do the same exercise with form_invalid()
. In fact, it should just return super().form_invalid(form)
. It should never redirect, because you want it to render
the same form on the same page (in the normal case) or just report the form errors in the ajax case.
Notes:
super()
, which is entirely legal, the parent's class method will never be called.super()
to take care of the basics (the default behaviour) and then add your own stuff. In your form_valid
, you do it the other way round because you first want to save the object, otherwise the JSON cannot contain the object's pk. And actually, if used incorrectly, this mixin will crash on that line.AjaxableResponseMixin
doesn't contain any python code forcing it to be "mixed" with a FormView
, but it has to, because it calls super().form_valid()
. In fact many IDEs warn you with that call. There's no way to tell python that AjaxableResponseMixin
MUST be mixed with a FormView
, but it should be part of the documentation so other developers know how to use it. The same holds for the warning that the object must be saved before calling AjaxableResponseMixin.form_valid
.Upvotes: 1