Add common class based views.
This commit is contained in:
parent
13106a7066
commit
2236d7b5c9
|
@ -0,0 +1,260 @@
|
|||
"""
|
||||
Class Based Views
|
||||
"""
|
||||
|
||||
|
||||
from django.views.generic import base, detail
|
||||
from django.forms import ModelForm
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
|
||||
class AppTemplateFirstMixin(object):
|
||||
def get_template_names(self):
|
||||
names = []
|
||||
model = getattr(self, 'model', None)
|
||||
if not model:
|
||||
model = self.queryset.model
|
||||
if model is not None:
|
||||
opts = model._meta
|
||||
names.append("%s/%s%s.html" % (opts.app_label,
|
||||
opts.object_name.lower(),
|
||||
self.template_name_suffix))
|
||||
names.append("%s/%s.html" % (opts.app_label,
|
||||
self.template_name_suffix.strip('_')))
|
||||
if getattr(self, 'template_name', None):
|
||||
names.append(self.template_name)
|
||||
return names
|
||||
|
||||
|
||||
class ModelNameMixin(object):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super(ModelNameMixin, self).get_context_data(**kwargs)
|
||||
ctx['model_verbose_name_plural'] = self.model._meta.verbose_name_plural
|
||||
ctx['model_verbose_name'] = self.model._meta.verbose_name
|
||||
return ctx
|
||||
|
||||
|
||||
class ContextMixin(object):
|
||||
"""
|
||||
A default context mixin that passes the keyword arguments received by
|
||||
get_context_data as the template context.
|
||||
"""
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
if 'view' not in kwargs:
|
||||
kwargs['view'] = self
|
||||
return kwargs
|
||||
|
||||
|
||||
class MultiFormMixin(ContextMixin):
|
||||
"""
|
||||
A mixin that provides a way to show and handle multiple forms in a request.
|
||||
"""
|
||||
|
||||
initial = {}
|
||||
initials = {}
|
||||
forms_classes = None
|
||||
success_url = None
|
||||
|
||||
def get_prefixes(self):
|
||||
return self.forms_classes.keys()
|
||||
|
||||
def get_initial(self, prefix):
|
||||
"""
|
||||
Returns the initial data to use for forms on this view.
|
||||
"""
|
||||
return self.initials.get(prefix, self.initial).copy()
|
||||
|
||||
def get_form_class(self, prefix):
|
||||
"""
|
||||
Returns the form class to use in this view
|
||||
"""
|
||||
return self.forms_classes[prefix]
|
||||
|
||||
def get_form(self, form_class, prefix):
|
||||
"""
|
||||
Returns an instance of the form to be used in this view.
|
||||
"""
|
||||
return form_class(**self.get_form_kwargs(prefix))
|
||||
|
||||
def get_current_prefix(self):
|
||||
"""
|
||||
Returns the current prefix by parsing first keys in POST
|
||||
"""
|
||||
keys = self.request.POST.keys() or self.request.FILES.keys()
|
||||
for key in keys:
|
||||
if '-' in key:
|
||||
return key.split('-', 1)[0]
|
||||
return None
|
||||
|
||||
def get_forms(self):
|
||||
"""
|
||||
Returns the dictionnary of forms instances
|
||||
"""
|
||||
form_instances = {}
|
||||
for prefix in self.get_prefixes():
|
||||
form_instances[prefix] = self.get_form(self.get_form_class(prefix), prefix)
|
||||
return form_instances
|
||||
|
||||
def get_form_kwargs(self, prefix):
|
||||
"""
|
||||
Returns the keyword arguments for instantiating the form.
|
||||
"""
|
||||
kwargs = {'initial': self.get_initial(prefix),
|
||||
'prefix': prefix}
|
||||
if self.request.method in ('POST', 'PUT') \
|
||||
and prefix == self.get_current_prefix():
|
||||
kwargs.update({
|
||||
'data': self.request.POST,
|
||||
'files': self.request.FILES,
|
||||
})
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
"""
|
||||
Returns the supplied success URL.
|
||||
"""
|
||||
if self.success_url:
|
||||
url = self.success_url
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Provide a success_url.")
|
||||
return url
|
||||
|
||||
def form_valid(self, forms):
|
||||
"""
|
||||
If the form is valid, redirect to the supplied URL.
|
||||
"""
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def form_invalid(self, forms):
|
||||
"""
|
||||
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(forms=forms))
|
||||
|
||||
|
||||
class MultiModelFormMixin(MultiFormMixin, detail.SingleObjectMixin):
|
||||
"""
|
||||
A mixin that provides a way to show and handle multiple forms or modelforms
|
||||
in a request.
|
||||
"""
|
||||
|
||||
def get_form_kwargs(self, prefix):
|
||||
"""
|
||||
Returns the keyword arguments for instantiating the form.
|
||||
"""
|
||||
kwargs = super(MultiModelFormMixin, self).get_form_kwargs(prefix)
|
||||
if issubclass(self.get_form_class(prefix), ModelForm):
|
||||
kwargs.update({'instance': self.object})
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
"""
|
||||
Returns the supplied URL.
|
||||
"""
|
||||
if self.success_url:
|
||||
url = self.success_url % self.object.__dict__
|
||||
else:
|
||||
try:
|
||||
url = self.object.get_absolute_url()
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Either provide a url or define"
|
||||
" a get_absolute_url method on the Model.")
|
||||
return url
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
If the form is valid, save the associated model.
|
||||
"""
|
||||
form = form[self.get_current_prefix()]
|
||||
if hasattr(form, 'save'):
|
||||
if isinstance(form, ModelForm):
|
||||
self.object = form.save()
|
||||
else:
|
||||
form.save()
|
||||
if hasattr(form, 'save_m2m'): # save many2many relations
|
||||
form.save_m2m()
|
||||
return super(MultiModelFormMixin, self).form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
If an object has been supplied, inject it into the context with the
|
||||
supplied context_object_name name.
|
||||
"""
|
||||
context = {}
|
||||
if self.object:
|
||||
context['object'] = self.object
|
||||
context_object_name = self.get_context_object_name(self.object)
|
||||
if context_object_name:
|
||||
context[context_object_name] = self.object
|
||||
context.update(kwargs)
|
||||
return super(MultiModelFormMixin, self).get_context_data(**context)
|
||||
|
||||
|
||||
class ProcessMultiFormView(base.View):
|
||||
"""
|
||||
A mixin that renders a form on GET and processes it on POST.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Handles GET requests and instantiates a blank version of the form.
|
||||
"""
|
||||
forms = self.get_forms()
|
||||
return self.render_to_response(self.get_context_data(forms=forms))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Handles POST requests, instantiating a form instance with the passed
|
||||
POST variables and then checked for validity.
|
||||
"""
|
||||
forms = self.get_forms()
|
||||
prefix = self.get_current_prefix()
|
||||
if prefix and forms[prefix].is_valid():
|
||||
return self.form_valid(forms)
|
||||
else:
|
||||
return self.form_invalid(forms)
|
||||
|
||||
# PUT is a valid HTTP verb for creating (with a known URL) or editing an
|
||||
# object, note that browsers only support POST for now.
|
||||
def put(self, *args, **kwargs):
|
||||
return self.post(*args, **kwargs)
|
||||
|
||||
|
||||
class BaseMultiFormView(MultiFormMixin, ProcessMultiFormView):
|
||||
"""
|
||||
A base view for displaying multiple forms
|
||||
"""
|
||||
|
||||
|
||||
class MultiFormView(base.TemplateResponseMixin, BaseMultiFormView):
|
||||
"""
|
||||
A base view for displaying multiple forms, and rendering a template reponse.
|
||||
"""
|
||||
|
||||
|
||||
class BaseMultiUpdateView(MultiModelFormMixin, ProcessMultiFormView):
|
||||
"""
|
||||
Base view for updating an existing object.
|
||||
|
||||
Using this base class requires subclassing to provide a response mixin.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(BaseMultiUpdateView, self).get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(BaseMultiUpdateView, self).post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class MultiUpdateView(AppTemplateFirstMixin,
|
||||
detail.SingleObjectTemplateResponseMixin,
|
||||
BaseMultiUpdateView):
|
||||
"""
|
||||
View for updating an object,
|
||||
with a response rendered by template.
|
||||
"""
|
||||
|
Reference in New Issue