summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorNick Pope <nick.pope@flightdataservices.com>2021-01-13 16:19:22 +0000
committerGitHub <noreply@github.com>2021-01-13 17:19:22 +0100
commit920448539631b52dcee53bd32a880abbc9de18bd (patch)
tree03dd52fd206088302de11e0b485b420726718a4a /tests
parent83fcfc9ec8610540948815e127101f1206562ead (diff)
Fixed #16117 -- Added decorators for admin action and display functions.
Refs #25134, #32099.
Diffstat (limited to 'tests')
-rw-r--r--tests/admin_changelist/admin.py3
-rw-r--r--tests/admin_checks/tests.py4
-rw-r--r--tests/admin_utils/models.py3
-rw-r--r--tests/admin_utils/tests.py11
-rw-r--r--tests/admin_views/admin.py54
-rw-r--r--tests/admin_views/models.py15
-rw-r--r--tests/admin_views/tests.py12
-rw-r--r--tests/modeladmin/test_actions.py15
-rw-r--r--tests/modeladmin/test_checks.py11
9 files changed, 76 insertions, 52 deletions
diff --git a/tests/admin_changelist/admin.py b/tests/admin_changelist/admin.py
index 890919557e..4f23296eca 100644
--- a/tests/admin_changelist/admin.py
+++ b/tests/admin_changelist/admin.py
@@ -19,6 +19,7 @@ class EventAdmin(admin.ModelAdmin):
date_hierarchy = 'date'
list_display = ['event_date_func']
+ @admin.display
def event_date_func(self, event):
return event.date
@@ -171,6 +172,6 @@ class EmptyValueChildAdmin(admin.ModelAdmin):
empty_value_display = '-empty-'
list_display = ('name', 'age_display', 'age')
+ @admin.display(empty_value='&dagger;')
def age_display(self, obj):
return obj.age
- age_display.empty_value_display = '&dagger;'
diff --git a/tests/admin_checks/tests.py b/tests/admin_checks/tests.py
index 5744dc1b39..139b6dec59 100644
--- a/tests/admin_checks/tests.py
+++ b/tests/admin_checks/tests.py
@@ -692,6 +692,7 @@ class SystemChecksTestCase(SimpleTestCase):
self.assertEqual(errors, [])
def test_readonly_on_method(self):
+ @admin.display
def my_function(obj):
pass
@@ -705,6 +706,7 @@ class SystemChecksTestCase(SimpleTestCase):
class SongAdmin(admin.ModelAdmin):
readonly_fields = ("readonly_method_on_modeladmin",)
+ @admin.display
def readonly_method_on_modeladmin(self, obj):
pass
@@ -717,6 +719,7 @@ class SystemChecksTestCase(SimpleTestCase):
def __getattr__(self, item):
if item == "dynamic_method":
+ @admin.display
def method(obj):
pass
return method
@@ -777,6 +780,7 @@ class SystemChecksTestCase(SimpleTestCase):
def test_extra(self):
class SongAdmin(admin.ModelAdmin):
+ @admin.display
def awesome_song(self, instance):
if instance.title == "Born to Run":
return "Best Ever!"
diff --git a/tests/admin_utils/models.py b/tests/admin_utils/models.py
index fda1380b23..e57c3926b5 100644
--- a/tests/admin_utils/models.py
+++ b/tests/admin_utils/models.py
@@ -1,3 +1,4 @@
+from django.contrib import admin
from django.db import models
from django.utils.translation import gettext_lazy as _
@@ -28,9 +29,9 @@ class Article(models.Model):
def test_from_model(self):
return "nothing"
+ @admin.display(description='not What you Expect')
def test_from_model_with_override(self):
return "nothing"
- test_from_model_with_override.short_description = "not What you Expect"
class ArticleProxy(Article):
diff --git a/tests/admin_utils/tests.py b/tests/admin_utils/tests.py
index ce9f94dbb9..a74449bdc0 100644
--- a/tests/admin_utils/tests.py
+++ b/tests/admin_utils/tests.py
@@ -3,6 +3,7 @@ from decimal import Decimal
from django import forms
from django.conf import settings
+from django.contrib import admin
from django.contrib.admin import helpers
from django.contrib.admin.utils import (
NestedObjects, display_for_field, display_for_value, flatten,
@@ -293,9 +294,9 @@ class UtilsTests(SimpleTestCase):
self.assertEqual(label_for_field('site_id', Article), 'Site id')
class MockModelAdmin:
+ @admin.display(description='not Really the Model')
def test_from_model(self, obj):
return "nothing"
- test_from_model.short_description = "not Really the Model"
self.assertEqual(
label_for_field("test_from_model", Article, model_admin=MockModelAdmin),
@@ -323,13 +324,11 @@ class UtilsTests(SimpleTestCase):
label_for_field('nonexistent', Article, form=ArticleForm()),
def test_label_for_property(self):
- # NOTE: cannot use @property decorator, because of
- # AttributeError: 'property' object has no attribute 'short_description'
class MockModelAdmin:
- def my_property(self):
+ @property
+ @admin.display(description='property short description')
+ def test_from_property(self):
return "this if from property"
- my_property.short_description = 'property short description'
- test_from_property = property(my_property)
self.assertEqual(
label_for_field("test_from_property", Article, model_admin=MockModelAdmin),
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 1140f03496..925da71982 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -49,6 +49,7 @@ from .models import (
)
+@admin.display(ordering='date')
def callable_year(dt_value):
try:
return dt_value.year
@@ -56,9 +57,6 @@ def callable_year(dt_value):
return None
-callable_year.admin_order_field = 'date'
-
-
class ArticleInline(admin.TabularInline):
model = Article
fk_name = 'section'
@@ -138,25 +136,24 @@ class ArticleAdmin(ArticleAdminWithExtraUrl):
# These orderings aren't particularly useful but show that expressions can
# be used for admin_order_field.
+ @admin.display(ordering=models.F('date') + datetime.timedelta(days=3))
def order_by_expression(self, obj):
return obj.model_year
- order_by_expression.admin_order_field = models.F('date') + datetime.timedelta(days=3)
+ @admin.display(ordering=models.F('date'))
def order_by_f_expression(self, obj):
return obj.model_year
- order_by_f_expression.admin_order_field = models.F('date')
+ @admin.display(ordering=models.F('date').asc(nulls_last=True))
def order_by_orderby_expression(self, obj):
return obj.model_year
- order_by_orderby_expression.admin_order_field = models.F('date').asc(nulls_last=True)
def changelist_view(self, request):
return super().changelist_view(request, extra_context={'extra_var': 'Hello!'})
+ @admin.display(ordering='date', description=None)
def modeladmin_year(self, obj):
return obj.date.year
- modeladmin_year.admin_order_field = 'date'
- modeladmin_year.short_description = None
def delete_model(self, request, obj):
EmailMessage(
@@ -216,6 +213,7 @@ class ThingAdmin(admin.ModelAdmin):
class InquisitionAdmin(admin.ModelAdmin):
list_display = ('leader', 'country', 'expected', 'sketch')
+ @admin.display
def sketch(self, obj):
# A method with the same name as a reverse accessor.
return 'list-display-sketch'
@@ -280,6 +278,7 @@ class SubscriberAdmin(admin.ModelAdmin):
SubscriberAdmin.overridden = True
super().delete_queryset(request, queryset)
+ @admin.action
def mail_admin(self, request, selected):
EmailMessage(
'Greetings from a ModelAdmin action',
@@ -289,6 +288,7 @@ class SubscriberAdmin(admin.ModelAdmin):
).send()
+@admin.action(description='External mail (Another awesome action)')
def external_mail(modeladmin, request, selected):
EmailMessage(
'Greetings from a function action',
@@ -298,32 +298,23 @@ def external_mail(modeladmin, request, selected):
).send()
-external_mail.short_description = 'External mail (Another awesome action)'
-
-
+@admin.action(description='Redirect to (Awesome action)')
def redirect_to(modeladmin, request, selected):
from django.http import HttpResponseRedirect
return HttpResponseRedirect('/some-where-else/')
-redirect_to.short_description = 'Redirect to (Awesome action)'
-
-
+@admin.action(description='Download subscription')
def download(modeladmin, request, selected):
buf = StringIO('This is the content of the file')
return StreamingHttpResponse(FileWrapper(buf))
-download.short_description = 'Download subscription'
-
-
+@admin.action(description='No permission to run')
def no_perm(modeladmin, request, selected):
return HttpResponse(content='No permission to perform this action', status=403)
-no_perm.short_description = 'No permission to run'
-
-
class ExternalSubscriberAdmin(admin.ModelAdmin):
actions = [redirect_to, external_mail, download, no_perm]
@@ -441,6 +432,7 @@ class LinkInline(admin.TabularInline):
readonly_fields = ("posted", "multiline", "readonly_link_content")
+ @admin.display
def multiline(self, instance):
return "InlineMultiline\ntest\nstring"
@@ -501,19 +493,22 @@ class PostAdmin(admin.ModelAdmin):
LinkInline
]
+ @admin.display
def coolness(self, instance):
if instance.pk:
return "%d amount of cool." % instance.pk
else:
return "Unknown coolness."
+ @admin.display(description='Value in $US')
def value(self, instance):
return 1000
- value.short_description = 'Value in $US'
+ @admin.display
def multiline(self, instance):
return "Multiline\ntest\nstring"
+ @admin.display
def multiline_html(self, instance):
return mark_safe("Multiline<br>\nhtml<br>\ncontent")
@@ -655,9 +650,9 @@ class ComplexSortedPersonAdmin(admin.ModelAdmin):
list_display = ('name', 'age', 'is_employee', 'colored_name')
ordering = ('name',)
+ @admin.display(ordering='name')
def colored_name(self, obj):
return format_html('<span style="color: #ff00ff;">{}</span>', obj.name)
- colored_name.admin_order_field = 'name'
class PluggableSearchPersonAdmin(admin.ModelAdmin):
@@ -706,20 +701,18 @@ class AdminOrderedModelMethodAdmin(admin.ModelAdmin):
class AdminOrderedAdminMethodAdmin(admin.ModelAdmin):
+ @admin.display(ordering='order')
def some_admin_order(self, obj):
return obj.order
- some_admin_order.admin_order_field = 'order'
ordering = ('order',)
list_display = ('stuff', 'some_admin_order')
+@admin.display(ordering='order')
def admin_ordered_callable(obj):
return obj.order
-admin_ordered_callable.admin_order_field = 'order'
-
-
class AdminOrderedCallableAdmin(admin.ModelAdmin):
ordering = ('order',)
list_display = ('stuff', admin_ordered_callable)
@@ -814,6 +807,7 @@ class UnchangeableObjectAdmin(admin.ModelAdmin):
return [p for p in urlpatterns if p.name and not p.name.endswith("_change")]
+@admin.display
def callable_on_unknown(obj):
return obj.unknown
@@ -831,21 +825,27 @@ class MessageTestingAdmin(admin.ModelAdmin):
actions = ["message_debug", "message_info", "message_success",
"message_warning", "message_error", "message_extra_tags"]
+ @admin.action
def message_debug(self, request, selected):
self.message_user(request, "Test debug", level="debug")
+ @admin.action
def message_info(self, request, selected):
self.message_user(request, "Test info", level="info")
+ @admin.action
def message_success(self, request, selected):
self.message_user(request, "Test success", level="success")
+ @admin.action
def message_warning(self, request, selected):
self.message_user(request, "Test warning", level="warning")
+ @admin.action
def message_error(self, request, selected):
self.message_user(request, "Test error", level="error")
+ @admin.action
def message_extra_tags(self, request, selected):
self.message_user(request, "Test tags", extra_tags="extra_tag")
@@ -1156,9 +1156,9 @@ class ArticleAdmin6(admin.ModelAdmin):
)
sortable_by = ('date', callable_year)
+ @admin.display(ordering='date')
def modeladmin_year(self, obj):
return obj.date.year
- modeladmin_year.admin_order_field = 'date'
class ActorAdmin6(admin.ModelAdmin):
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 5224f5f91a..f449ad792b 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -3,6 +3,7 @@ import os
import tempfile
import uuid
+from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation,
@@ -45,20 +46,18 @@ class Article(models.Model):
def __str__(self):
return self.title
+ @admin.display(ordering='date', description='')
def model_year(self):
return self.date.year
- model_year.admin_order_field = 'date'
- model_year.short_description = ''
+ @admin.display(ordering='-date', description='')
def model_year_reversed(self):
return self.date.year
- model_year_reversed.admin_order_field = '-date'
- model_year_reversed.short_description = ''
- def property_year(self):
+ @property
+ @admin.display(ordering='date')
+ def model_property_year(self):
return self.date.year
- property_year.admin_order_field = 'date'
- model_property_year = property(property_year)
@property
def model_month(self):
@@ -746,9 +745,9 @@ class AdminOrderedModelMethod(models.Model):
order = models.IntegerField()
stuff = models.CharField(max_length=200)
+ @admin.display(ordering='order')
def some_order(self):
return self.order
- some_order.admin_order_field = 'order'
class AdminOrderedAdminMethod(models.Model):
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 297760f807..0b2415cdb8 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -7,6 +7,7 @@ from urllib.parse import parse_qsl, urljoin, urlparse
import pytz
+from django.contrib import admin
from django.contrib.admin import AdminSite, ModelAdmin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.contrib.admin.models import ADDITION, DELETION, LogEntry
@@ -751,6 +752,17 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
response = self.client.get(reverse('admin:admin_views_post_changelist'))
self.assertContains(response, 'icon-unknown.svg')
+ def test_display_decorator_with_boolean_and_empty_value(self):
+ msg = (
+ 'The boolean and empty_value arguments to the @display decorator '
+ 'are mutually exclusive.'
+ )
+ with self.assertRaisesMessage(ValueError, msg):
+ class BookAdmin(admin.ModelAdmin):
+ @admin.display(boolean=True, empty_value='(Missing)')
+ def is_published(self, obj):
+ return obj.publish_date is not None
+
def test_i18n_language_non_english_default(self):
"""
Check if the JavaScript i18n view returns an empty language catalog
diff --git a/tests/modeladmin/test_actions.py b/tests/modeladmin/test_actions.py
index f7de725ffc..b61641c0c9 100644
--- a/tests/modeladmin/test_actions.py
+++ b/tests/modeladmin/test_actions.py
@@ -27,6 +27,7 @@ class AdminActionsTests(TestCase):
class BandAdmin(admin.ModelAdmin):
actions = ['custom_action']
+ @admin.action
def custom_action(modeladmin, request, queryset):
pass
@@ -60,6 +61,7 @@ class AdminActionsTests(TestCase):
class AdminBase(admin.ModelAdmin):
actions = ['custom_action']
+ @admin.action
def custom_action(modeladmin, request, queryset):
pass
@@ -78,13 +80,14 @@ class AdminActionsTests(TestCase):
self.assertEqual(action_names, ['delete_selected'])
def test_global_actions_description(self):
+ @admin.action(description='Site-wide admin action 1.')
def global_action_1(modeladmin, request, queryset):
pass
+ @admin.action
def global_action_2(modeladmin, request, queryset):
pass
- global_action_1.short_description = 'Site-wide admin action 1.'
admin_site = admin.AdminSite()
admin_site.add_action(global_action_1)
admin_site.add_action(global_action_2)
@@ -103,30 +106,28 @@ class AdminActionsTests(TestCase):
)
def test_actions_replace_global_action(self):
+ @admin.action(description='Site-wide admin action 1.')
def global_action_1(modeladmin, request, queryset):
pass
+ @admin.action(description='Site-wide admin action 2.')
def global_action_2(modeladmin, request, queryset):
pass
- global_action_1.short_description = 'Site-wide admin action 1.'
- global_action_2.short_description = 'Site-wide admin action 2.'
admin.site.add_action(global_action_1, name='custom_action_1')
admin.site.add_action(global_action_2, name='custom_action_2')
+ @admin.action(description='Local admin action 1.')
def custom_action_1(modeladmin, request, queryset):
pass
- custom_action_1.short_description = 'Local admin action 1.'
-
class BandAdmin(admin.ModelAdmin):
actions = [custom_action_1, 'custom_action_2']
+ @admin.action(description='Local admin action 2.')
def custom_action_2(self, request, queryset):
pass
- custom_action_2.short_description = 'Local admin action 2.'
-
ma = BandAdmin(Band, admin.site)
self.assertEqual(ma.check(), [])
self.assertEqual(
diff --git a/tests/modeladmin/test_checks.py b/tests/modeladmin/test_checks.py
index 308f4a19eb..d81cc3dd32 100644
--- a/tests/modeladmin/test_checks.py
+++ b/tests/modeladmin/test_checks.py
@@ -1,4 +1,5 @@
from django import forms
+from django.contrib import admin
from django.contrib.admin import BooleanFieldListFilter, SimpleListFilter
from django.contrib.admin.options import VERTICAL, ModelAdmin, TabularInline
from django.contrib.admin.sites import AdminSite
@@ -499,10 +500,12 @@ class ListDisplayTests(CheckTestCase):
)
def test_valid_case(self):
+ @admin.display
def a_callable(obj):
pass
class TestModelAdmin(ModelAdmin):
+ @admin.display
def a_method(self, obj):
pass
list_display = ('name', 'decade_published_in', 'a_method', a_callable)
@@ -563,10 +566,12 @@ class ListDisplayLinksCheckTests(CheckTestCase):
)
def test_valid_case(self):
+ @admin.display
def a_callable(obj):
pass
class TestModelAdmin(ModelAdmin):
+ @admin.display
def a_method(self, obj):
pass
list_display = ('name', 'decade_published_in', 'a_method', a_callable)
@@ -1417,11 +1422,10 @@ class AutocompleteFieldsTests(CheckTestCase):
class ActionsCheckTests(CheckTestCase):
def test_custom_permissions_require_matching_has_method(self):
+ @admin.action(permissions=['custom'])
def custom_permission_action(modeladmin, request, queryset):
pass
- custom_permission_action.allowed_permissions = ('custom',)
-
class BandAdmin(ModelAdmin):
actions = (custom_permission_action,)
@@ -1433,6 +1437,7 @@ class ActionsCheckTests(CheckTestCase):
)
def test_actions_not_unique(self):
+ @admin.action
def action(modeladmin, request, queryset):
pass
@@ -1447,9 +1452,11 @@ class ActionsCheckTests(CheckTestCase):
)
def test_actions_unique(self):
+ @admin.action
def action1(modeladmin, request, queryset):
pass
+ @admin.action
def action2(modeladmin, request, queryset):
pass