django-treebeard / treebeard / admin.py

"Django admin support for treebeard"

from django.contrib import admin, messages
from django.contrib.admin.views.main import ChangeList
from django.conf.urls.defaults import url, patterns
from django.http import HttpResponseBadRequest, HttpResponse

from treebeard.forms import MoveNodeForm
from treebeard.templatetags.admin_tree import check_empty_dict
from treebeard.exceptions import InvalidPosition, MissingNodeOrderBy, InvalidMoveToDescendant, PathOverflow

class TreeChangeList(ChangeList):

    def get_ordering(self):
        """
        Overriding default's ChangeList.get_ordering so we don't sort the
        results by '-id' as default
        """
        if not check_empty_dict(self.params):
            return super(TreeChangeList, self).get_ordering()
        return None, 'asc'


class TreeAdmin(admin.ModelAdmin):
    "Django Admin class for treebeard"
    change_list_template = 'admin/tree_change_list.html'
    form = MoveNodeForm

    def get_changelist(self, request):
        return TreeChangeList

    def queryset(self, request):
        from treebeard.al_tree import AL_Node
        if issubclass(self.model, AL_Node):
            # AL Trees return a list instead of a QuerySet for .get_tree()
            # So we're returning the regular .queryset cause we will use
            # the old admin
            return super(TreeAdmin, self).queryset(request)
        else:
            return self.model.get_tree()

    def changelist_view(self, request, extra_context=None):
        from treebeard.al_tree import AL_Node
        if issubclass(self.model, AL_Node):
            # For AL trees, use the old admin display
            self.change_list_template = 'admin/tree_list.html' 
        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()
        new_urls = patterns('',
            url('^move/$',
                self.admin_site.admin_view(self.move_node),),
            url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', {
                'packages': ('treebeard',)}),
        )
        return new_urls + urls

    def move_node(self, request):
        try:
            node_id = request.POST['node_id']
            parent_id = request.POST['parent_id']
            sibling_id = request.POST['sibling_id']
            as_child = request.POST.get('as_child', False)
            as_child = bool(int(as_child))
        except (KeyError, ValueError), e:
            # Some parameters were missing return a BadRequest
            return HttpResponseBadRequest(u'Malformed POST params')

        node = self.model.objects.get(pk=node_id)
        # Parent is not used at this time, need to handle special case
        # for root elements that do not have a parent
        #parent = self.model.objects.get(pk=parent_id)
        sibling = self.model.objects.get(pk=sibling_id)

        try:
            try:
                if as_child:
                    node.move(sibling, pos='target')
                else:
                    node.move(sibling, pos='left')
            except InvalidPosition, e:
                # This could be due two reasons (from the docs):
                # :raise InvalidPosition: when passing an invalid ``pos`` parm
                # :raise InvalidPosition: when :attr:`node_order_by` is enabled and
                #   the``pos`` parm wasn't ``sorted-sibling`` or ``sorted-child``
                # 
                # If it happened because the node is not a 'sorted-sibling' or 
                # 'sorted-child' then try to move just a child without preserving the
                # order, so try a different move
                if as_child:
                    try:
                        # Try as unsorted tree
                        node.move(sibling, pos='last-child')
                    except InvalidPosition:
                        # We are talking about a sorted tree
                        node.move(sibling, pos='sorted-child')
                else:
                    node.move(sibling)

            # Call the save method on the (reloaded) node in order to trigger
            # possible signal handlers etc.
            node = self.model.objects.get(pk=node.pk)
            node.save()
            # If we are here, means that we moved it in one of the tries
            messages.info(request, u'Moved node "%s" as %s of "%s"' % (node,
                ('sibling', 'child')[as_child], sibling))

        except (MissingNodeOrderBy, PathOverflow, InvalidMoveToDescendant,
            InvalidPosition), e:
            # 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, u'Exception raised while moving node: %s' % e)
            return HttpResponseBadRequest(u'Exception raised during move')
            
        return HttpResponse('OK')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.