diff --git a/CHANGELOG.md b/CHANGELOG.md index 9454168..3defde5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed * removed RemovedInDjango40Warning warning message, thanks to @Ivan-Feofanov +* fixed #49, fixes reverse url lookup to handle custom admin pages. ## [2.4.0] - 2021-02-08 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e468005..2d809a3 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,3 +9,4 @@ - [@tony](https://github.com/tony) - [@tripliks](https://github.com/tripliks) - [@Ivan-Feofanov](https://github.com/Ivan-Feofanov) +- [@bdnettleton](https://github.com/bdnettleton) diff --git a/inline_actions/admin.py b/inline_actions/admin.py index 4d0f6e8..b29f21c 100755 --- a/inline_actions/admin.py +++ b/inline_actions/admin.py @@ -202,7 +202,8 @@ def _execute_action(self, request, model_admin, action, obj, parent_obj=None): if parent_obj is None: # InlineActionsMixin.MODEL_ADMIN: # redirect to `changelist` url = reverse( - 'admin:{}_{}_changelist'.format( + '{}:{}_{}_changelist'.format( + self.admin_site.name, obj._meta.app_label, obj._meta.model_name, ), @@ -210,7 +211,8 @@ def _execute_action(self, request, model_admin, action, obj, parent_obj=None): else: # redirect to `changeform` url = reverse( - 'admin:{}_{}_change'.format( + '{}:{}_{}_change'.format( + self.admin_site.name, parent_obj._meta.app_label, parent_obj._meta.model_name, ), diff --git a/test_proj/blog/custom_admin.py b/test_proj/blog/custom_admin.py new file mode 100644 index 0000000..b97ec86 --- /dev/null +++ b/test_proj/blog/custom_admin.py @@ -0,0 +1,82 @@ +from django.contrib import admin + +from inline_actions.actions import DefaultActionsMixin, ViewAction +from inline_actions.admin import InlineActionsMixin, InlineActionsModelAdminMixin + +from .admin import ( + ChangeTitleActionsMixin, + TogglePublishActionsMixin, + UnPublishActionsMixin, +) +from .models import Article, AuthorProxy + + +class CustomAdminSite(admin.AdminSite): + site_header = "Custom admin" + site_title = "Custom Admin Portal" + index_title = "Welcome to Custom Administration" + + +custom_admin = CustomAdminSite(name="custom_admin") + + +class ArticleInline( + DefaultActionsMixin, + UnPublishActionsMixin, + TogglePublishActionsMixin, + InlineActionsMixin, + admin.TabularInline, +): + model = Article + fields = ( + 'title', + 'status', + ) + readonly_fields = ( + 'title', + 'status', + ) + + def has_add_permission(self, request, obj=None): + return False + + +class ArticleNoopInline(InlineActionsMixin, admin.TabularInline): + model = Article + fields = ( + 'title', + 'status', + ) + readonly_fields = ( + 'title', + 'status', + ) + + def get_inline_actions(self, request, obj=None): + actions = super(ArticleNoopInline, self).get_inline_actions( + request=request, obj=obj + ) + actions.append('noop_action') + return actions + + def noop_action(self, request, obj, parent_obj=None): + pass + + +@admin.register(AuthorProxy, site=custom_admin) +class AuthorMultipleInlinesAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin): + inlines = [ArticleInline, ArticleNoopInline] + list_display = ('name',) + inline_actions = None + + +@admin.register(Article, site=custom_admin) +class ArticleAdmin( + UnPublishActionsMixin, + TogglePublishActionsMixin, + ChangeTitleActionsMixin, + ViewAction, + InlineActionsModelAdminMixin, + admin.ModelAdmin, +): + list_display = ('title', 'status', 'author') diff --git a/test_proj/blog/tests/test_inline_admin.py b/test_proj/blog/tests/test_inline_admin.py index 9a0c932..a7e7ef8 100644 --- a/test_proj/blog/tests/test_inline_admin.py +++ b/test_proj/blog/tests/test_inline_admin.py @@ -156,6 +156,54 @@ def test_publish_action(admin_client, mocker, article): assert article.status == Article.DRAFT +def test_custom_admin_publish_action(admin_client, mocker, article): + """Test dynamically added actions using `get_actions()`""" + from ..admin import UnPublishActionsMixin + + mocker.spy(UnPublishActionsMixin, 'get_inline_actions') + mocker.spy(UnPublishActionsMixin, 'publish') + mocker.spy(UnPublishActionsMixin, 'unpublish') + author = article.author + assert article.status == Article.DRAFT + + author_url = reverse('custom_admin:blog_authorproxy_change', args=(author.pk,)) + publish_input_name = ( + '_action__articleinline__inline__publish__blog__article__{}'.format(article.pk) + ) + unpublish_input_name = ( + '_action__articleinline__inline__unpublish__blog__article__{}'.format( + article.pk + ) + ) + + # open changeform + changeview = admin_client.get(author_url) + assert UnPublishActionsMixin.get_inline_actions.call_count > 0 + assert publish_input_name in dict(changeview.form.fields) + assert "custom_admin" in changeview.request.path + + # execute and test publish action + changeview = changeview.form.submit(name=publish_input_name).follow() + # not available in django 1.7 + # article.refresh_from_db() + article = Article.objects.get(pk=article.pk) + assert publish_input_name not in dict(changeview.form.fields) + assert unpublish_input_name in dict(changeview.form.fields) + assert UnPublishActionsMixin.publish.call_count == 1 + assert article.status == Article.PUBLISHED + assert changeview.request.path == author_url + + # execute and test unpublish action + changeview = changeview.form.submit(name=unpublish_input_name).follow() + # article.refresh_from_db() + article = Article.objects.get(pk=article.pk) + assert publish_input_name in dict(changeview.form.fields) + assert unpublish_input_name not in dict(changeview.form.fields) + assert UnPublishActionsMixin.unpublish.call_count == 1 + assert article.status == Article.DRAFT + assert changeview.request.path == author_url + + def test_view_action(admin_client, mocker, article): """Test view action.""" from inline_actions.actions import ViewAction diff --git a/test_proj/blog/tests/test_model_admin.py b/test_proj/blog/tests/test_model_admin.py index 8414ea3..3924215 100644 --- a/test_proj/blog/tests/test_model_admin.py +++ b/test_proj/blog/tests/test_model_admin.py @@ -134,6 +134,53 @@ def test_publish_action(admin_client, mocker, article): assert article.status == Article.DRAFT +def test_custom_admin_publish_action(admin_client, mocker, article): + """Test dynamically added actions using `get_actions()`""" + from ..admin import UnPublishActionsMixin + + mocker.spy(UnPublishActionsMixin, 'get_inline_actions') + mocker.spy(UnPublishActionsMixin, 'publish') + mocker.spy(UnPublishActionsMixin, 'unpublish') + assert article.status == Article.DRAFT + + article_url = reverse('custom_admin:blog_article_changelist') + publish_input_name = ( + '_action__articleadmin__admin__publish__blog__article__{}'.format(article.pk) + ) + unpublish_input_name = ( + '_action__articleadmin__admin__unpublish__blog__article__{}'.format( + article.pk, + ) + ) + + # open changelist + changelist = admin_client.get(article_url) + assert UnPublishActionsMixin.get_inline_actions.call_count > 0 + assert publish_input_name in dict(changelist.form.fields) + assert "custom_admin" in article_url + + # execute and test publish action + changelist = changelist.form.submit(name=publish_input_name).follow() + # not available in django 1.7 + # article.refresh_from_db() + article = Article.objects.get(pk=article.pk) + assert publish_input_name not in dict(changelist.form.fields) + assert unpublish_input_name in dict(changelist.form.fields) + assert UnPublishActionsMixin.publish.call_count == 1 + assert article.status == Article.PUBLISHED + assert changelist.request.path == article_url + + # execute and test unpublish action + changelist = changelist.form.submit(name=unpublish_input_name).follow() + # article.refresh_from_db() + article = Article.objects.get(pk=article.pk) + assert publish_input_name in dict(changelist.form.fields) + assert unpublish_input_name not in dict(changelist.form.fields) + assert UnPublishActionsMixin.unpublish.call_count == 1 + assert article.status == Article.DRAFT + assert changelist.request.path == article_url + + def test_view_action(admin_client, mocker, article): """Test view action.""" from inline_actions.actions import ViewAction diff --git a/test_proj/urls.py b/test_proj/urls.py index dfc7362..98fdf01 100644 --- a/test_proj/urls.py +++ b/test_proj/urls.py @@ -1,6 +1,9 @@ from django.contrib import admin from django.urls import path +from test_proj.blog.custom_admin import custom_admin + urlpatterns = [ path('admin/', admin.site.urls), + path('custom_admin/', custom_admin.urls), ]