A Django app for easily adding object tools in the Django admin

Overview

Django Object Actions

Build Status

If you've ever tried making admin object tools you may have thought, "why can't this be as easy as making Django Admin Actions?" Well now they can be.

Quick-Start Guide

Install Django Object Actions:

$ pip install django-object-actions

Add django_object_actions to your INSTALLED_APPS so Django can find our templates.

In your admin.py:

from django_object_actions import DjangoObjectActions

class ArticleAdmin(DjangoObjectActions, admin.ModelAdmin):
    def publish_this(self, request, obj):
        publish_obj(obj)
    publish_this.label = "Publish"  # optional
    publish_this.short_description = "Submit this article"  # optional

    change_actions = ('publish_this', )

Usage

Defining new &tool actions is just like defining regular admin actions. The major difference is the functions for django-object-actions will take an object instance instead of a queryset (see Re-using Admin Actions below).

Tool actions are exposed by putting them in a change_actions attribute in your admin.ModelAdmin. You can also add tool actions to the main changelist views too. There, you'll get a queryset like a regular admin action:

from django_object_actions import DjangoObjectActions

class MyModelAdmin(DjangoObjectActions, admin.ModelAdmin):
    def toolfunc(self, request, obj):
        pass
    toolfunc.label = "This will be the label of the button"  # optional
    toolfunc.short_description = "This will be the tooltip of the button"  # optional

    def make_published(modeladmin, request, queryset):
        queryset.update(status='p')

    change_actions = ('toolfunc', )
    changelist_actions = ('make_published', )

Just like admin actions, you can send a message with self.message_user. Normally, you would do something to the object and return to the same url, but if you return a HttpResponse, it will follow it (hey, just like admin actions!).

If your admin modifies get_urls, change_view, or changelist_view, you'll need to take extra care because django-object-actions uses them too.

Re-using Admin Actions

If you would like a preexisting admin action to also be an object action, add the takes_instance_or_queryset decorator to convert object instances into a queryset and pass querysets:

from django_object_actions import DjangoObjectActions, takes_instance_or_queryset

class RobotAdmin(DjangoObjectActions, admin.ModelAdmin):
    # ... snip ...

    @takes_instance_or_queryset
    def tighten_lug_nuts(self, request, queryset):
        queryset.update(lugnuts=F('lugnuts') - 1)

    change_actions = ['tighten_lug_nuts']
    actions = ['tighten_lug_nuts']

Customizing Object Actions

To give the action some a helpful title tooltip, add a short_description attribute, similar to how admin actions work:

def increment_vote(self, request, obj):
    obj.votes = obj.votes + 1
    obj.save()
increment_vote.short_description = "Increment the vote count by one"

By default, Django Object Actions will guess what to label the button based on the name of the function. You can override this with a label attribute:

def increment_vote(self, request, obj):
    obj.votes = obj.votes + 1
    obj.save()
increment_vote.label = "Vote++"

If you need even more control, you can add arbitrary attributes to the buttons by adding a Django widget style attrs attribute:

def increment_vote(self, request, obj):
    obj.votes = obj.votes + 1
    obj.save()
increment_vote.attrs = {
    'class': 'addlink',
}

Programmatically Disabling Actions

You can programmatically disable registered actions by defining your own custom get_change_actions() method. In this example, certain actions only apply to certain object states (e.g. You should not be able to close an company account if the account is already closed):

def get_change_actions(self, request, object_id, form_url):
    actions = super(PollAdmin, self).get_change_actions(request, object_id, form_url)
    actions = list(actions)
    if not request.user.is_superuser:
        return []

    obj = self.model.objects.get(pk=object_id)
    if obj.question.endswith('?'):
        actions.remove('question_mark')

    return actions

The same is true for changelist actions with get_changelist_actions.

Alternate Installation

You don't have to add this to INSTALLED_APPS, all you need to to do is copy the template django_object_actions/change_form.html some place Django's template loader will find it.

If you don't intend to use the template customizations at all, don't add django_object_actions to your INSTALLED_APPS at all and use BaseDjangoObjectActions instead of DjangoObjectActions.

More Examples

Making an action that links off-site:

def external_link(self, request, obj):
    from django.http import HttpResponseRedirect
    return HttpResponseRedirect(f'https://example.com/{obj.id}')

Limitations

  1. django-object-actions expects functions to be methods of the model admin. While Django gives you a lot more options for their admin actions.
  2. If you provide your own custom change_form.html, you'll also need to manually copy in the relevant bits of our change form .
  3. Security. This has been written with the assumption that everyone in the Django admin belongs there. Permissions should be enforced in your own actions irregardless of what this provides. Better default security is planned for the future.

Python and Django compatibility

See tox.ini for which Python and Django versions this supports.

Demo Admin & Docker images

You can try the demo admin against several versions of Django with these Docker images: https://hub.docker.com/r/crccheck/django-object-actions/tags

This runs the example Django project in ./example_project based on the "polls" tutorial. admin.py demos what you can do with this app.

Development

Getting started (with virtualenvwrapper):

# get a copy of the code
git clone [email protected]:crccheck/django-object-actions.git
cd django-object-actions
# set up your virtualenv (with virtualenvwrapper)
mkvirtualenv django-object-actions
# Install requirements
make install
# Hack your path so that we can reference packages starting from the root
add2virtualenv .
make test  # run test suite
make quickstart  # runs 'make resetdb' and some extra steps

This will install whatever the latest stable version of Django is. You can also install a specific version of Django and pip install -r requirements.txt.

Various helpers are available as make commands. Type make help and view the Makefile to see what other things you can do.

Similar Packages

If you want an actions menu for each row of your changelist, check out Django Admin Row Actions.

Django Object Actions is very similar to django-object-tools, but does not require messing with your urls.py, does not do anything special with permissions, and uses the same patterns as making admin actions.

Comments
  • 404 on Django 1.7.1

    404 on Django 1.7.1

    I have implemented the DOA library exactly as indicated in the example, and am getting a 404 with the following detail:

    (model) object with primary key u'0b637de8-ae8a-4fe6-8558-369e9eed2c34/tools/mark_reject' does not exist.

    As you can see, i'm using a UUID as my primary key. Might this be a factor? How would you recommend me debugging it? I've gone into the source code and I'm afraid I'm not skilled enough to follow what's happening...

    Thanks! Would love for this to work, it would be a lifesaver...

    bug 
    opened by dbinetti 9
  • use django.urls.re_path when available

    use django.urls.re_path when available

    Use django.urls.re_path() when available, instead of the deprecated django.conf.urls.url().

    • re_path() is available since Django 2.0.
    • url() will be removed in Django 4.0.
    opened by mathiasertl 6
  • Button URL

    Button URL

    Hi guys, Quick question and sorry if it's a bit noobish...

    But how do I set the button's url?

    For example I want the button to be brought to admin/print/schedule.html

    Thanks, Ara

    opened by asivaneswaran 6
  • update individual item for instance case

    update individual item for instance case

    The docs have

        @takes_instance_or_queryset
        def tighten_lug_nuts(self, request, queryset):
            queryset.update(lugnuts=F('lugnuts') - 1)
    

    but this updates all values since queryset seems to ignore the _result_cache (as proven by the test in my first commit). I'm not sure filter(pk=...) is the right way to implement this behavior, but @takes_instance_or_queryset seems dangerous as it is now.

    opened by AlexRiina 5
  • Documented example of a custom get_object_actions() method

    Documented example of a custom get_object_actions() method

    Includes note about that context['original'] is not available when creating / adding new objects (and object actions can't be applied in that case anyways).

    opened by maestrofjp 5
  • Use a template tag for tidier templates

    Use a template tag for tidier templates

    If a user wants to use multiple admin extensions that all want to override the change form template they will need to incorporate all the template changes in their own change_form.html

    It's therefore a good idea to keep each template customization as clean and simple as possible.

    Sorry - I haven't updated the docs to mention this enhancement. If it's a blocker let me know.

    opened by andybak 5
  • 500 on prefixing pk with

    500 on prefixing pk with "%20" in URL

    Lets say you have a model "model" inside the app "app" with object actions enabled and available for this model. Lets also assume, object app.model with pk=13 exists in the DB.

    Calling http://localhost:8000/admin/app/model/13/ works.

    Calling the detail view like this http://localhost:8000/admin/app/model/%20%2013/ results in an uncaught 500 (NoReverseMatch). The reason should be, that django allows blanks in its admin urls for the pk variable and the django-object-actions URLs don't.

    opened by tuky 4
  • Instead of getting only the checked objects, I receive query with all the entities in the table

    Instead of getting only the checked objects, I receive query with all the entities in the table

    Hello guys, Is it possible to use the check boxes in the admin UI to filter only the objects that I would like to take place in the queryset, sent to my function? I red the documentation of the package couple of times but couldn't realize how to achieve this. For the moment, it doesn't matter if I have checked something, none or all, I am getting full list of all the objects in the table. It's a great package, though, thank you for sharing it with us :) :100:

    opened by DanailDimitrovx2 3
  • fix: Objects with special symbols in primary key 404-ed

    fix: Objects with special symbols in primary key 404-ed

    for case if object in database has any of special symbols https://github.com/django/django/blob/master/django/contrib/admin/utils.py#L17 clicking on action button causes 404 error, as in SingleObjectMixin there are already parsed kwargs from url, and they are unquoted

    made unquoting kwargs

    opened by ilyachch 3
  • `changelist_actions` doesn't execute related method in 1.1.0

    `changelist_actions` doesn't execute related method in 1.1.0

    I tried to setup changelist_action but found that related method is not executed. One can reproduce that using demo app version with docker (1.9 and 2.0) and click on DELETE_ALL button in Choices model. "just kidding!" message is not showed. It works fine in 1.0.0.

    opened by matmarczak 3
  • NoReverseMatch exception thrown for custom primary keys

    NoReverseMatch exception thrown for custom primary keys

    If you attempt to attach DjangoObjectActions to a model that has a primary key that is neither hex nor decimal, a NoReverseMatch exception is thrown.

    The problem is this code in _get_action_urls():

                # change, supports pks that are numbers or uuids
                url(r'^(?P<pk>[0-9a-f\-]+)/actions/(?P<tool>\w+)/$',
    

    This seems arbitrary, considering that we're creating a new custom URL space and all primary keys have to be already URL-compatible.

    I fixed my use case by relaxing the pattern:

                url(r'^(?P<pk>[\w\\-]+)/actions/(?P<tool>\w+)/$',
    

    ...which doesn't appear to have any negative consequences.

    Recommendation:

    1. Relax the regex; or

    2. Provide a mechanism to easily override the default assumptions about primary key regex; or

    3. Make it easier to add extra urls to the results of this function. I tried to create a subclass that appended another URL pattern to the results of super(DjangoObjectActions, self)._get_action_urls() but I needed a bunch of context that's created within that method, so it was just easier to copy the entire method into the subclass and change the regex.

    opened by murraylchapman 3
  • chore(deps): bump django from 3.2.12 to 3.2.15

    chore(deps): bump django from 3.2.12 to 3.2.15

    Bumps django from 3.2.12 to 3.2.15.

    Commits
    • 653a7bd [3.2.x] Bumped version for 3.2.15 release.
    • b3e4494 [3.2.x] Fixed CVE-2022-36359 -- Escaped filename in Content-Disposition header.
    • cb7fbac [3.2.x] Fixed collation tests on MySQL 8.0.30+.
    • 840d009 [3.2.x] Fixed inspectdb and schema tests on MariaDB 10.6+.
    • a5eba20 Adjusted release notes for 3.2.15.
    • ad104fb [3.2.x] Added stub release notes for 3.2.15 release.
    • 22916c8 [3.2.x] Fixed RelatedGeoModelTest.test08_defer_only() on MySQL 8+ with MyISAM...
    • e1cfbe5 [3.2.x] Added CVE-2022-34265 to security archive.
    • 605cf0d [3.2.x] Post-release version bump.
    • 746e88c [3.2.x] Bumped version for 3.2.14 release.
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • fix: unquote object_id when calling get_change_actions

    fix: unquote object_id when calling get_change_actions

    I was recently overriding get_change_actions() as instructed in the documentation, however I was having problems due to a model having a CharField for the primary key, and specifically values that included underscore characters. I was receiving what seemed to be invalid values in object_id which extra characters.

    By default the django admin quotes primary key values via the quote() function in "django/contrib/admin/utils.py". This is to prevent certain characters, such as underscores, in the primary key, especially if it's a CharField, from messing up the URL. Therefore, in the admin the object_id is unquoted before calling get_object() and in other cases.

    Therefore, I think object_id should also be unquoted before calling get_change_actions so that object_id is not url quoted any more, and operations such as obj = self.model.objects.get(pk=object_id) will succeed as expected.

    This PR simply does that. I tried for a short period to get tests running, but I've never installed poetry and was having issues, and just don't have time to debug it right now, so I wasn't able to write a test to confirm the behavior, apologies.

    opened by nshafer 0
  • Ability to place action buttons within fields

    Ability to place action buttons within fields

    Hi, let me start by expressing my gratitude for your efforts into this nice project !

    When actions are related to a certain field, it makes sense to have their button rendered within that field. See for instance: Reset_password_example ("Mot de passe" meaning "Password" in French).

    I've managed to make the "Reset password" button appear in the password field by dedicating the help_text to it:

    class UserChangeForm(admin_forms.UserChangeForm):
        class Meta(admin_forms.UserChangeForm.Meta):
            model = User
    
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            password = self.fields.get('password')
            if password:
                password.help_text = \
                    """<ul class="object-tools"><li class="objectaction-item" data-tool-name="reset_password">
                    <a href="/admin/users/guest/3/actions/reset_password/" title="" class="">Reset password</a>
                    </li></ul>
                    """
    

    Very hacky, but it hopefully provides you with a useful starting point for implementing this feature, would you consider it.

    Cheers

    opened by ngirard 0
  • Add label/description decorator for

    Add label/description decorator for "cleaner" code

    Perhaps it's something that only irks me, but I find that this syntax quickly becomes readable after the function goes beyond a few lines:

       def publish_this(self, request, obj):
           publish_obj(obj)
       publish_this.label = "Publish"  # optional
       publish_this.short_description = "Submit this article"  # optional
    

    I'm currently using this little decorator to take care of the labels and description which works great:

    def create_action(label=None, short_description=None):
    
        def _create_action(function):
            if label:
                function.label = label
    
            if short_description:
                function.short_description = short_description
    
            return function
    
        return _create_action
    

    Usage is like this:

        @create_action('Publish', 'Submit this article')
        def publish_this(self, request, obj):
            publish_obj(obj)
    
    opened by WoLpH 11
  • separate url name for changelist_actions

    separate url name for changelist_actions

    For me "Dupe name is fine. https://code.djangoproject.com/ticket/14259" is not working. When I click on the button/link from change list in admin function does not run

    opened by predatell 4
Releases(v4.0.0)
Owner
Chris Chang
Tell a little about yourself
Chris Chang
Jet Bridge (Universal) for Jet Admin – API-based Admin Panel Framework for your application

Jet Bridge for Jet Admin – Admin panel framework for your application Description About Jet Admin: https://about.jetadmin.io Live Demo: https://app.je

Jet Admin 1.3k Sep 26, 2022
aiohttp admin is generator for admin interface based on aiohttp

aiohttp admin is generator for admin interface based on aiohttp

Mykhailo Havelia 17 Jul 29, 2022
An improved django-admin-tools dashboard for Django projects

django-fluent-dashboard The fluent_dashboard module offers a custom admin dashboard, built on top of django-admin-tools (docs). The django-admin-tools

django-fluent 327 Sep 15, 2022
Firebase Admin Console is a centralized platform for easy viewing and maintenance of Firestore database, the back-end API is a Python Flask app.

Firebase Admin Console is a centralized platform for easy viewing and maintenance of Firestore database, the back-end API is a Python Flask app. A starting template for developers to customize, build, and even deploy the desired admin console for their DB.

Daqi Chen 1 Sep 10, 2022
📱 An extension for Django admin that makes interface mobile-friendly. Merged into Django 2.0

Django Flat Responsive django-flat-responsive is included as part of Django from version 2.0! ?? Use this app if your project is powered by an older D

elky 248 Sep 2, 2022
Disable dark mode in Django admin user interface in Django 3.2.x.

Django Non Dark Admin Disable or enable dark mode user interface in Django admin panel (Django==3.2). Installation For install this app run in termina

Artem Galichkin 5 Apr 13, 2022
Modern responsive template for the Django admin interface with improved functionality. We are proud to announce completely new Jet. Please check out Live Demo

Django JET Modern template for Django admin interface with improved functionality Attention! NEW JET We are proud to announce completely new Jet. Plea

Geex Arts 3.3k Sep 25, 2022
Drop-in replacement of Django admin comes with lots of goodies, fully extensible with plugin support, pretty UI based on Twitter Bootstrap.

Xadmin Drop-in replacement of Django admin comes with lots of goodies, fully extensible with plugin support, pretty UI based on Twitter Bootstrap. Liv

差沙 4.7k Sep 28, 2022
A jazzy skin for the Django Admin-Interface (official repository).

Django Grappelli A jazzy skin for the Django admin interface. Grappelli is a grid-based alternative/extension to the Django administration interface.

Patrick Kranzlmueller 3.3k Sep 27, 2022
A Django admin theme using Twitter Bootstrap. It doesn't need any kind of modification on your side, just add it to the installed apps.

django-admin-bootstrapped A Django admin theme using Bootstrap. It doesn't need any kind of modification on your side, just add it to the installed ap

null 1.6k Sep 19, 2022
django's default admin interface made customizable. popup windows replaced by modals. :mage: :zap:

django-admin-interface django-admin-interface is a modern responsive flat admin interface customizable by the admin itself. Features Beautiful default

Fabio Caccamo 1.2k Sep 21, 2022
Extendable, adaptable rewrite of django.contrib.admin

django-admin2 One of the most useful parts of django.contrib.admin is the ability to configure various views that touch and alter data. django-admin2

Jazzband 1.2k Sep 28, 2022
Modern theme for Django admin interface

Django Suit Modern theme for Django admin interface. Django Suit is alternative theme/skin/extension for Django administration interface. Project home

Kaspars Sprogis 2.2k Sep 30, 2022
Django application and library for importing and exporting data with admin integration.

django-import-export django-import-export is a Django application and library for importing and exporting data with included admin integration. Featur

null 2.5k Oct 3, 2022
:honey_pot: A fake Django admin login screen page.

django-admin-honeypot django-admin-honeypot is a fake Django admin login screen to log and notify admins of attempted unauthorized access. This app wa

Derek Payton 897 Oct 2, 2022
"Log in as user" for the Django admin.

django-loginas About "Login as user" for the Django admin. loginas supports Python 3 only, as of version 0.4. If you're on 2, use 0.3.6. Installing dj

Stavros Korokithakis 318 Sep 14, 2022
Visually distinguish environments in Django Admin

django-admin-env-notice Visually distinguish environments in Django Admin. Based on great advice from post: 5 ways to make Django Admin safer by hakib

Yuri Shikanov 256 Sep 25, 2022
A new style for Django admin

Djamin Djamin a new and clean styles for Django admin based in Google projects styles. Quick start Install djamin: pip install -e git://github.com/her

Herson Leite 236 Jul 30, 2022
Responsive Theme for Django Admin With Sidebar Menu

Responsive Django Admin If you're looking for a version compatible with Django 1.8 just install 0.3.7.1. Features Responsive Sidebar Menu Easy install

Douglas Miranda 846 Sep 21, 2022