django-inline-actions 为 ModelAdmin 或 InlineModelAdmin 的每一行添加操作。
项目描述
django-inline-actions
django-inline-actions 为 ModelAdmin 或 InlineModelAdmin 的每一行添加操作。
需求
- Python 3.6.1 或更高版本
屏幕截图
安装
-
安装 django-inline-actions
pip install django-inline-actions
-
将
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
中的每篇文章中添加两个简单动作(view
,unpublish
)。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种情况
- 表单已提交,我们想要重定向到先前的视图。
- 已点击返回按钮。
- 对中间页面/表单的初始访问。
相应的动作可能看起来像
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来运行flake8、isort、mypy和black。
克隆此存储库并运行
poetry install
poetry run pre-commit install
以创建包含所有依赖项的虚拟环境。之后,您可以使用以下命令运行测试套件:
poetry run pytest
此存储库遵循Conventional Commits风格。
Cookiecutter模板
本项目使用cruft和cookiecutter-pyproject模板创建。为了将此存储库更新到最新模板版本,请在存储库根目录中运行:
cruft update
。
项目详情
哈希值 for django_inline_actions-2.4.0-py3-none-any.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 671e3d757d3358c71be4cc05e6b07f068721fc74fa9b17e1be41f44afa03c1ac |
|
MD5 | ce40ad2f7ffce9aca58aef23f92b675d |
|
BLAKE2b-256 | 9ebaabf49f838982b4327a5bb231132d2b360410c79c3aee14e7e5bb89904e98 |