0

I'm making an admin panel for a Django-Mptt tree structure using the FeinCMS TreeEditor interface. This interface provides an 'actions column' per-node for things like adding or moving nodes quickly without using the typical Django admin action select box.

Example inteface from Django/TreeEditor/MPTT app

What I am trying to do is add a custom admin action to this collection which passes the pk of the node to a celery task which will then add a collection of nodes as children. Existing functions are simply href links to the URL for that task(add/delete/move), so thus far I have simply mimicked this.

My solution currently involves:

  1. Define the action as a function on the model
  2. Create a view which uses this function and redirects back to the changelist
  3. Add this view to the admin URLs
  4. Super the TreeEditor actions column into the ModelAdmin class
  5. Add an action to the collection which calls this URL

Surely there must be a better method than this? It works, but it feels massively convoluted and un-DRY, and I'm sure it'll break in odd ways.

Unfortunately I'm only a month or two into working with Django so there's probably some obvious functions I could be using. I suspect that I might be able to do something with get_urls() and defining the function directly in the ModelAdmin, or use a codeblock within the injected HTML to call the function directly, though I'm not sure how and whether it's considered a better option.

Code: I've renamed everything to a simpler library <> books example to remove the unrelated functionality from the above example image.

models.py

class Library(models.Model):
    def get_books(self):
        # Celery task; file omitted for brevity
        get_books_in_library.delay(self.pk)

views.py

def get_books_in_library(request, library_id):
    this_library = Library.objects.get(pk=library_id)
    this_library.get_books_in_library()
    messages.add_message(request, messages.SUCCESS, 'Library "{0}" books requested.'.format(this_library.name))
    redirect_url = urlresolvers.reverse('admin:myapp_library_changelist')
    return HttpResponseRedirect(redirect_url)

urls.py

urlpatterns = [
    url(r'^admin/myapp/library/(?P<library_id>[0-9]+)/get_books/$', get_books_in_library, name='get books in library'),
    url(r'^admin/', include(admin.site.urls)),
]

admin.py

class LibraryAdmin(TreeEditor):
    model = Library
    def _actions_column(self, obj):
        actions = super(LibraryAdmin, self)._actions_column(obj)
        actions.insert(
            0, u'<a title="{0}" href="{1}/get_books"><img src="{2}admin/img/icon_addlink.gif" alt="{0}" /></a>'.format(
                _('Get Books'),
                obj.pk,
                settings.STATIC_URL
            )
        )
        return actions

Note that I may have broken something in renaming things and removing the extraneous cruft if you try to execute this code, I think it should adequately illustrate what I'm trying to do here however.

4

1 回答 1

0

在今天四处挖掘并简单地尝试了各种其他解决方案之后,我将一个使用 get_urls 和一个直接定义到管理界面中的视图放在一起,尽管它实际上只是将代码从多个 django 文件移动到管理界面中,但感觉更整洁——尽管它确实使用管理包装器来阻止未经身份验证的用户,这是一个改进。

我会在此处为将来发现此代码的任何人留下一份工作代码的副本,因为我很少看到 TreeEditor 等人的示例。在较新版本的 Django 中使用。

class NodeAdmin(TreeEditor):
    model = Node
    # < ... > Other details removed for brevity
    def get_urls(self):
        urls = super(NodeAdmin, self).get_urls()
        my_urls = [
            url(r'^(?P<node_id>[0-9]+)/get_suggestions/$', self.admin_site.admin_view(self.get_suggestions)),
        ]
        return my_urls + urls

    def get_suggestions(self, request, node_id):
        this_node = Node.objects.get(pk=node_id)
        get_suggestions(this_node.pk)
        messages.add_message(request, messages.SUCCESS, 'Requested suggestions for {0}'.format(this_node.term))
        redirect_url = urlresolvers.reverse('admin:trinket_node_changelist')
        return HttpResponseRedirect(redirect_url)


    def _actions_column(self, obj):
        actions = super(NodeAdmin, self)._actions_column(obj)
        # Adds an 'get suggestions' action to the Node editor using a search icon
        actions.insert(
            0, u'<a title="{0}" href="{1}/get_suggestions"><img src="{2}admin/img/selector-search.gif" alt="{0}" /></a>'.format(
                _('Get Suggestions'),
                obj.pk,
                settings.STATIC_URL,
            )
        )
        # Adds an 'add child' action to the Node editor using a plus icon
        actions.insert(
            0, u'<a title="{0}" href="add/?{1}={2}"><img src="{3}admin/img/icon_addlink.gif" alt="{0}" /></a>'.format(
                _('Add child'),
                getattr(self.model._meta,'parent_attr', 'parent'),
                obj.pk,
                settings.STATIC_URL
            )
        )
        return actions
于 2015-09-28T14:56:56.893 回答