Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
582 lines (497 sloc) 22.5 KB
from django.conf.urls.defaults import patterns, url
from django.utils.functional import update_wrapper
from django.utils.encoding import force_unicode
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.contrib.admin import widgets
from django.contrib.admin.options import get_ul_class
from django.contrib.contenttypes.models import ContentType
from django import forms
from dockit.paginator import Paginator
from dockit.forms import DocumentForm
from dockit.forms.fields import PrimitiveListField, HiddenJSONField
from dockit.models import DockitPermission
import views
from widgets import AdminPrimitiveListWidget
from breadcrumbs import Breadcrumb
from objecttools import LinkObjectTool
FORMFIELD_FOR_FIELD_DEFAULTS = {
forms.DateTimeField: {
'form_class': forms.SplitDateTimeField,
'widget': widgets.AdminSplitDateTime
},
forms.DateField: {'widget': widgets.AdminDateWidget},
forms.TimeField: {'widget': widgets.AdminTimeWidget},
#models.TextField: {'widget': widgets.AdminTextareaWidget},
#models.URLField: {'widget': widgets.AdminURLFieldWidget},
forms.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
#models.BigIntegerField: {'widget': widgets.AdminIntegerFieldWidget},
forms.CharField: {'widget': widgets.AdminTextInputWidget},
forms.ImageField: {'widget': widgets.AdminFileWidget},
forms.FileField: {'widget': widgets.AdminFileWidget},
}
class SchemaAdmin(object):
#class based views
create = views.CreateView
update = views.UpdateView
delete = views.DeleteView
history = views.HistoryView
select_schema = views.SchemaTypeSelectionView
field_list_index = views.ListFieldIndexView
raw_id_fields = ()
fields = None
exclude = []
fieldsets = None
form = forms.ModelForm #only for legacy purposes, remove this and django admin complains
form_class = None
filter_vertical = ()
filter_horizontal = ()
radio_fields = {}
prepopulated_fields = {}
formfield_overrides = {}
readonly_fields = ()
declared_fieldsets = None
save_as = False
save_on_top = False
paginator = Paginator
inlines = []
#list display options
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 = ()
date_hierarchy = None
ordering = None
# Custom templates (designed to be over-ridden in subclasses)
add_form_template = None
change_form_template = None
change_list_template = None
delete_confirmation_template = None
delete_selected_confirmation_template = None
object_history_template = None
schema = None
proxy_django_model = DockitPermission #if we really need to give a model to django, let it be this one
def __init__(self, model, admin_site, schema=None, documentadmin=None):
self.model = model
self.admin_site = admin_site
self.app_name = model._meta.app_label +'_'+ model._meta.module_name
self.app_label = model._meta.app_label
overrides = FORMFIELD_FOR_FIELD_DEFAULTS.copy()
overrides.update(self.formfield_overrides)
self.formfield_overrides = overrides
self.schema = schema
self.documentadmin = documentadmin
def get_view_kwargs(self):
return {'admin':self,
'admin_site':self.admin_site,}
def _media(self):
from django.conf import settings
from django import forms
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js',
'js/jquery.min.js', 'js/jquery.init.js']
#if self.actions is not None:
# js.extend(['js/actions.min.js'])
if self.prepopulated_fields:
js.append('js/urlify.js')
js.append('js/prepopulate.min.js')
#if self.opts.get_ordered_objects():
# js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
return forms.Media(js=['%s%s' % (getattr(settings, 'ADMIN_MEDIA_PREFIX', 'admin'), url_s) for url_s in js])
media = property(_media)
def has_add_permission(self, request):
return True
def has_change_permission(self, request, obj=None):
return True
def has_delete_permission(self, request, obj=None):
return True
def as_view(self, view, cacheable=False):
return self.admin_site.admin_view(view, cacheable)
def get_create_view(self, **kwargs):
params = self.get_view_kwargs()
params.update(kwargs)
return self.as_view(self.create.as_view(**params))
def get_update_view(self, **kwargs):
params = self.get_view_kwargs()
params.update(kwargs)
return self.as_view(self.update.as_view(**params))
def get_select_schema_view(self, **kwargs):
params = self.get_view_kwargs()
params['schema'] = self.schema
params.update(kwargs)
return self.as_view(self.select_schema.as_view(**params))
def get_field_list_index_view(self, **kwargs):
params = self.get_view_kwargs()
params.update(kwargs)
return self.as_view(self.field_list_index.as_view(**params))
def get_model_perms(self, request):
return {
'add': self.has_add_permission(request),
'change': self.has_change_permission(request),
'delete': self.has_delete_permission(request),
}
def queryset(self, request):
return self.model.objects.all()
def get_form_class(self, request, obj=None):
"""
Returns the form class to use in this view
"""
if self.form_class:
return self.form_class
else:
return DocumentForm
def _get_schema_fields(self):
for field in self.schema._meta.fields.itervalues():
if getattr(field, 'schema', None):
yield (field, field.schema, False)
elif getattr(field, 'subfield', None) and getattr(field.subfield, 'schema', None):
yield (field, field.subfield.schema, True)
def _get_static_schema_fields(self):
fields = list()
for field, schema, many in self._get_schema_fields():
if not schema._meta.is_dynamic():
fields.append((field, schema, many))
return fields
def get_excludes(self):
excludes = set(self.exclude)
excludes.update([inline.dotpath for inline in self.inlines])
excludes.update([field.name for field, schema, many in self._get_static_schema_fields()])
return list(excludes)
def get_default_inline_instances(self, exclude=[]):
inline_instances = list()
from inlines import StackedInline
for field, schema, many in self._get_static_schema_fields():
if field.name in exclude:
continue
kwargs = {'dotpath':field.name + '.*'}
if not many:
kwargs['max_num'] = 1
kwargs['dotpath'] = field.name
inline_instance = StackedInline(self.model, self.admin_site, schema, self.documentadmin, **kwargs)
inline_instances.append(inline_instance)
return inline_instances
def get_inline_instances(self):
inline_instances = list()
seen = set()
for inline_class in self.inlines:
inline_instance = inline_class(self.model, self.admin_site, None, self.documentadmin)
inline_instances.append(inline_instance)
seen.add(inline_instance.dotpath)
inline_instances.extend(self.get_default_inline_instances(exclude=seen))
return inline_instances
def get_formsets(self, request, view, obj=None):
inline_instances = self.get_inline_instances()
for inline in inline_instances:
yield inline.get_formset(request, view=view, obj=obj)
def get_fieldsets(self, request, obj=None):
"Hook for specifying fieldsets for the add form."
if self.declared_fieldsets:
return self.declared_fieldsets
#form = self.get_form(request, obj)
#fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj))
fields = list()
excludes = self.get_excludes()
for key, field in self.schema._meta.fields.iteritems():
if key in excludes:
continue
if not field.editable:
continue
fields.append(key)
return [(None, {'fields': fields})]
def formfield_for_field(self, prop, field, view, **kwargs):
from dockit import schema
if isinstance(prop, schema.ModelReferenceField):
return self.formfield_for_foreignkey(prop, field, view, **kwargs)
if isinstance(prop, schema.ModelSetField):
return self.formfield_for_manytomany(prop, field, view, **kwargs)
request = kwargs.pop('request', None)
base_property = prop
if isinstance(prop, schema.ListField):
base_property = prop.subfield
#TODO self.raw_id_fields
#TODO ForeignKeyRawIdWidget => DocumentReferenceRawIdWidget
if prop.name in self.raw_id_fields:
if isinstance(prop, schema.ReferenceField):
pass
elif isinstance(prop, schema.ModelReferenceField):
pass
if (isinstance(base_property, schema.TypedSchemaField) or
(isinstance(base_property, schema.SchemaField) and base_property.schema._meta.typed_field)):
from fields import TypedSchemaField
field = TypedSchemaField
kwargs['dotpath'] = view.dotpath()
kwargs['params'] = request.GET.copy()
if isinstance(base_property, schema.SchemaField):
type_selector = base_property.schema._meta.fields[base_property.schema._meta.typed_field]
else:
type_selector = base_property
kwargs['schema_property'] = type_selector
if view.next_dotpath():
kwargs['required'] = False
return field(**kwargs)
if issubclass(field, HiddenJSONField):
return self.formfield_for_jsonfield(prop, field, view, request=request, **kwargs)
if issubclass(field, PrimitiveListField) and 'subfield' in kwargs:
subfield_kwargs = prop.subfield.formfield_kwargs()
subfield_kwargs.pop('initial', None)
subfield = self.formfield_for_field(prop, type(kwargs['subfield']), view, **subfield_kwargs)
kwargs['subfield'] = subfield
kwargs['widget'] = AdminPrimitiveListWidget
if field in self.formfield_overrides:
opts = dict(self.formfield_overrides[field])
field = opts.pop('form_class', field)
kwargs = dict(opts, **kwargs)
return field(**kwargs)
def formfield_for_jsonfield(self, prop, field, view, **kwargs):
request = kwargs.pop('request', None)
from fields import DotPathField
field = DotPathField
kwargs['dotpath'] = view.dotpath()
kwargs['params'] = request.GET.copy()
if view.next_dotpath():
kwargs['required'] = False
return field(**kwargs)
def raw_id_widget_for_property(self,prop,WidgetType):
class FauxRel(object):
def get_related_field(self):
return self
rel = FauxRel()
rel.to = prop.model
rel.limit_choices_to = {}
rel.name = "pk"
thewidget = WidgetType(rel=rel)
# Monkey patch the render function to replace
# the hardcoded admin relative url
thewidget._render = thewidget.render
def _tmprender(name,value,attrs=None):
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse
output = thewidget._render(name,value,attrs=attrs)
return mark_safe(output.replace("../../../",reverse("admin:index")))
thewidget.render = _tmprender
return thewidget
def formfield_for_foreignkey(self, prop, field, view, **kwargs):
"""
Get a form Field for a ForeignKey.
"""
request = kwargs.pop('request', None)
if True: #prop.name in self.raw_id_fields:
kwargs["widget"] = self.raw_id_widget_for_property(prop,widgets.ForeignKeyRawIdWidget)
elif prop.name in self.radio_fields:
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
'class': get_ul_class(self.radio_fields[prop.name]),
})
kwargs['empty_label'] = prop.blank and _('None') or None
return prop.formfield(**kwargs)
def formfield_for_manytomany(self, prop, field, view, **kwargs):
"""
Get a form Field for a ManyToManyField.
"""
request = kwargs.pop('request', None)
if True: #prop.name in self.raw_id_fields:
kwargs['widget'] = self.raw_id_widget_for_property(prop,widgets.ManyToManyRawIdWidget)
kwargs['help_text'] = ''
elif prop.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
kwargs['widget'] = widgets.FilteredSelectMultiple(prop.verbose_name, (prop.name in self.filter_vertical))
return prop.formfield(**kwargs)
def log_addition(self, request, object):
return self.documentadmin.log_addition(request, object)
def log_change(self, request, object, message):
return self.documentadmin.log_change(request, object, message)
def log_deletion(self, request, object, object_repr):
return self.documentadmin.log_deletion(request, object, object_repr)
def reverse(self, name, *args, **kwargs):
return self.documentadmin.reverse(name, *args, **kwargs)
def get_readonly_fields(self, request, schema=None):
schema = schema or self.schema
read_only = list()
for key, field in schema._meta.fields.iteritems():
if not field.editable:
read_only.append(key)
return read_only
def get_paginator(self, request, query_set, paginate_by):
return self.paginator(query_set, paginate_by)
def get_object_tools(self, request, object=None, add=False):
#object tools are renderable object that are displayed at the top the admin
init = self.get_view_kwargs()
if object:
return [self.history(**init).get_object_tool(request, object)]
elif add:
if self.schema:
opts = self.schema._meta
else:
opts = self.model._meta
title = _('Add %s') % force_unicode(opts.verbose_name)
return [LinkObjectTool(title, self.reverse('%s_add' % self.app_name), css_class='addlink')]
return []
def get_field(self, schema, dotpath, obj=None):
field = None
if dotpath and obj:
field = obj.dot_notation_to_field(dotpath)
if field is None:
parent_path = dotpath.rsplit('.', 1)[0]
print 'no field', dotpath, obj
from dockit.schema.common import DotPathTraverser
traverser = DotPathTraverser(parent_path)
traverser.resolve_for_instance(obj)
info = traverser.resolved_paths
subschema = info[2]['field'].schema
fields = subschema._meta.fields
field = obj.dot_notation_to_field(parent_path)
data = obj._primitive_data
assert field
return field
def get_base_breadcrumbs(self, top=False):
return self.documentadmin.get_base_breadcrumbs(top)
def get_instance_breadcrumb(self, obj=None, include_link=False):
return self.documentadmin.get_instance_breadcrumb(obj, include_link)
class DocumentAdmin(SchemaAdmin):
# Actions
actions = []
#action_form = helpers.ActionForm
actions_on_top = True
actions_on_bottom = False
actions_selection_counter = True
#the following proxy to the proper schema admin
create = views.DocumentProxyView
update = views.DocumentProxyView
delete = views.DeleteView
index = views.IndexView
detail_views = [views.HistoryView, views.DeleteView]
default_schema_admin = SchemaAdmin
schema_inlines = [] # [(Schema, SchemaAdminCls),]
def get_urls(self):
def wrap(view, cacheable=False):
def wrapper(*args, **kwargs):
return self.as_view(view, cacheable)(*args, **kwargs)
return update_wrapper(wrapper, view)
init = self.get_view_kwargs()
# Admin-site-wide views.
urlpatterns = self.get_extra_urls()
urlpatterns += patterns('',
url(r'^$',
wrap(self.index.as_view(**init)),
name=self.app_name+'_changelist'),
url(r'^add/$',
wrap(self.create.as_view(**init)),
name=self.app_name+'_add'),
)
for key, view in self.get_detail_views().iteritems():
url_p = r'^(?P<pk>.+)/%s/$' % key
if hasattr(view, 'get_url_pattern'):
url_p = view.get_url_pattern(self)
urlpatterns += patterns('',
url(url_p,
wrap(view.as_view(**init)),
name='%s_%s' % (self.app_name, key)),
)
urlpatterns += patterns('', #we shouldn't put anything after this url
url(r'^(?P<pk>.+)/$',
wrap(self.update.as_view(**init)),
name=self.app_name+'_change'),
)
return urlpatterns
def urls(self):
return self.get_urls(), self.app_name, None
urls = property(urls)
def get_extra_urls(self):
return patterns('',)
def get_detail_views(self):
ret = dict()
for detail_view in self.detail_views:
ret[detail_view.key] = detail_view
return ret
def get_changelist(self, request):
from changelist import ChangeList
return ChangeList
def reverse(self, name, *args, **kwargs):
from django.core.urlresolvers import get_urlconf, get_resolver
urlconf = get_urlconf()
resolver = get_resolver(urlconf)
app_list = resolver.app_dict['admin']
return reverse('%s:%s' % (self.admin_site.name, name), args=args, kwargs=kwargs, current_app=self.app_name)
def create_admin_for_schema(self, schema, object=None):
admin_class = self.get_admin_class_for_schema(schema)
return admin_class(self.model, self.admin_site, schema, self)
def get_admin_class_for_schema(self, schema):
for cls, admin_class in self.schema_inlines:
if schema == cls:
return admin_class
return self.default_schema_admin
def log_addition(self, request, object):
"""
Log that an object has been successfully added.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, ADDITION
object_id = '%s:%s' % (object._meta.collection, object.pk)
LogEntry.objects.log_action(
user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(self.proxy_django_model).pk,
object_id = object_id,
object_repr = force_unicode(object),
action_flag = ADDITION
)
def log_change(self, request, object, message):
"""
Log that an object has been successfully changed.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, CHANGE
object_id = '%s:%s' % (object._meta.collection, object.pk)
LogEntry.objects.log_action(
user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(self.proxy_django_model).pk,
object_id = object_id,
object_repr = force_unicode(object),
action_flag = CHANGE,
change_message = message
)
def log_deletion(self, request, object, object_repr):
"""
Log that an object will be deleted. Note that this method is called
before the deletion.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, DELETION
object_id = '%s:%s' % (object._meta.collection, object.pk)
LogEntry.objects.log_action(
user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(self.proxy_django_model).pk,
object_id = object_id,
object_repr = object_repr,
action_flag = DELETION
)
def get_base_breadcrumbs(self, top=False):
admin_name = self.admin_site.name
model_name = self.model._meta.verbose_name
opts = self.model._meta
breadcrumbs = [
Breadcrumb('Home', ['%s:index' % admin_name]),
Breadcrumb(opts.app_label, ['%s:app_list' % admin_name, (self.app_label,), {}]),
]
if top:
breadcrumbs.append(Breadcrumb(opts.verbose_name_plural))
else:
breadcrumbs.append(Breadcrumb(opts.verbose_name_plural, ['%s:%s_changelist' % (admin_name, self.app_name)]))
return breadcrumbs
def get_instance_breadcrumb(self, obj=None, include_link=False):
if obj:
if include_link:
return Breadcrumb(unicode(obj), self.reverse(self.app_name+'_change', obj.pk))
return Breadcrumb(unicode(obj))
return Breadcrumb('Add %s' % self.model._meta.verbose_name)
def has_add_permission(self, request):
opts = self.model._meta
#print opts.collection, request.user.has_perm('dockit.'+ opts.get_add_permission()), opts.get_add_permission()
#print request.user.user_permissions.all().filter(codename=opts.get_add_permission()).values_list('codename', 'content_type')
return request.user.has_perm('dockit.'+ opts.get_add_permission())
def has_change_permission(self, request, obj=None):
opts = self.model._meta
return request.user.has_perm('dockit.'+ opts.get_change_permission(), obj)
def has_delete_permission(self, request, obj=None):
opts = self.model._meta
return request.user.has_perm('dockit.'+ opts.get_delete_permission(), obj)
Something went wrong with that request. Please try again.