Skip to content

Instantly share code, notes, and snippets.

@egasimus
Last active October 25, 2020 00:00
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save egasimus/5248517 to your computer and use it in GitHub Desktop.
Save egasimus/5248517 to your computer and use it in GitHub Desktop.
Django CBV factory function, with example usage.
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
}
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
@riajulkashem
Copy link

What about permission mixin ?

@egasimus
Copy link
Author

Who cares?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment