跳转到主要内容

django-inline-actions 为 ModelAdmin 或 InlineModelAdmin 的每一行添加操作。

项目描述

django-inline-actions

PyPI GitHub Workflow Status (master) Coveralls github branch PyPI - Python Version PyPI - License

django-inline-actions 为 ModelAdmin 或 InlineModelAdmin 的每一行添加操作。

需求

  • Python 3.6.1 或更高版本

屏幕截图

Changelist example Inline example

安装

  1. 安装 django-inline-actions

    pip install django-inline-actions
    
  2. inline_actions 添加到您的 INSTALLED_APPS

集成

InlineActionsModelAdminMixin 添加到您的 ModelAdmin。如果您想在内联中添加操作,将 InlineActionsMixin 添加到您的 InlineModelAdmin。每个操作都作为 ModelAdmin/InlineModelAdmin 上的方法实现,并且 必须 具有以下签名。

def action_name(self, request, obj, parent_obj=None):
参数 描述
request 当前请求
obj 触发操作的实例
parent_obj 父模型实例,仅在内联中设置

并且应该返回 None 以返回到当前更改表单或一个 HttpResponse。最后,将您的方法名称添加到在相应的 ModelAdmin 上定义的操作列表 inline_actions 中。如果您想禁用 操作 列,您必须显式设置 inline_actions = None。要动态添加您的操作,您可以使用方法 get_inline_actions(self, request, obj=None)

此模块包含两个动作用于查看(inline_actions.actions.ViewAction)和删除(inline_actions.actions.DeleteAction)。只需将这些类添加到您的管理员中即可。

此外,您可以添加方法来为每个对象生成自定义标签和CSS类。如果您有一个名为action_name的行内动作,则可以定义

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

def get_action_name_css(self, obj):
    return 'some string'
参数 描述
obj 触发操作的实例

每个定义的方法都必须返回一个字符串。

示例 1

想象一个简单的新闻应用,其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, request, obj=None):
        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')

现在我们想在AuthorAdmin中的每篇文章中添加两个简单动作(viewunpublish)。view动作将重定向到所选实例的更改表单。

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")

由于unpublish依赖于article.status,我们必须使用get_inline_actions来动态添加此动作。

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, parent_obj=None):
        obj.status = Article.DRAFT
        obj.save()
        messages.info(request, _("Article unpublished"))
    unpublish.short_description = _("Unpublish")

inline_actions添加到更改列表中工作方式类似。有关更多详细信息,请参阅示例项目(test_proj/blog/admin.py)。

示例 2

我们可能更喜欢一个动作,该动作在发布和取消发布之间切换,而不是为这两个状态创建单独的动作。toggle_publish实现了上述行为。

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."))

这可能会使用户的按钮标签含糊不清,因为无论内部状态如何,它都将被调用为Toggle publish。我们可以通过添加特殊方法get_ACTIONNAME_label来指定动态标签。

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

因此,假设行中的对象具有DRAFT状态,则按钮标签将为Toggle publish,否则为Toggle unpublish

当创建一个方法,该方法将根据状态(如)为每个对象添加CSS类时,我们可以更加巧妙

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

您可以使用btn-green使按钮变绿,使用btn-red使按钮变红。或者,您可以使用这些类添加一些javascript逻辑(例如确认框)。

关于确认警报的提示

当执行某些特定关键操作或可能难以撤销的操作时,在提交动作表单之前有一个确认提示是很好的。为了实现这一点,一种方法是用以下内容覆盖templates/admin/change_list.html

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

{% block extrahead %}
    {{ block.super }}
    <script>
        (function() {
            document.addEventListener("DOMContentLoaded", function(event) {
                let inline_actions = document.querySelectorAll(".inline_actions input");
                for (var i=0; i < inline_actions.length; i++) {
                    inline_actions[i].addEventListener("click", function(e) {
                        if(!confirm("Do you really want to " + e.target.value + "?")) {
                            e.preventDefault();
                        }
                    });
                }
            });
        })();
    </script>
{% endblock %}

如果工作人员用户意外点击了任何行内动作,他们可以在确认提示中安全地点击“否”,并且行内动作表单不会被提交。

中间表单

当前实现使用中间表单涉及一些手动处理。在下一个主要版本中这将简化!

为了有一个中间表单,您必须添加有关触发动作的一些信息。django-inline-actions提供了一个方便的模板标签render_inline_action_fields,它将这些信息作为隐藏字段添加到表单中。

{% extends "admin/base_site.html" %}
{% load inline_action_tags %}

{% block content %}
  <form action="" method="post">
    {% csrf_token %}
    {% render_inline_action_fields %}

    {{ form.as_p }}

    <input type="submit" name="_back" value="Cancel"/>
    <input type="submit" name="_save" value="Update"/>
  </form>
{% endblock %}

由于动作不知道是否使用中间表单,我们必须进行一些特殊处理。在上面的情况中,我们必须考虑以下3种情况

  1. 表单已提交,我们想要重定向到先前的视图。
  2. 已点击返回按钮。
  3. 对中间页面/表单的初始访问。

相应的动作可能看起来像

    def change_title(self, request, obj, parent_obj=None):

        # 1. has the form been submitted?
        if '_save' in request.POST:
            form = forms.ChangeTitleForm(request.POST, instance=obj)
            form.save()
            return None  # return back to list view
        # 2. has the back button been pressed?
        elif '_back' in request.POST:
            return None  # return back to list view
        # 3. simply display the form
        else:
            form = forms.ChangeTitleForm(instance=obj)

        return render(
            request,
            'change_title.html',
            context={'form': form}
        )

示例应用

您可以通过捆绑的测试应用test_proj看到django-inline-actions在动作中的使用。使用poetry运行它。

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

在您的浏览器中打开https://:8000/admin/,并创建一个作者和一些文章。

如何测试您的动作?

有两种方法可以编写动作的测试。我们将使用pytest来展示以下示例。

测试动作本身

在我们可以调用管理员类本身上的操作之前,我们必须实例化管理员环境,并将其与我们的模型实例一起传递给ModelAdmin。因此,我们实现了一个名为admin_site的固定装置,它在每个测试中都会使用。

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

测试管理员集成

或者,您可以在真实的Django管理员页面上测试您的操作。您将需要登录,导航到相应的管理员并触发操作的点击。为了简化此过程,您可以使用django-webtest。示例可以在这里找到。

开发

本项目使用poetry进行打包和管理所有依赖项,并使用pre-commit来运行flake8isortmypyblack

克隆此存储库并运行

poetry install
poetry run pre-commit install

以创建包含所有依赖项的虚拟环境。之后,您可以使用以下命令运行测试套件:

poetry run pytest

此存储库遵循Conventional Commits风格。

Cookiecutter模板

本项目使用cruftcookiecutter-pyproject模板创建。为了将此存储库更新到最新模板版本,请在存储库根目录中运行:

cruft update

项目详情


下载文件

下载您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

django-inline-actions-2.4.0.tar.gz (15.2 kB 查看哈希值)

上传时间

构建分布

django_inline_actions-2.4.0-py3-none-any.whl (11.6 kB 查看哈希值)

上传时间 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面