public
Last active

Django CBV factory function, with example usage.

  • Download Gist
cbv_factory.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
from inspect import isfunction
from django.views.generic import View, CreateView, UpdateView, DeleteView, DetailView, ListView
from django.views.generic.edit import ModelFormMixin
from django.views.generic.detail import SingleObjectMixin
 
def parse_func_dict(selfobj, d):
"""
Recursively goes through a dict, calling functions that refer to a view's
self and therefore need to be called from within -- such as getting extra
kwargs for a form constructor based on request data.
"""
for i in d.keys():
if isfunction(d[i]):
d[i] = d[i](selfobj)
elif isinstance(d[i],dict):
d[i] = parse_func_dict(selfobj,d[i])
return d
 
def cbv_factory(modelclass, **kwargs):
"""
For a given model, returns generic class-based ListView, DetailView,
CreateView, UpdateView, DeleteView.
"""
_queryset = kwargs.get('queryset',None)
_form_class = kwargs.get('form_class',None)
_extra_context = kwargs.get('extra_context',{})
_extra_form_kwargs = kwargs.get('extra_form_kwargs',{})
_list_template = kwargs.get('list_template',{})
_form_template = kwargs.get('form_template',{})
_detail_template = kwargs.get('detail_template',{})
 
class FactoryObjectMixin(SingleObjectMixin):
"""
Common properties of all views.
"""
model = modelclass
if _queryset:
queryset = _queryset
def get_context_data(self, **kwargs):
d = super(FactoryObjectMixin, self).get_context_data(**kwargs)
d.update(parse_func_dict(self, _extra_context))
return d
 
class FactoryFormMixin(ModelFormMixin):
"""
Common properties of form-based views (Create, Update).
"""
if _form_template:
template_name = _form_template
if _form_class:
form_class = _form_class
def get_form_kwargs(self, **kwargs):
d = super(FactoryFormMixin,self).get_form_kwargs(**kwargs)
d.update(parse_func_dict(self, _extra_form_kwargs))
return d
 
class Detail(FactoryObjectMixin, DetailView):
if _detail_template:
template_name = _detail_template
 
class List(ListView):
model = modelclass
if _list_template:
template_name = _list_template
if _field_list:
field_list = _field_list
if _queryset:
queryset = _queryset
 
class Create(FactoryFormMixin, FactoryObjectMixin, CreateView):
pass
 
class Update(FactoryFormMixin, FactoryObjectMixin, UpdateView):
pass
 
class Delete(FactoryObjectMixin, DeleteView):
pass
 
return {
'list':List,
'detail':Detail,
'create':Create,
'update':Update,
'delete':Delete
}
views.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
import os
from django.db.models import Model
from django.conf.urls import patterns, url
from django.views.generic import CreateView
 
from cbv_factory import cbv_factory
 
def get_request_company(obj):
return obj.request.user.employee.company
 
def get_request_user(obj):
return obj.request.user
 
def get_createview(obj):
if issubclass(obj.__class__,CreateView):
return True
return False
 
 
def manufacture_views():
"""
Automatically creates a lot of views using objectviews_factory to handle the
shared boilerplate code.
"""
 
# Import models and form classes from corresponding modules.
MODELS = __import__('models',level=2,globals=globals(),locals=locals())
FORMS = __import__('forms',level=2,globals=globals(),locals=locals())
 
# Any model that should explicitly not get this treatment goes here
EXCLUDE_MODELS = ['ActionLog','StatusMixin']
 
# Extra context variables to be added to each view
EXTRA_CONTEXT = {}
for k in MODELS.__all__:
if k in EXCLUDE_MODELS:
continue
EXTRA_CONTEXT[k] = {}
EXTRA_CONTEXT[k].update({ 'modelclass':getattr(MODELS,k) })
for k in ['Model1','Model2','Model3']:
EXTRA_CONTEXT[k].update({ 'mode':'manager' })
for k in ['Model4','Model5','Model6']:
EXTRA_CONTEXT[k].update({ 'mode':'admin' })
 
# Extra kwargs to be passed as kwargs to CreateView/UpdateView constructors.
EXTRA_FORM_KWARGS = {}
for k in MODELS.__all__:
if k in EXCLUDE_MODELS:
continue
EXTRA_FORM_KWARGS[k] = {}
formclass = getattr(FORMS,k+'Form')
if issubclass(formclass,FORMS.LogActionMixin):
EXTRA_FORM_KWARGS[k].update({ 'user' : get_request_user })
if issubclass(formclass,FORMS.CreateOrUpdateMixin):
EXTRA_FORM_KWARGS[k].update({ 'is_createview' : get_createview })
for k in ['Model1', 'Model2']:
EXTRA_FORM_KWARGS[k].update({ 'extra_values' : { 'company' : get_request_company } })
 
# Create views from imported modules
VIEWS = {}
for classname in MODELS.__all__:
if classname in EXCLUDE_MODELS:
continue
cls = getattr(MODELS,classname)
if issubclass(cls, Model):
VIEWS[classname] = cbv_factory(
cls,
extra_context=EXTRA_CONTEXT.get(classname,{}),
form_class=getattr(FORMS,classname+'Form'),
extra_form_kwargs=EXTRA_FORM_KWARGS.get(classname,{})
)
 
return VIEWS
 
def generate_urls():
"""
Generates URLs from manufactured views
"""
urlpatterns = []
 
# URL pattern templates for each type of view
VIEW_PATTERNS = {
'list':'^%s/$',
'detail':'^%s/(?P<pk>\d+)/$',
'create':'^%s/add/$',
'update':'^%s/(?P<pk>\d+)/edit/$',
'delete':'^%s/(?P<pk>\d+)/delete/$',
}
 
VIEWS = manufacture_views()
 
for m, mod in VIEWS.items():
for t, cls in mod.items():
urlpatterns += patterns('',url(
VIEW_PATTERNS[t] % m.lower(),
cls.as_view(),
name=('%s_%s') % (m.lower(), t.lower())
))
return urlpatterns

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.