开发者

Add a custom button to a Django application's admin page

开发者 https://www.devze.com 2023-01-17 03:24 出处:网络
I have an application in Django with a routine which would be available only to the admin. What I want to do is add a button to perform the routine in this application\'s section of the admin app.

I have an application in Django with a routine which would be available only to the admin. What I want to do is add a button to perform the routine in this application's section of the admin app.

Am I supposed to make a template for 开发者_StackOverflow社区it, and if that's the case, how do I add a html template for an app in the admin. Or maybe there's a command to simply add a button?


Messing with the admin forms can be complicated but i've commonly found that adding links, buttons, or extra info is easy and helpful. (Like a list of links to related objects witout making an inline, esp for things that are more viewed than edited).

From Django docs

Because of the modular design of the admin templates, it is usually neither necessary nor advisable to replace an entire template. It is almost always better to override only the section of the template which you need to change.

This will add a list over the top of the form.

Place in templates/admin/[your_app]/[template_to_override]:

{% extends "admin/change_form.html" %}

{% block form_top %}

{% for item in original.items %}
  {{ item }}
{% endfor %}

{% endblock %}


Django1.10:

1) Override admin/submit_line.html:

{% load i18n admin_urls %}
<div class="submit-row">
{% if extra_buttons %}
    {% for button in extra_buttons %}
        {{ button }}
    {% endfor %}
{% endif %}
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_delete_link %}
    {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
    <p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}" class="deletelink">{% trans "Delete" %}</a></p>
{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}
</div>

This assumes, of course, that button's string representation is an appropriate browser input or button element, and is marked safe with django.utils.safestring.mark_safe. Alternatively, you could use the safe template filter or access the attributes of button directly to construct the <input>. In my opinion, it's better to isolate such things to the python level.

2) Override MyModelAdmin.change_view:

def change_view(self, request, object_id, form_url='', extra_context=None):
    extra_context = extra_context or self.extra_context()
    return super(PollAdmin, self).change_view(
        request, object_id, form_url, extra_context=extra_context,
    )

This method enables you to add buttons to any ModelAdmin easily. Alternatively to step (1), you could extend admin/change_form.html and override block submit_row. This would be slightly more verbose due to extra tags required in the template.

If you want the extra action available across all of your models (or a specific subset) then subclass ModelAdmin with the desired functionality (an example would be to add archiving to your models. You could even add an override for delete--and the other default buttons--so that the mode is archived instead of deleted; this would require some template modifications)


You can also use django-admin-tools, which allows you to easily customize the admin front page like a dashboard. Using a LinkList, you can point to some view method and check if the user is authenticated. It goes like thies:

# dashboard.py (read more about how to create one on django-admin-tools docs)
class CustomIndexDashboard(Dashboard):
    """
    Custom index dashboard for captr.
    """
    def init_with_context(self, context):
        self.children.append(modules.LinkList(
            _('Tasks'),
            children=[
                ['Your task name', '/task']
            ]
        ))

# urls.py (mapping uri to your view function)
urlpatterns += patterns('yourapp.views',
    (r'^task$', 'task'),
)

# views.py
def task(request):
    if request.user.is_authenticated():
        update_definitions_task.delay() # do your thing here. in my case I'm using django-celery for messaging

    return redirect('/admin')


You might consider adding a custom admin action for this kind of object (similar to the built in 'delete'), if appropriate. Some benefits include: "pure Django", not having to mess with templates, and being able to act on multiple objects at once.

Django’s admin lets you write and register “actions” – simple functions that get called with a list of objects selected on the change list page. If you look at any change list in the admin, you’ll see this feature in action; Django ships with a “delete selected objects” action available to all models.

https://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/

I got the idea from this article on how to add a custom action button, which is another answer all together. I was able to get by with the simpler built-in actions though.

https://medium.com/@hakibenita/how-to-add-custom-action-buttons-to-django-admin-8d266f5b0d41


Don't mess with the admin pages.

  1. Create an "application" for this. Yes, your function is just a "routine". That's okay. Many smaller applications are a good thing.

  2. This application has nothing new in models.py. No new model. Zero lines of code.

  3. This application has a useful URL in urls.py. Something that can be used to display this admin page. One URL. Not many lines of code (less than a dozen.)

  4. This application has one view function in views.py. On "GET", this view function presents the form. On "POST", this view function does the "routine". This is the "heart" of your application. The GET -- of course -- simply returns the template for display. The POST does the real work, and returns a final status or something.

This view function is protected with a decorator so that only an admin can execute it. See http://docs.djangoproject.com/en/1.2/topics/auth/#django.contrib.auth.decorators.user_passes_test. You want to write a test for being an admin. lambda u: u.is_staff is probably it.

  1. This application has one template, presented by the GET and POST. That template has your form with your button. The one you can't add to admin easily.

  2. The tests.py is a test case with two users, one who is an admin and one who is not an admin.

No messing with built-in admin pages.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号