django-admin2/djadmin2/types.py
2013-06-21 10:16:08 -03:00

265 lines
8.1 KiB
Python

from collections import namedtuple
import logging
from django.core.urlresolvers import reverse
from django.conf.urls import patterns, url
import extra_views
from . import apiviews
from . import settings
from . import views
from . import actions
from . import utils
from .forms import modelform_factory
logger = logging.getLogger('djadmin2')
class ModelAdminBase2(type):
def __new__(cls, name, bases, attrs):
new_class = super(ModelAdminBase2, cls).__new__(cls, name,
bases, attrs)
view_list = [attr for attr in attrs.values()
if isinstance(attr, views.AdminView)]
setattr(new_class, 'views', view_list)
return new_class
class ModelAdmin2(object):
"""
Warning: This class is targeted for reduction.
It's bloated and ugly.
"""
__metaclass__ = ModelAdminBase2
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
list_select_related = False
list_per_page = 100
list_max_show_all = 200
list_editable = ()
search_fields = ()
save_as = False
save_on_top = False
verbose_name = None
verbose_name_plural = None
model_admin_attributes = settings.MODEL_ADMIN_ATTRS
search_fields = []
# Show the fields to be displayed as columns
# TODO: Confirm that this is what the Django admin uses
list_fields = []
#This shows up on the DocumentListView of the Posts
list_actions = [actions.DeleteSelectedAction]
# This shows up in the DocumentDetailView of the Posts.
document_actions = []
# shows up on a particular field
field_actions = {}
fields = None
exclude = None
fieldsets = None
form_class = None
filter_vertical = ()
filter_horizontal = ()
radio_fields = {}
prepopulated_fields = {}
formfield_overrides = {}
readonly_fields = ()
ordering = None
create_form_class = None
update_form_class = None
inlines = []
# Views
index_view = views.ModelListView
create_view = views.ModelAddFormView
update_view = views.ModelEditFormView
detail_view = views.ModelDetailView
delete_view = views.ModelDeleteView
views = []
# API configuration
api_serializer_class = None
# API Views
api_list_view = apiviews.ListCreateAPIView
api_detail_view = apiviews.RetrieveUpdateDestroyAPIView
def __init__(self, model, admin, name=None, **kwargs):
self.name = name
self.model = model
self.admin = admin
model_options = utils.model_options(model)
self.app_label = model_options.app_label
self.model_name = model_options.object_name.lower()
if self.name is None:
self.name = '{}_{}'.format(self.app_label, self.model_name)
if self.verbose_name is None:
self.verbose_name = model_options.verbose_name
if self.verbose_name_plural is None:
self.verbose_name_plural = model_options.verbose_name_plural
def get_default_view_kwargs(self):
return {
'app_label': self.app_label,
'model': self.model,
'model_name': self.model_name,
'model_admin': immutable_admin_factory(self),
}
def get_default_api_view_kwargs(self):
kwargs = self.get_default_view_kwargs()
kwargs.update({
'serializer_class': self.api_serializer_class,
})
return kwargs
def get_prefixed_view_name(self, view_name):
return '{}_{}'.format(self.name, view_name)
def get_index_kwargs(self):
return self.get_default_view_kwargs()
def get_create_kwargs(self):
kwargs = self.get_default_view_kwargs()
kwargs.update({
'inlines': self.inlines,
'form_class': self.create_form_class if self.create_form_class else self.form_class,
})
return kwargs
def get_update_kwargs(self):
kwargs = self.get_default_view_kwargs()
form_class = self.update_form_class if self.update_form_class else self.form_class
if form_class is None:
form_class = modelform_factory(self.model)
kwargs.update({
'inlines': self.inlines,
'form_class': form_class,
})
return kwargs
def get_detail_kwargs(self):
return self.get_default_view_kwargs()
def get_delete_kwargs(self):
return self.get_default_view_kwargs()
def get_index_url(self):
return reverse('admin2:{}'.format(self.get_prefixed_view_name('index')))
def get_api_list_kwargs(self):
kwargs = self.get_default_api_view_kwargs()
kwargs.update({
'paginate_by': self.list_per_page,
})
return kwargs
def get_api_detail_kwargs(self):
return self.get_default_api_view_kwargs()
def get_urls(self):
return patterns('',
url(
regex=r'^$',
view=self.index_view.as_view(**self.get_index_kwargs()),
name=self.get_prefixed_view_name('index')
),
url(
regex=r'^create/$',
view=self.create_view.as_view(**self.get_create_kwargs()),
name=self.get_prefixed_view_name('create')
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.detail_view.as_view(**self.get_detail_kwargs()),
name=self.get_prefixed_view_name('detail')
),
url(
regex=r'^(?P<pk>[0-9]+)/update/$',
view=self.update_view.as_view(**self.get_update_kwargs()),
name=self.get_prefixed_view_name('update')
),
url(
regex=r'^(?P<pk>[0-9]+)/delete/$',
view=self.delete_view.as_view(**self.get_delete_kwargs()),
name=self.get_prefixed_view_name('delete')
),
)
def get_api_urls(self):
return patterns('',
url(
regex=r'^$',
view=self.api_list_view.as_view(**self.get_api_list_kwargs()),
name=self.get_prefixed_view_name('api_list'),
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.api_detail_view.as_view(**self.get_api_detail_kwargs()),
name=self.get_prefixed_view_name('api_detail'),
),
)
@property
def urls(self):
# We set the application and instance namespace here
return self.get_urls(), None, None
@property
def api_urls(self):
return self.get_api_urls(), None, None
def get_list_actions(self):
actions_dict = {}
for cls in type(self).mro()[::-1]:
class_actions = getattr(cls, 'list_actions', [])
for action in class_actions:
actions_dict[action.__name__] = {
'name': action.__name__,
'description': actions.get_description(action),
'action_callable': action
}
return actions_dict
class Admin2Inline(extra_views.InlineFormSet):
"""
A simple extension of django-extra-view's InlineFormSet that
adds some useful functionality.
"""
def construct_formset(self):
"""
Overrides construct_formset to attach the model class as
an attribute of the returned formset instance.
"""
formset = super(Admin2Inline, self).construct_formset()
formset.model = self.inline_model
return formset
def immutable_admin_factory(model_admin):
""" Provide an ImmutableAdmin to make it harder for developers to dig themselves into holes.
See https://github.com/twoscoops/django-admin2/issues/99
Frozen class implementation as namedtuple suggested by Audrey Roy
Note: This won't stop developers from saving mutable objects to the result, but hopefully
developers attempting that 'workaround/hack' will read our documentation.
"""
ImmutableAdmin = namedtuple("ImmutableAdmin", model_admin.model_admin_attributes, verbose=False)
return ImmutableAdmin(*[getattr(model_admin, x) for x in model_admin.model_admin_attributes])