Skip to main content

django-inline-actions adds actions to each row of the ModelAdmin or InlineModelAdmin.

Project description

django-inline-actions

pypi Build Status Coverage python version Project status license

django-inline-actions adds actions to the InlineModelAdmin and ModelAdmin changelist.

Screenshot

Changelist example Inline example

Installation

NOTE If you are on django<2.0, you have to use django-inline-actions<2.0.

  1. Install django-inline-actions

    pip install django-inline-actions

  2. Add inline_actions to your INSTALLED_APPS.

Integration

Add the InlineActionsModelAdminMixin to your ModelAdmin. If you want to have actions on your inlines, add the InlineActionMixin to your InlineModelAdmin. Each action is implemented as a method on the ModelAdmin/InlineModelAdmin and must have the following signature.

def action_name(self, request, obj, parent_obj=None):
Argument Description
request current request
obj instance on which the action was triggered
parent_obj instance of the parent model, only set on inlines

and should return None to return to the current changeform or a HttpResponse. Finally, add your method name to list of actions inline_actions defined on the corresponding ModelAdmin. If you want to disable the actions column, you have to explicitly set inline_actions = None. To add your actions dynamically, you can use the method get_inline_actions(self, request, obj=None) instead.

This module is bundled with two actions for viewing (inline_actions.actions.ViewAction) and deleting (inline_actions.actions.DeleteAction). Just add these classes to your admin and you're done.

Additionally, you can add methods to generate a custom label and CSS classes per object. If you have an inline action called action_name then you can define

def get_action_name_label(self, obj):
    return 'some string'

def get_action_name_css(self, obj):
    return 'some string'
Argument Description
obj instance on which the action was triggered

Each defined method has to return a string.

Example 1

Imagine a simple news application with the following admin.py.

from django.contrib import admin
from inline_actions.admin import InlineActionsMixin
from inline_actions.admin import InlineActionsModelAdminMixin

from .models import Article, Author


class ArticleInline(InlineActionsMixin,
                    admin.TabularInline):
    model = Article
    inline_actions = []

    def has_add_permission(self):
        return False


@admin.register(Author)
class AuthorAdmin(InlineActionsModelAdminMixin,
                  admin.ModelAdmin):
    inlines = [ArticleInline]
    list_display = ('name',)


@admin.register(Article)
class AuthorAdmin(admin.ModelAdmin):
    list_display = ('title', 'status', 'author')

We now want to add two simple actions (view, unpublish) to each article within the AuthorAdmin. The view action redirects to the changeform of the selected instance.

from django.core.urlresolvers import reverse
from django.shortcuts import redirect


class ArticleInline(InlineActionsMixin,
                    admin.TabularInline):
    # ...
    inline_actions = ['view']
    # ...

    def view(self, request, obj, parent_obj=None):
        url = reverse(
            'admin:{}_{}_change'.format(
                obj._meta.app_label,
                obj._meta.model_name,
            ),
            args=(obj.pk,)
        )
        return redirect(url)
    view.short_description = _("View")

Since unpublish depends on article.status we must use get_inline_actions to add this action dynamically.

from django.contrib import admin, messages
from django.utils.translation import ugettext_lazy as _


class ArticleInline(InlineActionsMixin,
                    admin.TabularInline):
    # ...
    def get_inline_actions(self, request, obj=None):
        actions = super(ArticleInline, self).get_inline_actions(request, obj)
        if obj:
            if obj.status == Article.PUBLISHED:
                actions.append('unpublish')
        return actions

    def unpublish(self, request, obj, inline_obj):
        inline_obj.status = Article.DRAFT
        inline_obj.save()
        messages.info(request, _("Article unpublished"))
    unpublish.short_description = _("Unpublish")

Adding inline_actions to the changelist works similar. See the sample project for further details (test_proj/blog/admin.py).

Example 2

Instead of creating separate actions for publishing and unpublishing, we might prefer an action, which toggles between those two states. toggle_publish implements the behaviour described above.

def toggle_publish(self, request, obj, parent_obj=None):
    if obj.status == Article.DRAFT:
        obj.status = Article.PUBLISHED
    else:
        obj.status = Article.DRAFT

    obj.save()

    if obj.status == Article.DRAFT:
        messages.info(request, _("Article unpublished."))
    else:
        messages.info(request, _("Article published."))

This might leave the user with an ambiguous button label as it will be called Toggle publish regardless of the internal state. We can specify a dynamic label by adding a special method get_ACTIONNAME_label.

def get_toggle_publish_label(self, obj):
    if obj.status == Article.DRAFT:
        return 'Publish'
    return 'Unpublish'

So assuming an object in a row has DRAFT status, then the button label will be Toggle publish and Toggle unpublish otherwise.

We can go even fancier when we create a method that will add css classes for each object depending on a status like:

def get_toggle_publish_css(self, obj):
    if obj.status == Article.DRAFT:
        return 'btn-red'
    return 'btn-green'

You can make it more eye-candy by using btn-green that makes your button green and btn-red that makes your button red. Or you can use those classes to add some javascript logic (i.e. confirmation box).

Example Application

You can see django-inline-actions in action using the bundled test application test_proj. Use poetry to run it.

git clone https://github.com/escaped/django-inline-actions.git
cd django-inline-actions/
poetry install
poetry run pip install Django
cd test_proj
poetry run ./manage.py migrate
poetry run ./manage.py createsuperuser
poetry run ./manage.py runserver

Open http://localhost:8000/admin/ in your browser and create an author and some articles.

How to test your actions?

There are two ways how you could write tests for your actions. We will use pytest for the following examples.

Test the action itself

Before we can call our action on the admin class itself, we have to instantiate the admin environment and pass it to the ModelAdmin together with an instance of our model. Therefore, we implement a fixture called admin_site, which is used on each test.

import pytest
from django.contrib.admin import AdminSite

from yourapp.module.admin import MyAdmin


@pytest.fixture
def admin_site():
    return AdminSite()

@pytest.mark.django_db
def test_action_XXX(admin_site):
    """Test action XXX"""
    fake_request = {}  # you might need to use a RequestFactory here
    obj = ...  # create an instance

    admin = MyAdmin(obj, admin_site)

    admin.render_inline_actions(article)
    response = admin.action_XXX(fake_request, obj)
    # assert the state of the application

Test the admin integration

Alternatively, you can test your actions on the real Django admin page. You will have to log in, navigate to the corresponding admin and trigger a click on the action. To simplify this process you can use django-webtest. Example can be found here.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-inline-actions-2.2.0.tar.gz (10.8 kB view details)

Uploaded Source

Built Distribution

django_inline_actions-2.2.0-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

Details for the file django-inline-actions-2.2.0.tar.gz.

File metadata

  • Download URL: django-inline-actions-2.2.0.tar.gz
  • Upload date:
  • Size: 10.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/0.12.11 CPython/3.7.3 Linux/5.1.7-arch1-1-ARCH

File hashes

Hashes for django-inline-actions-2.2.0.tar.gz
Algorithm Hash digest
SHA256 ddd81026560b0f42cfa16ff54b59390308795dae0ccf4916abec0a575ac4d290
MD5 8878ad569970c5659d05345bd0719c22
BLAKE2b-256 9aa7b38c1488b02ae56522ac45f8eb8e0434dcafa1fc0f16de7c7fc7734cb732

See more details on using hashes here.

File details

Details for the file django_inline_actions-2.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_inline_actions-2.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b4ae5a119de23a1d6820293fdda7a74b9cf22a3f34138cb9805c9f0ba08cbfb1
MD5 a7e312fadd7c870359b7126e7e06c41b
BLAKE2b-256 41b7137d23a0a47c5c2cf917e680d83cdc92ded82abb78ce5ad93184cb5b837e

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page