Django Recipes. Part 1 - AJAX Forms
Hello, habrauser!
I may have already started a midlife crisis , but in the summer I began work on a major Open Source project. However, about it a little later, when the code will not be ashamed . So, I want to share a number of snippets that I had to write to fit the DRY Don't Repeat Youself concept . Therefore, I am going to write several articles.
By the way, you can pay attention to my previous article .
I want to make a reservation right away - not so long ago I found an example on the dzhangi website for implementing ajax forms. As it turned out, I did almost the same thing, but I did it myself and I am satisfied =) Below I will give my example and analyze it.
I ask for cat.
Speaking about the processing of forms in the jung, taking into account the appearance of the django.views.generic module back in release 1.5, one cannot but pay attention to the FormView class. Since all other generic classes for processing forms are inherited from it, I chose it as the object of my experiments.
The is_valid (self, form) and is_invalid (self, form) methods are responsible for processing the returned result if the form meets the requirements of is_valid () and not, respectively. Making our way up the ancestors to FormMixin we see the following:
In the case of the is_valid () method, only a redirect bothers us. Therefore, we can safely replace this code with the answer we need:
As a result, we get the HTTP 200 code in the output that jquery expects .
I also allowed myself to peek into the ModelFormMixin class and add a form save before the response. As a result, we got such a hybrid:
The answer in case of a form error is somewhat more complicated - we need to return the dictionary of errors and display them for the user. However, the task is easily performed using the standard attribute errors of the form:
Thus we get the following class:
We now have a class that we can safely inherit, like any other type of Class-based views . The call will look something like this:
It can be used as CreateView only with the caveat that we __init __ () method of the parent FormMixin class does not accept the model argument and we will have to do this work on our own:
So, we have a base class for handling ajax forms. Why not follow the django-way and create one more generic "in the image" to update yourself . In the same django.views.generic module we find the UpdateView class and the answer for us - we need the object attribute for our class ... Voila:
Without a fancy, we copy the code, we just inherit everything else from the existing AjaxFormMixin class . By the way, this class already has a model attribute, since we inherited it from UpdateView , whose roots go back to ModelFormMixin .
As a result, we have a set of classes that support the creation, modification of objects and other actions with them (if necessary, override is_valid () as well as / dev / brain and / dev / hands to taste) with ajax answers, for free inheritancenot prohibited by law .
PS. The purpose of the article was not so much to demonstrate my achievements (although what is there to dissemble), but to analyze the process itself. A little later, when my assistant completes the application muzzle code, we will supplement the article with the front-end component code as well. In the meantime, thank you all for your attention.
I may have already started a midlife crisis , but in the summer I began work on a major Open Source project. However, about it a little later
By the way, you can pay attention to my previous article .
I'll start with the implementation of AJAX.
I want to make a reservation right away - not so long ago I found an example on the dzhangi website for implementing ajax forms. As it turned out, I did almost the same thing, but I did it myself and I am satisfied =) Below I will give my example and analyze it.
I ask for cat.
Speaking about the processing of forms in the jung, taking into account the appearance of the django.views.generic module back in release 1.5, one cannot but pay attention to the FormView class. Since all other generic classes for processing forms are inherited from it, I chose it as the object of my experiments.
So, let's go:
Backend
The is_valid (self, form) and is_invalid (self, form) methods are responsible for processing the returned result if the form meets the requirements of is_valid () and not, respectively. Making our way up the ancestors to FormMixin we see the following:
def form_valid(self, form):
"""
If the form is valid, redirect to the supplied URL.
"""
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
"""
If the form is invalid, re-render the context data with the
data-filled form and errors.
"""
return self.render_to_response(self.get_context_data(form=form))
In the case of the is_valid () method, only a redirect bothers us. Therefore, we can safely replace this code with the answer we need:
from django.shortcuts import HttpResponse
def form_valid(self, form):
return HttpResponse('OK')
As a result, we get the HTTP 200 code in the output that jquery expects .
I also allowed myself to peek into the ModelFormMixin class and add a form save before the response. As a result, we got such a hybrid:
def form_valid(self, form):
form.save()
return HttpResponse('OK')
The answer in case of a form error is somewhat more complicated - we need to return the dictionary of errors and display them for the user. However, the task is easily performed using the standard attribute errors of the form:
import json
from django.http import HttpResponseBadRequest
def form_invalid(self, form):
errors_dict = json.dumps(dict([(k, [e for e in v]) for k, v in form.errors.items()]))
return HttpResponseBadRequest(json.dumps(errors_dict))
AjaxFormMixin
Thus we get the following class:
from django.views.generic import FormView
class AjaxFormMixin(FormView):
template_name = 'form_ajax.html' # Поскольку существенная часть форм в проекте рендерятся одним шаблоном, то зачем его везде указывать=) Впрочем, эта строка несущественна.
def form_valid(self, form):
form.save()
return HttpResponse('OK')
def form_invalid(self, form):
errors_dict = json.dumps(dict([(k, [e for e in v]) for k, v in form.errors.items()]))
return HttpResponseBadRequest(json.dumps(errors_dict))
We now have a class that we can safely inherit, like any other type of Class-based views . The call will look something like this:
from django.contrib.auth.forms import PasswordChangeForm # если кому-то будет полезно;-)
class PasswordChange(AjaxFormMixin):
form_class = PasswordChangeForm
It can be used as CreateView only with the caveat that we __init __ () method of the parent FormMixin class does not accept the model argument and we will have to do this work on our own:
from django.forms.models import modelform_factory
class TaskUserAssign(AjaxFormMixin):
form_class = modelform_factory(models.TaskRole)
AjaxUpdateFormMixin
So, we have a base class for handling ajax forms. Why not follow the django-way and create one more generic "in the image" to update yourself . In the same django.views.generic module we find the UpdateView class and the answer for us - we need the object attribute for our class ... Voila:
class AjaxUpdateFormMixin(AjaxFormMixin, UpdateView):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(AjaxFormMixin, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(AjaxFormMixin, self).post(request, *args, **kwargs)
Without a fancy, we copy the code, we just inherit everything else from the existing AjaxFormMixin class . By the way, this class already has a model attribute, since we inherited it from UpdateView , whose roots go back to ModelFormMixin .
As a result, we have a set of classes that support the creation, modification of objects and other actions with them (if necessary, override is_valid () as well as / dev / brain and / dev / hands to taste) with ajax answers, for free inheritance
PS. The purpose of the article was not so much to demonstrate my achievements (although what is there to dissemble), but to analyze the process itself. A little later, when my assistant completes the application muzzle code, we will supplement the article with the front-end component code as well. In the meantime, thank you all for your attention.