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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
"""Django admin support for treebeard"""
import sys
import django
from django.conf import settings
from django.conf.urls import url
from django.contrib import admin, messages
from django.http import HttpResponse, HttpResponseBadRequest
from django.utils.translation import ugettext_lazy as _
if sys.version_info >= (3, 0):
from django.utils.encoding import force_str
else:
from django.utils.encoding import force_unicode as force_str
from treebeard.exceptions import (InvalidPosition, MissingNodeOrderBy,
InvalidMoveToDescendant, PathOverflow)
from treebeard.al_tree import AL_Node
try:
from django.contrib.admin.options import TO_FIELD_VAR
except ImportError:
from django.contrib.admin.views.main import TO_FIELD_VAR
class TreeAdmin(admin.ModelAdmin):
"""Django Admin class for treebeard."""
change_list_template = 'admin/tree_change_list.html'
def get_queryset(self, request):
if issubclass(self.model, AL_Node):
# AL Trees return a list instead of a QuerySet for .get_tree()
# So we're returning the regular .get_queryset cause we will use
# the old admin
return super(TreeAdmin, self).get_queryset(request)
else:
return self.model.get_tree()
def changelist_view(self, request, extra_context=None):
if issubclass(self.model, AL_Node):
# For AL trees, use the old admin display
self.change_list_template = 'admin/tree_list.html'
if extra_context is None:
extra_context = {}
if django.VERSION < (1, 10):
request_context = 'django.core.context_processors.request' in settings.TEMPLATE_CONTEXT_PROCESSORS
else:
request_context = any(
map(
lambda tmpl:
tmpl.get('BACKEND', None) == 'django.template.backends.django.DjangoTemplates' and
tmpl.get('APP_DIRS', False) and
'django.template.context_processors.request' in tmpl.get('OPTIONS', {}).get('context_processors', []),
settings.TEMPLATES
)
)
lacks_request = ('request' not in extra_context and not request_context)
if lacks_request:
extra_context['request'] = request
return super(TreeAdmin, self).changelist_view(request, extra_context)
def get_urls(self):
"""
Adds a url to move nodes to this admin
"""
urls = super(TreeAdmin, self).get_urls()
if django.VERSION < (1, 10):
from django.views.i18n import javascript_catalog
jsi18n_url = url(r'^jsi18n/$', javascript_catalog, {'packages': ('treebeard',)})
else:
from django.views.i18n import JavaScriptCatalog
jsi18n_url = url(r'^jsi18n/$',
JavaScriptCatalog.as_view(packages=['treebeard']),
name='javascript-catalog'
)
new_urls = [
url('^move/$', self.admin_site.admin_view(self.move_node), ),
jsi18n_url,
]
return new_urls + urls
def get_node(self, node_id):
return self.model.objects.get(pk=node_id)
def try_to_move_node(self, as_child, node, pos, request, target):
try:
node.move(target, pos=pos)
# Call the save method on the (reloaded) node in order to trigger
# possible signal handlers etc.
node = self.get_node(node.pk)
node.save()
except (MissingNodeOrderBy, PathOverflow, InvalidMoveToDescendant,
InvalidPosition):
e = sys.exc_info()[1]
# An error was raised while trying to move the node, then set an
# error message and return 400, this will cause a reload on the
# client to show the message
messages.error(request,
_('Exception raised while moving node: %s') % _(
force_str(e)))
return HttpResponseBadRequest('Exception raised during move')
if as_child:
msg = _('Moved node "%(node)s" as child of "%(other)s"')
else:
msg = _('Moved node "%(node)s" as sibling of "%(other)s"')
messages.info(request, msg % {'node': node, 'other': target})
return HttpResponse('OK')
def move_node(self, request):
try:
node_id = request.POST['node_id']
target_id = request.POST['sibling_id']
as_child = bool(int(request.POST.get('as_child', 0)))
except (KeyError, ValueError):
# Some parameters were missing return a BadRequest
return HttpResponseBadRequest('Malformed POST params')
node = self.get_node(node_id)
target = self.get_node(target_id)
is_sorted = True if node.node_order_by else False
pos = {
(True, True): 'sorted-child',
(True, False): 'last-child',
(False, True): 'sorted-sibling',
(False, False): 'left',
}[as_child, is_sorted]
return self.try_to_move_node(as_child, node, pos, request, target)
def admin_factory(form_class):
"""Dynamically build a TreeAdmin subclass for the given form class.
:param form_class:
:return: A TreeAdmin subclass.
"""
return type(
form_class.__name__ + 'Admin',
(TreeAdmin,),
dict(form=form_class))
|