From 9c19aff7c7561e3a82978a272ecdaad40dda5c00 Mon Sep 17 00:00:00 2001 From: django-bot Date: Thu, 3 Feb 2022 20:24:19 +0100 Subject: Refs #33476 -- Reformatted code with Black. --- tests/admin_views/admin.py | 727 +-- tests/admin_views/custom_has_permission_admin.py | 14 +- tests/admin_views/customadmin.py | 33 +- tests/admin_views/forms.py | 11 +- tests/admin_views/models.py | 238 +- tests/admin_views/test_actions.py | 414 +- tests/admin_views/test_adminsite.py | 72 +- tests/admin_views/test_autocomplete_view.py | 442 +- tests/admin_views/test_forms.py | 25 +- tests/admin_views/test_history_view.py | 79 +- tests/admin_views/test_multidb.py | 38 +- tests/admin_views/test_nav_sidebar.py | 187 +- tests/admin_views/test_templatetags.py | 147 +- tests/admin_views/tests.py | 5981 +++++++++++++--------- tests/admin_views/urls.py | 42 +- tests/admin_views/views.py | 2 +- 16 files changed, 5122 insertions(+), 3330 deletions(-) (limited to 'tests/admin_views') diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 9cd078074b..b78746c0ff 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -20,33 +20,127 @@ from django.views.decorators.common import no_append_slash from .forms import MediaActionForm from .models import ( - Actor, AdminOrderedAdminMethod, AdminOrderedCallable, AdminOrderedField, - AdminOrderedModelMethod, Album, Answer, Answer2, Article, BarAccount, Book, - Bookmark, Box, Category, Chapter, ChapterXtra1, Child, ChildOfReferer, - Choice, City, Collector, Color, Color2, ComplexSortedPerson, CoverLetter, - CustomArticle, CyclicOne, CyclicTwo, DependentChild, DooHickey, EmptyModel, - EmptyModelHidden, EmptyModelMixin, EmptyModelVisible, ExplicitlyProvidedPK, - ExternalSubscriber, Fabric, FancyDoodad, FieldOverridePost, - FilteredManager, FooAccount, FoodDelivery, FunkyTag, Gadget, Gallery, - GenRelReference, Grommet, ImplicitlyGeneratedPK, Ingredient, - InlineReference, InlineReferer, Inquisition, Language, Link, - MainPrepopulated, ModelWithStringPrimaryKey, NotReferenced, OldSubscriber, - OtherStory, Paper, Parent, ParentWithDependentChildren, ParentWithUUIDPK, - Person, Persona, Picture, Pizza, Plot, PlotDetails, PlotProxy, - PluggableSearchPerson, Podcast, Post, PrePopulatedPost, - PrePopulatedPostLargeSlug, PrePopulatedSubPost, Promo, Question, - ReadablePizza, ReadOnlyPizza, ReadOnlyRelatedField, Recipe, Recommendation, - Recommender, ReferencedByGenRel, ReferencedByInline, ReferencedByParent, - RelatedPrepopulated, RelatedWithUUIDPKModel, Report, Reservation, - Restaurant, RowLevelChangePermissionModel, Section, ShortMessage, Simple, - Sketch, Song, State, Story, StumpJoke, Subscriber, SuperVillain, Telegram, - Thing, Topping, UnchangeableObject, UndeletableObject, UnorderedObject, - UserMessenger, UserProxy, Villain, Vodcast, Whatsit, Widget, Worker, + Actor, + AdminOrderedAdminMethod, + AdminOrderedCallable, + AdminOrderedField, + AdminOrderedModelMethod, + Album, + Answer, + Answer2, + Article, + BarAccount, + Book, + Bookmark, + Box, + Category, + Chapter, + ChapterXtra1, + Child, + ChildOfReferer, + Choice, + City, + Collector, + Color, + Color2, + ComplexSortedPerson, + CoverLetter, + CustomArticle, + CyclicOne, + CyclicTwo, + DependentChild, + DooHickey, + EmptyModel, + EmptyModelHidden, + EmptyModelMixin, + EmptyModelVisible, + ExplicitlyProvidedPK, + ExternalSubscriber, + Fabric, + FancyDoodad, + FieldOverridePost, + FilteredManager, + FooAccount, + FoodDelivery, + FunkyTag, + Gadget, + Gallery, + GenRelReference, + Grommet, + ImplicitlyGeneratedPK, + Ingredient, + InlineReference, + InlineReferer, + Inquisition, + Language, + Link, + MainPrepopulated, + ModelWithStringPrimaryKey, + NotReferenced, + OldSubscriber, + OtherStory, + Paper, + Parent, + ParentWithDependentChildren, + ParentWithUUIDPK, + Person, + Persona, + Picture, + Pizza, + Plot, + PlotDetails, + PlotProxy, + PluggableSearchPerson, + Podcast, + Post, + PrePopulatedPost, + PrePopulatedPostLargeSlug, + PrePopulatedSubPost, + Promo, + Question, + ReadablePizza, + ReadOnlyPizza, + ReadOnlyRelatedField, + Recipe, + Recommendation, + Recommender, + ReferencedByGenRel, + ReferencedByInline, + ReferencedByParent, + RelatedPrepopulated, + RelatedWithUUIDPKModel, + Report, + Reservation, + Restaurant, + RowLevelChangePermissionModel, + Section, + ShortMessage, + Simple, + Sketch, + Song, + State, + Story, + StumpJoke, + Subscriber, + SuperVillain, + Telegram, + Thing, + Topping, + UnchangeableObject, + UndeletableObject, + UnorderedObject, + UserMessenger, + UserProxy, + Villain, + Vodcast, + Whatsit, + Widget, + Worker, WorkHour, ) -@admin.display(ordering='date') +@admin.display(ordering="date") def callable_year(dt_value): try: return dt_value.year @@ -56,19 +150,11 @@ def callable_year(dt_value): class ArticleInline(admin.TabularInline): model = Article - fk_name = 'section' - prepopulated_fields = { - 'title': ('content',) - } + fk_name = "section" + prepopulated_fields = {"title": ("content",)} fieldsets = ( - ('Some fields', { - 'classes': ('collapse',), - 'fields': ('title', 'content') - }), - ('Some other fields', { - 'classes': ('wide',), - 'fields': ('date', 'section') - }) + ("Some fields", {"classes": ("collapse",), "fields": ("title", "content")}), + ("Some other fields", {"classes": ("wide",), "fields": ("date", "section")}), ) @@ -78,13 +164,13 @@ class ChapterInline(admin.TabularInline): class ChapterXtra1Admin(admin.ModelAdmin): list_filter = ( - 'chap', - 'chap__title', - 'chap__book', - 'chap__book__name', - 'chap__book__promo', - 'chap__book__promo__name', - 'guest_author__promo__book', + "chap", + "chap__title", + "chap__book", + "chap__book__name", + "chap__book__promo", + "chap__book__promo__name", + "guest_author__promo__book", ) @@ -92,7 +178,7 @@ class ArticleForm(forms.ModelForm): extra_form_field = forms.BooleanField(required=False) class Meta: - fields = '__all__' + fields = "__all__" model = Article @@ -100,7 +186,11 @@ class ArticleAdminWithExtraUrl(admin.ModelAdmin): def get_urls(self): urlpatterns = super().get_urls() urlpatterns.append( - path('extra.json', self.admin_site.admin_view(self.extra_json), name='article_extra_json') + path( + "extra.json", + self.admin_site.admin_view(self.extra_json), + name="article_extra_json", + ) ) return urlpatterns @@ -110,76 +200,87 @@ class ArticleAdminWithExtraUrl(admin.ModelAdmin): class ArticleAdmin(ArticleAdminWithExtraUrl): list_display = ( - 'content', 'date', callable_year, 'model_year', 'modeladmin_year', - 'model_year_reversed', 'section', lambda obj: obj.title, - 'order_by_expression', 'model_property_year', 'model_month', - 'order_by_f_expression', 'order_by_orderby_expression', + "content", + "date", + callable_year, + "model_year", + "modeladmin_year", + "model_year_reversed", + "section", + lambda obj: obj.title, + "order_by_expression", + "model_property_year", + "model_month", + "order_by_f_expression", + "order_by_orderby_expression", ) - list_editable = ('section',) - list_filter = ('date', 'section') - autocomplete_fields = ('section',) + list_editable = ("section",) + list_filter = ("date", "section") + autocomplete_fields = ("section",) view_on_site = False form = ArticleForm fieldsets = ( - ('Some fields', { - 'classes': ('collapse',), - 'fields': ('title', 'content', 'extra_form_field'), - }), - ('Some other fields', { - 'classes': ('wide',), - 'fields': ('date', 'section', 'sub_section') - }) + ( + "Some fields", + { + "classes": ("collapse",), + "fields": ("title", "content", "extra_form_field"), + }, + ), + ( + "Some other fields", + {"classes": ("wide",), "fields": ("date", "section", "sub_section")}, + ), ) # 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)) + @admin.display(ordering=models.F("date") + datetime.timedelta(days=3)) def order_by_expression(self, obj): return obj.model_year - @admin.display(ordering=models.F('date')) + @admin.display(ordering=models.F("date")) def order_by_f_expression(self, obj): return obj.model_year - @admin.display(ordering=models.F('date').asc(nulls_last=True)) + @admin.display(ordering=models.F("date").asc(nulls_last=True)) def order_by_orderby_expression(self, obj): return obj.model_year def changelist_view(self, request): - return super().changelist_view(request, extra_context={'extra_var': 'Hello!'}) + return super().changelist_view(request, extra_context={"extra_var": "Hello!"}) - @admin.display(ordering='date', description=None) + @admin.display(ordering="date", description=None) def modeladmin_year(self, obj): return obj.date.year def delete_model(self, request, obj): EmailMessage( - 'Greetings from a deleted object', - 'I hereby inform you that some user deleted me', - 'from@example.com', - ['to@example.com'] + "Greetings from a deleted object", + "I hereby inform you that some user deleted me", + "from@example.com", + ["to@example.com"], ).send() return super().delete_model(request, obj) def save_model(self, request, obj, form, change=True): EmailMessage( - 'Greetings from a created object', - 'I hereby inform you that some user created me', - 'from@example.com', - ['to@example.com'] + "Greetings from a created object", + "I hereby inform you that some user created me", + "from@example.com", + ["to@example.com"], ).send() return super().save_model(request, obj, form, change) class ArticleAdmin2(admin.ModelAdmin): - def has_module_permission(self, request): return False class RowLevelChangePermissionModelAdmin(admin.ModelAdmin): def has_change_permission(self, request, obj=None): - """ Only allow changing objects with even id number """ + """Only allow changing objects with even id number""" return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0) def has_view_permission(self, request, obj=None): @@ -191,63 +292,68 @@ class CustomArticleAdmin(admin.ModelAdmin): """ Tests various hooks for using custom templates and contexts. """ - change_list_template = 'custom_admin/change_list.html' - change_form_template = 'custom_admin/change_form.html' - add_form_template = 'custom_admin/add_form.html' - object_history_template = 'custom_admin/object_history.html' - delete_confirmation_template = 'custom_admin/delete_confirmation.html' - delete_selected_confirmation_template = 'custom_admin/delete_selected_confirmation.html' - popup_response_template = 'custom_admin/popup_response.html' + + change_list_template = "custom_admin/change_list.html" + change_form_template = "custom_admin/change_form.html" + add_form_template = "custom_admin/add_form.html" + object_history_template = "custom_admin/object_history.html" + delete_confirmation_template = "custom_admin/delete_confirmation.html" + delete_selected_confirmation_template = ( + "custom_admin/delete_selected_confirmation.html" + ) + popup_response_template = "custom_admin/popup_response.html" def changelist_view(self, request): - return super().changelist_view(request, extra_context={'extra_var': 'Hello!'}) + return super().changelist_view(request, extra_context={"extra_var": "Hello!"}) class ThingAdmin(admin.ModelAdmin): - list_filter = ('color', 'color__warm', 'color__value', 'pub_date') + list_filter = ("color", "color__warm", "color__value", "pub_date") class InquisitionAdmin(admin.ModelAdmin): - list_display = ('leader', 'country', 'expected', 'sketch') + 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' + return "list-display-sketch" class SketchAdmin(admin.ModelAdmin): - raw_id_fields = ('inquisition', 'defendant0', 'defendant1') + raw_id_fields = ("inquisition", "defendant0", "defendant1") class FabricAdmin(admin.ModelAdmin): - list_display = ('surface',) - list_filter = ('surface',) + list_display = ("surface",) + list_filter = ("surface",) class BasePersonModelFormSet(BaseModelFormSet): def clean(self): for person_dict in self.cleaned_data: - person = person_dict.get('id') - alive = person_dict.get('alive') + person = person_dict.get("id") + alive = person_dict.get("alive") if person and alive and person.name == "Grace Hopper": raise ValidationError("Grace is not a Zombie") class PersonAdmin(admin.ModelAdmin): - list_display = ('name', 'gender', 'alive') - list_editable = ('gender', 'alive') - list_filter = ('gender',) - search_fields = ('^name',) + list_display = ("name", "gender", "alive") + list_editable = ("gender", "alive") + list_filter = ("gender",) + search_fields = ("^name",) save_as = True def get_changelist_formset(self, request, **kwargs): - return super().get_changelist_formset(request, formset=BasePersonModelFormSet, **kwargs) + return super().get_changelist_formset( + request, formset=BasePersonModelFormSet, **kwargs + ) def get_queryset(self, request): # Order by a field that isn't in list display, to be able to test # whether ordering is preserved. - return super().get_queryset(request).order_by('age') + return super().get_queryset(request).order_by("age") class FooAccountAdmin(admin.StackedInline): @@ -261,14 +367,11 @@ class BarAccountAdmin(admin.StackedInline): class PersonaAdmin(admin.ModelAdmin): - inlines = ( - FooAccountAdmin, - BarAccountAdmin - ) + inlines = (FooAccountAdmin, BarAccountAdmin) class SubscriberAdmin(admin.ModelAdmin): - actions = ['mail_admin'] + actions = ["mail_admin"] action_form = MediaActionForm def delete_queryset(self, request, queryset): @@ -278,38 +381,39 @@ class SubscriberAdmin(admin.ModelAdmin): @admin.action def mail_admin(self, request, selected): EmailMessage( - 'Greetings from a ModelAdmin action', - 'This is the test email from an admin action', - 'from@example.com', - ['to@example.com'] + "Greetings from a ModelAdmin action", + "This is the test email from an admin action", + "from@example.com", + ["to@example.com"], ).send() -@admin.action(description='External mail (Another awesome action)') +@admin.action(description="External mail (Another awesome action)") def external_mail(modeladmin, request, selected): EmailMessage( - 'Greetings from a function action', - 'This is the test email from a function action', - 'from@example.com', - ['to@example.com'] + "Greetings from a function action", + "This is the test email from a function action", + "from@example.com", + ["to@example.com"], ).send() -@admin.action(description='Redirect to (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/') + return HttpResponseRedirect("/some-where-else/") -@admin.action(description='Download subscription') + +@admin.action(description="Download subscription") def download(modeladmin, request, selected): - buf = StringIO('This is the content of the file') + buf = StringIO("This is the content of the file") return StreamingHttpResponse(FileWrapper(buf)) -@admin.action(description='No permission to run') +@admin.action(description="No permission to run") def no_perm(modeladmin, request, selected): - return HttpResponse(content='No permission to perform this action', status=403) + return HttpResponse(content="No permission to perform this action", status=403) class ExternalSubscriberAdmin(admin.ModelAdmin): @@ -317,17 +421,17 @@ class ExternalSubscriberAdmin(admin.ModelAdmin): class PodcastAdmin(admin.ModelAdmin): - list_display = ('name', 'release_date') - list_editable = ('release_date',) - date_hierarchy = 'release_date' - ordering = ('name',) + list_display = ("name", "release_date") + list_editable = ("release_date",) + date_hierarchy = "release_date" + ordering = ("name",) class VodcastAdmin(admin.ModelAdmin): - list_display = ('name', 'released') - list_editable = ('released',) + list_display = ("name", "released") + list_editable = ("released",) - ordering = ('name',) + ordering = ("name",) class ChildInline(admin.StackedInline): @@ -338,16 +442,19 @@ class ParentAdmin(admin.ModelAdmin): model = Parent inlines = [ChildInline] save_as = True - list_display = ('id', 'name',) - list_display_links = ('id',) - list_editable = ('name',) + list_display = ( + "id", + "name", + ) + list_display_links = ("id",) + list_editable = ("name",) def save_related(self, request, form, formsets, change): super().save_related(request, form, formsets, change) first_name, last_name = form.instance.name.split() for child in form.instance.child_set.all(): if len(child.name.split()) < 2: - child.name = child.name + ' ' + last_name + child.name = child.name + " " + last_name child.save() @@ -374,13 +481,16 @@ class PictureAdmin(admin.ModelAdmin): class LanguageAdmin(admin.ModelAdmin): - list_display = ['iso', 'shortlist', 'english_name', 'name'] - list_editable = ['shortlist'] + list_display = ["iso", "shortlist", "english_name", "name"] + list_editable = ["shortlist"] class RecommendationAdmin(admin.ModelAdmin): show_full_result_count = False - search_fields = ('=titletranslation__text', '=the_recommender__titletranslation__text',) + search_fields = ( + "=titletranslation__text", + "=the_recommender__titletranslation__text", + ) class WidgetInline(admin.StackedInline): @@ -404,8 +514,8 @@ class FancyDoodadInline(admin.StackedInline): class CategoryAdmin(admin.ModelAdmin): - list_display = ('id', 'collector', 'order') - list_editable = ('order',) + list_display = ("id", "collector", "order") + list_editable = ("order",) class CategoryInline(admin.StackedInline): @@ -414,8 +524,12 @@ class CategoryInline(admin.StackedInline): class CollectorAdmin(admin.ModelAdmin): inlines = [ - WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, - FancyDoodadInline, CategoryInline + WidgetInline, + DooHickeyInline, + GrommetInline, + WhatsitInline, + FancyDoodadInline, + CategoryInline, ] @@ -433,13 +547,11 @@ class LinkInline(admin.TabularInline): class SubPostInline(admin.TabularInline): model = PrePopulatedSubPost - prepopulated_fields = { - 'subslug': ('subtitle',) - } + prepopulated_fields = {"subslug": ("subtitle",)} def get_readonly_fields(self, request, obj=None): if obj and obj.published: - return ('subslug',) + return ("subslug",) return self.readonly_fields def get_prepopulated_fields(self, request, obj=None): @@ -449,16 +561,14 @@ class SubPostInline(admin.TabularInline): class PrePopulatedPostAdmin(admin.ModelAdmin): - list_display = ['title', 'slug'] - prepopulated_fields = { - 'slug': ('title',) - } + list_display = ["title", "slug"] + prepopulated_fields = {"slug": ("title",)} inlines = [SubPostInline] def get_readonly_fields(self, request, obj=None): if obj and obj.published: - return ('slug',) + return ("slug",) return self.readonly_fields def get_prepopulated_fields(self, request, obj=None): @@ -468,23 +578,26 @@ class PrePopulatedPostAdmin(admin.ModelAdmin): class PrePopulatedPostReadOnlyAdmin(admin.ModelAdmin): - prepopulated_fields = {'slug': ('title',)} + prepopulated_fields = {"slug": ("title",)} def has_change_permission(self, *args, **kwargs): return False class PostAdmin(admin.ModelAdmin): - list_display = ['title', 'public'] + list_display = ["title", "public"] readonly_fields = ( - 'posted', 'awesomeness_level', 'coolness', 'value', - 'multiline', 'multiline_html', lambda obj: "foo", - 'readonly_content', + "posted", + "awesomeness_level", + "coolness", + "value", + "multiline", + "multiline_html", + lambda obj: "foo", + "readonly_content", ) - inlines = [ - LinkInline - ] + inlines = [LinkInline] @admin.display def coolness(self, instance): @@ -493,7 +606,7 @@ class PostAdmin(admin.ModelAdmin): else: return "Unknown coolness." - @admin.display(description='Value in $US') + @admin.display(description="Value in $US") def value(self, instance): return 1000 @@ -511,10 +624,10 @@ class FieldOverridePostForm(forms.ModelForm): class Meta: help_texts = { - 'posted': 'Overridden help text for the date', + "posted": "Overridden help text for the date", } labels = { - 'public': 'Overridden public label', + "public": "Overridden public label", } @@ -524,7 +637,7 @@ class FieldOverridePostAdmin(PostAdmin): class CustomChangeList(ChangeList): def get_queryset(self, request): - return self.root_queryset.order_by('pk').filter(pk=9999) # Doesn't exist + return self.root_queryset.order_by("pk").filter(pk=9999) # Doesn't exist class GadgetAdmin(admin.ModelAdmin): @@ -533,23 +646,23 @@ class GadgetAdmin(admin.ModelAdmin): class ToppingAdmin(admin.ModelAdmin): - readonly_fields = ('pizzas',) + readonly_fields = ("pizzas",) class PizzaAdmin(admin.ModelAdmin): - readonly_fields = ('toppings',) + readonly_fields = ("toppings",) class ReadOnlyRelatedFieldAdmin(admin.ModelAdmin): - readonly_fields = ('chapter', 'language', 'user') + readonly_fields = ("chapter", "language", "user") class StudentAdmin(admin.ModelAdmin): - search_fields = ('name',) + search_fields = ("name",) class ReadOnlyPizzaAdmin(admin.ModelAdmin): - readonly_fields = ('name', 'toppings') + readonly_fields = ("name", "toppings") def has_add_permission(self, request): return False @@ -562,13 +675,13 @@ class ReadOnlyPizzaAdmin(admin.ModelAdmin): class WorkHourAdmin(admin.ModelAdmin): - list_display = ('datum', 'employee') - list_filter = ('employee',) + list_display = ("datum", "employee") + list_filter = ("employee",) class FoodDeliveryAdmin(admin.ModelAdmin): - list_display = ('reference', 'driver', 'restaurant') - list_editable = ('driver', 'restaurant') + list_display = ("reference", "driver", "restaurant") + list_editable = ("driver", "restaurant") class CoverLetterAdmin(admin.ModelAdmin): @@ -580,7 +693,7 @@ class CoverLetterAdmin(admin.ModelAdmin): """ def get_queryset(self, request): - return super().get_queryset(request).defer('date_written') + return super().get_queryset(request).defer("date_written") class PaperAdmin(admin.ModelAdmin): @@ -592,7 +705,7 @@ class PaperAdmin(admin.ModelAdmin): """ def get_queryset(self, request): - return super().get_queryset(request).only('title') + return super().get_queryset(request).only("title") class ShortMessageAdmin(admin.ModelAdmin): @@ -604,7 +717,7 @@ class ShortMessageAdmin(admin.ModelAdmin): """ def get_queryset(self, request): - return super().get_queryset(request).defer('timestamp') + return super().get_queryset(request).defer("timestamp") class TelegramAdmin(admin.ModelAdmin): @@ -616,45 +729,47 @@ class TelegramAdmin(admin.ModelAdmin): """ def get_queryset(self, request): - return super().get_queryset(request).only('title') + return super().get_queryset(request).only("title") class StoryForm(forms.ModelForm): class Meta: - widgets = {'title': forms.HiddenInput} + widgets = {"title": forms.HiddenInput} class StoryAdmin(admin.ModelAdmin): - list_display = ('id', 'title', 'content') - list_display_links = ('title',) # 'id' not in list_display_links - list_editable = ('content',) + list_display = ("id", "title", "content") + list_display_links = ("title",) # 'id' not in list_display_links + list_editable = ("content",) form = StoryForm - ordering = ['-id'] + ordering = ["-id"] class OtherStoryAdmin(admin.ModelAdmin): - list_display = ('id', 'title', 'content') - list_display_links = ('title', 'id') # 'id' in list_display_links - list_editable = ('content',) - ordering = ['-id'] + list_display = ("id", "title", "content") + list_display_links = ("title", "id") # 'id' in list_display_links + list_editable = ("content",) + ordering = ["-id"] class ComplexSortedPersonAdmin(admin.ModelAdmin): - list_display = ('name', 'age', 'is_employee', 'colored_name') - ordering = ('name',) + list_display = ("name", "age", "is_employee", "colored_name") + ordering = ("name",) - @admin.display(ordering='name') + @admin.display(ordering="name") def colored_name(self, obj): return format_html('{}', obj.name) class PluggableSearchPersonAdmin(admin.ModelAdmin): - list_display = ('name', 'age') - search_fields = ('name',) + list_display = ("name", "age") + search_fields = ("name",) def get_search_results(self, request, queryset, search_term): queryset, may_have_duplicates = super().get_search_results( - request, queryset, search_term, + request, + queryset, + search_term, ) try: search_term_as_int = int(search_term) @@ -666,51 +781,50 @@ class PluggableSearchPersonAdmin(admin.ModelAdmin): class AlbumAdmin(admin.ModelAdmin): - list_filter = ['title'] + list_filter = ["title"] class QuestionAdmin(admin.ModelAdmin): - ordering = ['-posted'] - search_fields = ['question'] - autocomplete_fields = ['related_questions'] + ordering = ["-posted"] + search_fields = ["question"] + autocomplete_fields = ["related_questions"] class AnswerAdmin(admin.ModelAdmin): - autocomplete_fields = ['question'] + autocomplete_fields = ["question"] class PrePopulatedPostLargeSlugAdmin(admin.ModelAdmin): - prepopulated_fields = { - 'slug': ('title',) - } + prepopulated_fields = {"slug": ("title",)} class AdminOrderedFieldAdmin(admin.ModelAdmin): - ordering = ('order',) - list_display = ('stuff', 'order') + ordering = ("order",) + list_display = ("stuff", "order") class AdminOrderedModelMethodAdmin(admin.ModelAdmin): - ordering = ('order',) - list_display = ('stuff', 'some_order') + ordering = ("order",) + list_display = ("stuff", "some_order") class AdminOrderedAdminMethodAdmin(admin.ModelAdmin): - @admin.display(ordering='order') + @admin.display(ordering="order") def some_admin_order(self, obj): return obj.order - ordering = ('order',) - list_display = ('stuff', 'some_admin_order') + + ordering = ("order",) + list_display = ("stuff", "some_admin_order") -@admin.display(ordering='order') +@admin.display(ordering="order") def admin_ordered_callable(obj): return obj.order class AdminOrderedCallableAdmin(admin.ModelAdmin): - ordering = ('order',) - list_display = ('stuff', admin_ordered_callable) + ordering = ("order",) + list_display = ("stuff", admin_ordered_callable) class ReportAdmin(admin.ModelAdmin): @@ -719,60 +833,67 @@ class ReportAdmin(admin.ModelAdmin): def get_urls(self): # Corner case: Don't call parent implementation - return [path('extra/', self.extra, name='cable_extra')] + return [path("extra/", self.extra, name="cable_extra")] class CustomTemplateBooleanFieldListFilter(BooleanFieldListFilter): - template = 'custom_filter_template.html' + template = "custom_filter_template.html" class CustomTemplateFilterColorAdmin(admin.ModelAdmin): - list_filter = (('warm', CustomTemplateBooleanFieldListFilter),) + list_filter = (("warm", CustomTemplateBooleanFieldListFilter),) # For Selenium Prepopulated tests ------------------------------------- class RelatedPrepopulatedInline1(admin.StackedInline): fieldsets = ( - (None, { - 'fields': ( - ('fk', 'm2m'), - ('pubdate', 'status'), - ('name', 'slug1', 'slug2',), - ), - }), + ( + None, + { + "fields": ( + ("fk", "m2m"), + ("pubdate", "status"), + ( + "name", + "slug1", + "slug2", + ), + ), + }, + ), ) - formfield_overrides = {models.CharField: {'strip': False}} + formfield_overrides = {models.CharField: {"strip": False}} model = RelatedPrepopulated extra = 1 - autocomplete_fields = ['fk', 'm2m'] + autocomplete_fields = ["fk", "m2m"] prepopulated_fields = { - 'slug1': ['name', 'pubdate'], - 'slug2': ['status', 'name'], + "slug1": ["name", "pubdate"], + "slug2": ["status", "name"], } class RelatedPrepopulatedInline2(admin.TabularInline): model = RelatedPrepopulated extra = 1 - autocomplete_fields = ['fk', 'm2m'] + autocomplete_fields = ["fk", "m2m"] prepopulated_fields = { - 'slug1': ['name', 'pubdate'], - 'slug2': ['status', 'name'], + "slug1": ["name", "pubdate"], + "slug2": ["status", "name"], } class RelatedPrepopulatedInline3(admin.TabularInline): model = RelatedPrepopulated extra = 0 - autocomplete_fields = ['fk', 'm2m'] + autocomplete_fields = ["fk", "m2m"] class RelatedPrepopulatedStackedInlineNoFieldsets(admin.StackedInline): model = RelatedPrepopulated extra = 1 prepopulated_fields = { - 'slug1': ['name', 'pubdate'], - 'slug2': ['status'], + "slug1": ["name", "pubdate"], + "slug2": ["status"], } @@ -784,28 +905,29 @@ class MainPrepopulatedAdmin(admin.ModelAdmin): RelatedPrepopulatedStackedInlineNoFieldsets, ] fieldsets = ( - (None, { - 'fields': (('pubdate', 'status'), ('name', 'slug1', 'slug2', 'slug3')) - }), + ( + None, + {"fields": (("pubdate", "status"), ("name", "slug1", "slug2", "slug3"))}, + ), ) - formfield_overrides = {models.CharField: {'strip': False}} + formfield_overrides = {models.CharField: {"strip": False}} prepopulated_fields = { - 'slug1': ['name', 'pubdate'], - 'slug2': ['status', 'name'], - 'slug3': ['name'], + "slug1": ["name", "pubdate"], + "slug2": ["status", "name"], + "slug3": ["name"], } class UnorderedObjectAdmin(admin.ModelAdmin): - list_display = ['id', 'name'] - list_display_links = ['id'] - list_editable = ['name'] + list_display = ["id", "name"] + list_display_links = ["id"] + list_editable = ["name"] list_per_page = 2 class UndeletableObjectAdmin(admin.ModelAdmin): def change_view(self, *args, **kwargs): - kwargs['extra_context'] = {'show_delete': False} + kwargs["extra_context"] = {"show_delete": False} return super().change_view(*args, **kwargs) @@ -831,8 +953,14 @@ class CustomManagerAdmin(admin.ModelAdmin): class MessageTestingAdmin(admin.ModelAdmin): - actions = ["message_debug", "message_info", "message_success", - "message_warning", "message_error", "message_extra_tags"] + actions = [ + "message_debug", + "message_info", + "message_success", + "message_warning", + "message_error", + "message_extra_tags", + ] @admin.action def message_debug(self, request, selected): @@ -860,9 +988,9 @@ class MessageTestingAdmin(admin.ModelAdmin): class ChoiceList(admin.ModelAdmin): - list_display = ['choice'] - readonly_fields = ['choice'] - fields = ['choice'] + list_display = ["choice"] + readonly_fields = ["choice"] + fields = ["choice"] class DependentChildAdminForm(forms.ModelForm): @@ -870,11 +998,16 @@ class DependentChildAdminForm(forms.ModelForm): Issue #20522 Form to test child dependency on parent object's validation """ + def clean(self): - parent = self.cleaned_data.get('parent') - if parent.family_name and parent.family_name != self.cleaned_data.get('family_name'): - raise ValidationError("Children must share a family name with their parents " + - "in this contrived test case") + parent = self.cleaned_data.get("parent") + if parent.family_name and parent.family_name != self.cleaned_data.get( + "family_name" + ): + raise ValidationError( + "Children must share a family name with their parents " + + "in this contrived test case" + ) return super().clean() @@ -889,6 +1022,7 @@ class ParentWithDependentChildrenAdmin(admin.ModelAdmin): # Tests for ticket 11277 ---------------------------------- + class FormWithoutHiddenField(forms.ModelForm): first = forms.CharField() second = forms.CharField() @@ -907,9 +1041,12 @@ class FormWithVisibleAndHiddenField(forms.ModelForm): class EmptyModelVisibleAdmin(admin.ModelAdmin): form = FormWithoutHiddenField fieldsets = ( - (None, { - 'fields': (('first', 'second'),), - }), + ( + None, + { + "fields": (("first", "second"),), + }, + ), ) @@ -933,8 +1070,8 @@ class StateAdminForm(forms.ModelForm): class Meta: model = State - fields = '__all__' - labels = {'name': 'State name (from form’s Meta.labels)'} + fields = "__all__" + labels = {"name": "State name (from form’s Meta.labels)"} @property def changed_data(self): @@ -942,7 +1079,7 @@ class StateAdminForm(forms.ModelForm): if data: # Add arbitrary name to changed_data to test # change message construction. - return data + ['not_a_form_field'] + return data + ["not_a_form_field"] return data @@ -963,20 +1100,20 @@ class CityAdmin(admin.ModelAdmin): def get_formset_kwargs(self, request, obj, inline, prefix): return { **super().get_formset_kwargs(request, obj, inline, prefix), - 'form_kwargs': {'initial': {'name': 'overridden_name'}}, + "form_kwargs": {"initial": {"name": "overridden_name"}}, } class WorkerAdmin(admin.ModelAdmin): def view_on_site(self, obj): - return '/worker/%s/%s/' % (obj.surname, obj.name) + return "/worker/%s/%s/" % (obj.surname, obj.name) class WorkerInlineAdmin(admin.TabularInline): model = Worker def view_on_site(self, obj): - return '/worker_inline/%s/%s/' % (obj.surname, obj.name) + return "/worker_inline/%s/%s/" % (obj.surname, obj.name) class RestaurantAdmin(admin.ModelAdmin): @@ -984,11 +1121,11 @@ class RestaurantAdmin(admin.ModelAdmin): view_on_site = False def get_changeform_initial_data(self, request): - return {'name': 'overridden_value'} + return {"name": "overridden_value"} class FunkyTagAdmin(admin.ModelAdmin): - list_display = ('name', 'content_object') + list_display = ("name", "content_object") class InlineReferenceInline(admin.TabularInline): @@ -1000,11 +1137,11 @@ class InlineRefererAdmin(admin.ModelAdmin): class PlotReadonlyAdmin(admin.ModelAdmin): - readonly_fields = ('plotdetails',) + readonly_fields = ("plotdetails",) class GetFormsetsArgumentCheckingAdmin(admin.ModelAdmin): - fields = ['name'] + fields = ["name"] def add_view(self, request, *args, **kwargs): request.is_add_view = True @@ -1016,19 +1153,26 @@ class GetFormsetsArgumentCheckingAdmin(admin.ModelAdmin): def get_formsets_with_inlines(self, request, obj=None): if request.is_add_view and obj is not None: - raise Exception("'obj' passed to get_formsets_with_inlines wasn't None during add_view") + raise Exception( + "'obj' passed to get_formsets_with_inlines wasn't None during add_view" + ) if not request.is_add_view and obj is None: - raise Exception("'obj' passed to get_formsets_with_inlines was None during change_view") + raise Exception( + "'obj' passed to get_formsets_with_inlines was None during change_view" + ) return super().get_formsets_with_inlines(request, obj) site = admin.AdminSite(name="admin") -site.site_url = '/my-site-url/' +site.site_url = "/my-site-url/" site.register(Article, ArticleAdmin) site.register(CustomArticle, CustomArticleAdmin) site.register( - Section, save_as=True, inlines=[ArticleInline], - readonly_fields=['name_property'], search_fields=['name'], + Section, + save_as=True, + inlines=[ArticleInline], + readonly_fields=["name_property"], + search_fields=["name"], ) site.register(ModelWithStringPrimaryKey) site.register(Color) @@ -1091,7 +1235,7 @@ site.register(InlineReferer, InlineRefererAdmin) site.register(ReferencedByGenRel) site.register(GenRelReference) site.register(ParentWithUUIDPK) -site.register(RelatedPrepopulated, search_fields=['name']) +site.register(RelatedPrepopulated, search_fields=["name"]) site.register(RelatedWithUUIDPKModel) site.register(ReadOnlyRelatedField, ReadOnlyRelatedFieldAdmin) @@ -1113,8 +1257,8 @@ site.register(Topping, ToppingAdmin) site.register(Album, AlbumAdmin) site.register(Song) site.register(Question, QuestionAdmin) -site.register(Answer, AnswerAdmin, date_hierarchy='question__posted') -site.register(Answer2, date_hierarchy='question__expires') +site.register(Answer, AnswerAdmin, date_hierarchy="question__posted") +site.register(Answer2, date_hierarchy="question__expires") site.register(PrePopulatedPost, PrePopulatedPostAdmin) site.register(ComplexSortedPerson, ComplexSortedPersonAdmin) site.register(FilteredManager, CustomManagerAdmin) @@ -1152,9 +1296,9 @@ site2.register(Group, GroupAdmin) site2.register(ParentWithUUIDPK) site2.register( RelatedWithUUIDPKModel, - list_display=['pk', 'parent'], - list_editable=['parent'], - raw_id_fields=['parent'], + list_display=["pk", "parent"], + list_editable=["parent"], + raw_id_fields=["parent"], ) site2.register(Person, save_as_continue=False) site2.register(ReadOnlyRelatedField, ReadOnlyRelatedFieldAdmin) @@ -1166,21 +1310,27 @@ site7.register(Section) site7.register(PrePopulatedPost, PrePopulatedPostReadOnlyAdmin) site7.register( Pizza, - filter_horizontal=['toppings'], + filter_horizontal=["toppings"], fieldsets=( - ('Collapsible', { - 'classes': ['collapse'], - 'fields': ['toppings'], - }), + ( + "Collapsible", + { + "classes": ["collapse"], + "fields": ["toppings"], + }, + ), ), ) site7.register( Question, - filter_horizontal=['related_questions'], + filter_horizontal=["related_questions"], fieldsets=( - ('Not collapsible', { - 'fields': ['related_questions'], - }), + ( + "Not collapsible", + { + "fields": ["related_questions"], + }, + ), ), ) @@ -1188,37 +1338,42 @@ site7.register( # Used to test ModelAdmin.sortable_by and get_sortable_by(). class ArticleAdmin6(admin.ModelAdmin): list_display = ( - 'content', 'date', callable_year, 'model_year', 'modeladmin_year', - 'model_year_reversed', 'section', + "content", + "date", + callable_year, + "model_year", + "modeladmin_year", + "model_year_reversed", + "section", ) - sortable_by = ('date', callable_year) + sortable_by = ("date", callable_year) - @admin.display(ordering='date') + @admin.display(ordering="date") def modeladmin_year(self, obj): return obj.date.year class ActorAdmin6(admin.ModelAdmin): - list_display = ('name', 'age') - sortable_by = ('name',) + list_display = ("name", "age") + sortable_by = ("name",) def get_sortable_by(self, request): - return ('age',) + return ("age",) class ChapterAdmin6(admin.ModelAdmin): - list_display = ('title', 'book') + list_display = ("title", "book") sortable_by = () class ColorAdmin6(admin.ModelAdmin): - list_display = ('value',) + list_display = ("value",) def get_sortable_by(self, request): return () -site6 = admin.AdminSite(name='admin6') +site6 = admin.AdminSite(name="admin6") site6.register(Article, ArticleAdmin6) site6.register(Actor, ActorAdmin6) site6.register(Chapter, ChapterAdmin6) @@ -1240,10 +1395,10 @@ class ActorAdmin9(admin.ModelAdmin): return urls -site9 = admin.AdminSite(name='admin9') +site9 = admin.AdminSite(name="admin9") site9.register(Article, ArticleAdmin9) site9.register(Actor, ActorAdmin9) -site10 = admin.AdminSite(name='admin10') +site10 = admin.AdminSite(name="admin10") site10.final_catch_all_view = False site10.register(Article, ArticleAdminWithExtraUrl) diff --git a/tests/admin_views/custom_has_permission_admin.py b/tests/admin_views/custom_has_permission_admin.py index 354f6a2418..6dc2011b24 100644 --- a/tests/admin_views/custom_has_permission_admin.py +++ b/tests/admin_views/custom_has_permission_admin.py @@ -6,24 +6,26 @@ from django.contrib.auth import get_permission_codename from django.contrib.auth.forms import AuthenticationForm from django.core.exceptions import ValidationError -from . import admin as base_admin, models +from . import admin as base_admin +from . import models -PERMISSION_NAME = 'admin_views.%s' % get_permission_codename('change', models.Article._meta) +PERMISSION_NAME = "admin_views.%s" % get_permission_codename( + "change", models.Article._meta +) class PermissionAdminAuthenticationForm(AuthenticationForm): def confirm_login_allowed(self, user): if not user.is_active or not (user.is_staff or user.has_perm(PERMISSION_NAME)): - raise ValidationError('permission denied') + raise ValidationError("permission denied") class HasPermissionAdmin(admin.AdminSite): login_form = PermissionAdminAuthenticationForm def has_permission(self, request): - return ( - request.user.is_active and - (request.user.is_staff or request.user.has_perm(PERMISSION_NAME)) + return request.user.is_active and ( + request.user.is_staff or request.user.has_perm(PERMISSION_NAME) ) diff --git a/tests/admin_views/customadmin.py b/tests/admin_views/customadmin.py index a9d8a060b9..f9e1f6fe1a 100644 --- a/tests/admin_views/customadmin.py +++ b/tests/admin_views/customadmin.py @@ -7,32 +7,33 @@ from django.contrib.auth.models import User from django.http import HttpResponse from django.urls import path -from . import admin as base_admin, forms, models +from . import admin as base_admin +from . import forms, models class Admin2(admin.AdminSite): - app_index_template = 'custom_admin/app_index.html' + app_index_template = "custom_admin/app_index.html" login_form = forms.CustomAdminAuthenticationForm - login_template = 'custom_admin/login.html' - logout_template = 'custom_admin/logout.html' - index_template = ['custom_admin/index.html'] # a list, to test fix for #18697 - password_change_template = 'custom_admin/password_change_form.html' - password_change_done_template = 'custom_admin/password_change_done.html' + login_template = "custom_admin/login.html" + logout_template = "custom_admin/logout.html" + index_template = ["custom_admin/index.html"] # a list, to test fix for #18697 + password_change_template = "custom_admin/password_change_form.html" + password_change_done_template = "custom_admin/password_change_done.html" # A custom index view. def index(self, request, extra_context=None): - return super().index(request, {'foo': '*bar*'}) + return super().index(request, {"foo": "*bar*"}) def get_urls(self): return [ - path('my_view/', self.admin_view(self.my_view), name='my_view'), + path("my_view/", self.admin_view(self.my_view), name="my_view"), ] + super().get_urls() def my_view(self, request): return HttpResponse("Django is a magical pony!") def password_change(self, request, extra_context=None): - return super().password_change(request, {'spam': 'eggs'}) + return super().password_change(request, {"spam": "eggs"}) class UserLimitedAdmin(UserAdmin): @@ -43,19 +44,23 @@ class UserLimitedAdmin(UserAdmin): class CustomPwdTemplateUserAdmin(UserAdmin): - change_user_password_template = ['admin/auth/user/change_password.html'] # a list, to test fix for #18697 + change_user_password_template = [ + "admin/auth/user/change_password.html" + ] # a list, to test fix for #18697 class BookAdmin(admin.ModelAdmin): def get_deleted_objects(self, objs, request): - return ['a deletable object'], {'books': 1}, set(), [] + return ["a deletable object"], {"books": 1}, set(), [] site = Admin2(name="admin2") site.register(models.Article, base_admin.ArticleAdmin) site.register(models.Book, BookAdmin) -site.register(models.Section, inlines=[base_admin.ArticleInline], search_fields=['name']) +site.register( + models.Section, inlines=[base_admin.ArticleInline], search_fields=["name"] +) site.register(models.Thing, base_admin.ThingAdmin) site.register(models.Fabric, base_admin.FabricAdmin) site.register(models.ChapterXtra1, base_admin.ChapterXtra1Admin) @@ -63,5 +68,5 @@ site.register(User, UserLimitedAdmin) site.register(models.UndeletableObject, base_admin.UndeletableObjectAdmin) site.register(models.Simple, base_admin.AttributeErrorRaisingAdmin) -simple_site = Admin2(name='admin4') +simple_site = Admin2(name="admin4") simple_site.register(User, CustomPwdTemplateUserAdmin) diff --git a/tests/admin_views/forms.py b/tests/admin_views/forms.py index b25e49246a..e9d9c0a8a5 100644 --- a/tests/admin_views/forms.py +++ b/tests/admin_views/forms.py @@ -4,17 +4,16 @@ from django.core.exceptions import ValidationError class CustomAdminAuthenticationForm(AdminAuthenticationForm): - class Media: - css = {'all': ('path/to/media.css',)} + css = {"all": ("path/to/media.css",)} def clean_username(self): - username = self.cleaned_data.get('username') - if username == 'customform': - raise ValidationError('custom form error') + username = self.cleaned_data.get("username") + if username == "customform": + raise ValidationError("custom form error") return username class MediaActionForm(ActionForm): class Media: - js = ['path/to/media.js'] + js = ["path/to/media.js"] diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index c2972e0940..f6632816e8 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -4,9 +4,7 @@ import uuid from django.contrib import admin from django.contrib.auth.models import User -from django.contrib.contenttypes.fields import ( - GenericForeignKey, GenericRelation, -) +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.core.files.storage import FileSystemStorage @@ -18,6 +16,7 @@ class Section(models.Model): A simple section that links to articles, to test linking to related items in admin views. """ + name = models.CharField(max_length=100) def __str__(self): @@ -35,26 +34,31 @@ class Article(models.Model): """ A simple article to test admin views. Test backwards compatibility. """ + title = models.CharField(max_length=100) content = models.TextField() date = models.DateTimeField() section = models.ForeignKey(Section, models.CASCADE, null=True, blank=True) - another_section = models.ForeignKey(Section, models.CASCADE, null=True, blank=True, related_name='+') - sub_section = models.ForeignKey(Section, models.SET_NULL, null=True, blank=True, related_name='+') + another_section = models.ForeignKey( + Section, models.CASCADE, null=True, blank=True, related_name="+" + ) + sub_section = models.ForeignKey( + Section, models.SET_NULL, null=True, blank=True, related_name="+" + ) def __str__(self): return self.title - @admin.display(ordering='date', description='') + @admin.display(ordering="date", description="") def model_year(self): return self.date.year - @admin.display(ordering='-date', description='') + @admin.display(ordering="-date", description="") def model_year_reversed(self): return self.date.year @property - @admin.display(ordering='date') + @admin.display(ordering="date") def model_property_year(self): return self.date.year @@ -67,14 +71,15 @@ class Book(models.Model): """ A simple book that has chapters. """ - name = models.CharField(max_length=100, verbose_name='¿Name?') + + name = models.CharField(max_length=100, verbose_name="¿Name?") def __str__(self): return self.name class Promo(models.Model): - name = models.CharField(max_length=100, verbose_name='¿Name?') + name = models.CharField(max_length=100, verbose_name="¿Name?") book = models.ForeignKey(Book, models.CASCADE) author = models.ForeignKey(User, models.SET_NULL, blank=True, null=True) @@ -83,33 +88,33 @@ class Promo(models.Model): class Chapter(models.Model): - title = models.CharField(max_length=100, verbose_name='¿Title?') + title = models.CharField(max_length=100, verbose_name="¿Title?") content = models.TextField() book = models.ForeignKey(Book, models.CASCADE) class Meta: # Use a utf-8 bytestring to ensure it works (see #11710) - verbose_name = '¿Chapter?' + verbose_name = "¿Chapter?" def __str__(self): return self.title class ChapterXtra1(models.Model): - chap = models.OneToOneField(Chapter, models.CASCADE, verbose_name='¿Chap?') - xtra = models.CharField(max_length=100, verbose_name='¿Xtra?') + chap = models.OneToOneField(Chapter, models.CASCADE, verbose_name="¿Chap?") + xtra = models.CharField(max_length=100, verbose_name="¿Xtra?") guest_author = models.ForeignKey(User, models.SET_NULL, blank=True, null=True) def __str__(self): - return '¿Xtra1: %s' % self.xtra + return "¿Xtra1: %s" % self.xtra class ChapterXtra2(models.Model): - chap = models.OneToOneField(Chapter, models.CASCADE, verbose_name='¿Chap?') - xtra = models.CharField(max_length=100, verbose_name='¿Xtra?') + chap = models.OneToOneField(Chapter, models.CASCADE, verbose_name="¿Chap?") + xtra = models.CharField(max_length=100, verbose_name="¿Xtra?") def __str__(self): - return '¿Xtra2: %s' % self.xtra + return "¿Xtra2: %s" % self.xtra class RowLevelChangePermissionModel(models.Model): @@ -128,7 +133,7 @@ class ModelWithStringPrimaryKey(models.Model): return self.string_pk def get_absolute_url(self): - return '/dummy/%s/' % self.string_pk + return "/dummy/%s/" % self.string_pk class Color(models.Model): @@ -147,7 +152,7 @@ class Color2(Color): class Thing(models.Model): title = models.CharField(max_length=20) - color = models.ForeignKey(Color, models.CASCADE, limit_choices_to={'warm': True}) + color = models.ForeignKey(Color, models.CASCADE, limit_choices_to={"warm": True}) pub_date = models.DateField(blank=True, null=True) def __str__(self): @@ -178,22 +183,22 @@ class Sketch(models.Model): Inquisition, models.CASCADE, limit_choices_to={ - 'leader__name': 'Palin', - 'leader__age': 27, - 'expected': False, + "leader__name": "Palin", + "leader__age": 27, + "expected": False, }, ) defendant0 = models.ForeignKey( Actor, models.CASCADE, - limit_choices_to={'title__isnull': False}, - related_name='as_defendant0', + limit_choices_to={"title__isnull": False}, + related_name="as_defendant0", ) defendant1 = models.ForeignKey( Actor, models.CASCADE, - limit_choices_to={'title__isnull': True}, - related_name='as_defendant1', + limit_choices_to={"title__isnull": True}, + related_name="as_defendant1", ) def __str__(self): @@ -224,7 +229,9 @@ class StumpJoke(models.Model): limit_choices_to=today_callable_dict, related_name="+", ) - has_fooled_today = models.ManyToManyField(Character, limit_choices_to=today_callable_q, related_name="+") + has_fooled_today = models.ManyToManyField( + Character, limit_choices_to=today_callable_q, related_name="+" + ) def __str__(self): return self.variation @@ -232,11 +239,14 @@ class StumpJoke(models.Model): class Fabric(models.Model): NG_CHOICES = ( - ('Textured', ( - ('x', 'Horizontal'), - ('y', 'Vertical'), - )), - ('plain', 'Smooth'), + ( + "Textured", + ( + ("x", "Horizontal"), + ("y", "Vertical"), + ), + ), + ("plain", "Smooth"), ) surface = models.CharField(max_length=20, choices=NG_CHOICES) @@ -260,6 +270,7 @@ class Persona(models.Model): A simple persona associated with accounts, to test inlining of related accounts which inherit from a common accounts class. """ + name = models.CharField(blank=False, max_length=80) def __str__(self): @@ -271,9 +282,10 @@ class Account(models.Model): A simple, generic account encapsulating the information shared by all types of accounts. """ + username = models.CharField(blank=False, max_length=80) persona = models.ForeignKey(Persona, models.CASCADE, related_name="accounts") - servicename = 'generic service' + servicename = "generic service" def __str__(self): return "%s: %s" % (self.servicename, self.username) @@ -281,12 +293,14 @@ class Account(models.Model): class FooAccount(Account): """A service-specific account of type Foo.""" - servicename = 'foo' + + servicename = "foo" class BarAccount(Account): """A service-specific account of type Bar.""" - servicename = 'bar' + + servicename = "bar" class Subscriber(models.Model): @@ -313,11 +327,13 @@ class Podcast(Media): release_date = models.DateField() class Meta: - ordering = ('release_date',) # overridden in PodcastAdmin + ordering = ("release_date",) # overridden in PodcastAdmin class Vodcast(Media): - media = models.OneToOneField(Media, models.CASCADE, primary_key=True, parent_link=True) + media = models.OneToOneField( + Media, models.CASCADE, primary_key=True, parent_link=True + ) released = models.BooleanField(default=False) @@ -325,8 +341,8 @@ class Parent(models.Model): name = models.CharField(max_length=128) def clean(self): - if self.name == '_invalid': - raise ValidationError('invalid') + if self.name == "_invalid": + raise ValidationError("invalid") class Child(models.Model): @@ -334,19 +350,20 @@ class Child(models.Model): name = models.CharField(max_length=30, blank=True) def clean(self): - if self.name == '_invalid': - raise ValidationError('invalid') + if self.name == "_invalid": + raise ValidationError("invalid") class PKChild(models.Model): """ Used to check autocomplete to_field resolution when ForeignKey is PK. """ + parent = models.ForeignKey(Parent, models.CASCADE, primary_key=True) name = models.CharField(max_length=128) class Meta: - ordering = ['parent'] + ordering = ["parent"] def __str__(self): return self.name @@ -370,7 +387,7 @@ class Gallery(models.Model): class Picture(models.Model): name = models.CharField(max_length=100) - image = models.FileField(storage=temp_storage, upload_to='test_upload') + image = models.FileField(storage=temp_storage, upload_to="test_upload") gallery = models.ForeignKey(Gallery, models.CASCADE, related_name="pictures") @@ -384,7 +401,7 @@ class Language(models.Model): return self.iso class Meta: - ordering = ('iso',) + ordering = ("iso",) # a base class for Recommender and Recommendation @@ -446,10 +463,10 @@ class Category(models.Model): order = models.PositiveIntegerField() class Meta: - ordering = ('order',) + ordering = ("order",) def __str__(self): - return '%s:o%s' % (self.id, self.order) + return "%s:o%s" % (self.id, self.order) def link_posted_default(): @@ -476,12 +493,16 @@ class PrePopulatedSubPost(models.Model): class Post(models.Model): - title = models.CharField(max_length=100, help_text='Some help text for the title (with Unicode ŠĐĆŽćžšđ)') - content = models.TextField(help_text='Some help text for the content (with Unicode ŠĐĆŽćžšđ)') + title = models.CharField( + max_length=100, help_text="Some help text for the title (with Unicode ŠĐĆŽćžšđ)" + ) + content = models.TextField( + help_text="Some help text for the content (with Unicode ŠĐĆŽćžšđ)" + ) readonly_content = models.TextField() posted = models.DateField( default=datetime.date.today, - help_text='Some help text for the date (with Unicode ŠĐĆŽćžšđ)', + help_text="Some help text for the date (with Unicode ŠĐĆŽćžšđ)", ) public = models.BooleanField(null=True, blank=True) @@ -519,7 +540,7 @@ class FunkyTag(models.Model): name = models.CharField(max_length=25) content_type = models.ForeignKey(ContentType, models.CASCADE) object_id = models.PositiveIntegerField() - content_object = GenericForeignKey('content_type', 'object_id') + content_object = GenericForeignKey("content_type", "object_id") def __str__(self): return self.name @@ -527,8 +548,8 @@ class FunkyTag(models.Model): class Plot(models.Model): name = models.CharField(max_length=100) - team_leader = models.ForeignKey(Villain, models.CASCADE, related_name='lead_plots') - contact = models.ForeignKey(Villain, models.CASCADE, related_name='contact_plots') + team_leader = models.ForeignKey(Villain, models.CASCADE, related_name="lead_plots") + contact = models.ForeignKey(Villain, models.CASCADE, related_name="contact_plots") tags = GenericRelation(FunkyTag) def __str__(self): @@ -549,7 +570,8 @@ class PlotProxy(Plot): class SecretHideout(models.Model): - """ Secret! Not registered with the admin! """ + """Secret! Not registered with the admin!""" + location = models.CharField(max_length=100) villain = models.ForeignKey(Villain, models.CASCADE) @@ -558,7 +580,8 @@ class SecretHideout(models.Model): class SuperSecretHideout(models.Model): - """ Secret! Not registered with the admin! """ + """Secret! Not registered with the admin!""" + location = models.CharField(max_length=100) supervillain = models.ForeignKey(SuperVillain, models.CASCADE) @@ -568,7 +591,7 @@ class SuperSecretHideout(models.Model): class Bookmark(models.Model): name = models.CharField(max_length=60) - tag = GenericRelation(FunkyTag, related_query_name='bookmark') + tag = GenericRelation(FunkyTag, related_query_name="bookmark") def __str__(self): return self.name @@ -576,7 +599,7 @@ class Bookmark(models.Model): class CyclicOne(models.Model): name = models.CharField(max_length=25) - two = models.ForeignKey('CyclicTwo', models.CASCADE) + two = models.ForeignKey("CyclicTwo", models.CASCADE) def __str__(self): return self.name @@ -599,7 +622,7 @@ class Topping(models.Model): class Pizza(models.Model): name = models.CharField(max_length=20) - toppings = models.ManyToManyField('Topping', related_name='pizzas') + toppings = models.ManyToManyField("Topping", related_name="pizzas") # Pizza's ModelAdmin has readonly_fields = ['toppings']. @@ -634,7 +657,7 @@ class Employee(Person): code = models.CharField(max_length=20) class Meta: - ordering = ['name'] + ordering = ["name"] class WorkHour(models.Model): @@ -646,6 +669,7 @@ class Manager(Employee): """ A multi-layer MTI child. """ + pass @@ -658,7 +682,7 @@ class Question(models.Model): question = models.CharField(max_length=20) posted = models.DateField(default=datetime.date.today) expires = models.DateTimeField(null=True, blank=True) - related_questions = models.ManyToManyField('self') + related_questions = models.ManyToManyField("self") uuid = models.UUIDField(default=uuid.uuid4, unique=True) def __str__(self): @@ -668,12 +692,15 @@ class Question(models.Model): class Answer(models.Model): question = models.ForeignKey(Question, models.PROTECT) question_with_to_field = models.ForeignKey( - Question, models.SET_NULL, - blank=True, null=True, to_field='uuid', - related_name='uuid_answers', - limit_choices_to=~models.Q(question__istartswith='not'), + Question, + models.SET_NULL, + blank=True, + null=True, + to_field="uuid", + related_name="uuid_answers", + limit_choices_to=~models.Q(question__istartswith="not"), ) - related_answers = models.ManyToManyField('self') + related_answers = models.ManyToManyField("self") answer = models.CharField(max_length=20) def __str__(self): @@ -692,17 +719,19 @@ class Reservation(models.Model): class FoodDelivery(models.Model): DRIVER_CHOICES = ( - ('bill', 'Bill G'), - ('steve', 'Steve J'), + ("bill", "Bill G"), + ("steve", "Steve J"), ) RESTAURANT_CHOICES = ( - ('indian', 'A Taste of India'), - ('thai', 'Thai Pography'), - ('pizza', 'Pizza Mama'), + ("indian", "A Taste of India"), + ("thai", "Thai Pography"), + ("pizza", "Pizza Mama"), ) reference = models.CharField(max_length=100) driver = models.CharField(max_length=100, choices=DRIVER_CHOICES, blank=True) - restaurant = models.CharField(max_length=100, choices=RESTAURANT_CHOICES, blank=True) + restaurant = models.CharField( + max_length=100, choices=RESTAURANT_CHOICES, blank=True + ) class Meta: unique_together = (("driver", "restaurant"),) @@ -761,6 +790,7 @@ class PrePopulatedPostLargeSlug(models.Model): be localized in prepopulated_fields_js.html or it might end up breaking the JavaScript (ie, using THOUSAND_SEPARATOR ends up with maxLength=1,000) """ + title = models.CharField(max_length=100) published = models.BooleanField(default=False) # `db_index=False` because MySQL cannot index large CharField (#21196). @@ -776,7 +806,7 @@ class AdminOrderedModelMethod(models.Model): order = models.IntegerField() stuff = models.CharField(max_length=200) - @admin.display(ordering='order') + @admin.display(ordering="order") def some_order(self): return self.order @@ -803,8 +833,8 @@ class MainPrepopulated(models.Model): pubdate = models.DateField() status = models.CharField( max_length=20, - choices=(('option one', 'Option One'), - ('option two', 'Option Two'))) + choices=(("option one", "Option One"), ("option two", "Option Two")), + ) slug1 = models.SlugField(blank=True) slug2 = models.SlugField(blank=True) slug3 = models.SlugField(blank=True, allow_unicode=True) @@ -813,13 +843,13 @@ class MainPrepopulated(models.Model): class RelatedPrepopulated(models.Model): parent = models.ForeignKey(MainPrepopulated, models.CASCADE) name = models.CharField(max_length=75) - fk = models.ForeignKey('self', models.CASCADE, blank=True, null=True) - m2m = models.ManyToManyField('self', blank=True) + fk = models.ForeignKey("self", models.CASCADE, blank=True, null=True) + m2m = models.ManyToManyField("self", blank=True) pubdate = models.DateField() status = models.CharField( max_length=20, - choices=(('option one', 'Option One'), - ('option two', 'Option Two'))) + choices=(("option one", "Option One"), ("option two", "Option Two")), + ) slug1 = models.SlugField(max_length=50) slug2 = models.SlugField(max_length=60) @@ -829,6 +859,7 @@ class UnorderedObject(models.Model): Model without any defined `Meta.ordering`. Refs #16819. """ + name = models.CharField(max_length=255) bool = models.BooleanField(default=True) @@ -838,6 +869,7 @@ class UndeletableObject(models.Model): Model whose show_delete in admin change_view has been disabled Refs #10057. """ + name = models.CharField(max_length=255) @@ -862,8 +894,9 @@ class Simple(models.Model): class Choice(models.Model): choice = models.IntegerField( - blank=True, null=True, - choices=((1, 'Yes'), (0, 'No'), (None, 'No opinion')), + blank=True, + null=True, + choices=((1, "Yes"), (0, "No"), (None, "No opinion")), ) @@ -873,6 +906,7 @@ class ParentWithDependentChildren(models.Model): Model where the validation of child foreign-key relationships depends on validation of the parent """ + some_required_info = models.PositiveIntegerField() family_name = models.CharField(max_length=255, blank=False) @@ -883,6 +917,7 @@ class DependentChild(models.Model): Model that depends on validation of the parent class for one of its fields to validate during clean """ + parent = models.ForeignKey(ParentWithDependentChildren, models.CASCADE) family_name = models.CharField(max_length=255) @@ -901,27 +936,27 @@ class FilteredManager(models.Model): class EmptyModelVisible(models.Model): - """ See ticket #11277. """ + """See ticket #11277.""" class EmptyModelHidden(models.Model): - """ See ticket #11277. """ + """See ticket #11277.""" class EmptyModelMixin(models.Model): - """ See ticket #11277. """ + """See ticket #11277.""" class State(models.Model): - name = models.CharField(max_length=100, verbose_name='State verbose_name') + name = models.CharField(max_length=100, verbose_name="State verbose_name") class City(models.Model): state = models.ForeignKey(State, models.CASCADE) - name = models.CharField(max_length=100, verbose_name='City verbose_name') + name = models.CharField(max_length=100, verbose_name="City verbose_name") def get_absolute_url(self): - return '/dummy/%s/' % self.pk + return "/dummy/%s/" % self.pk class Restaurant(models.Model): @@ -929,7 +964,7 @@ class Restaurant(models.Model): name = models.CharField(max_length=100) def get_absolute_url(self): - return '/dummy/%s/' % self.pk + return "/dummy/%s/" % self.pk class Worker(models.Model): @@ -947,8 +982,8 @@ class ParentWithFK(models.Model): fk = models.ForeignKey( ReferencedByParent, models.CASCADE, - to_field='name', - related_name='hidden+', + to_field="name", + related_name="hidden+", ) @@ -970,8 +1005,8 @@ class InlineReference(models.Model): fk = models.ForeignKey( ReferencedByInline, models.CASCADE, - to_field='name', - related_name='hidden+', + to_field="name", + related_name="hidden+", ) @@ -981,12 +1016,12 @@ class Recipe(models.Model): class Ingredient(models.Model): iname = models.CharField(max_length=20, unique=True) - recipes = models.ManyToManyField(Recipe, through='RecipeIngredient') + recipes = models.ManyToManyField(Recipe, through="RecipeIngredient") class RecipeIngredient(models.Model): - ingredient = models.ForeignKey(Ingredient, models.CASCADE, to_field='iname') - recipe = models.ForeignKey(Recipe, models.CASCADE, to_field='rname') + ingredient = models.ForeignKey(Ingredient, models.CASCADE, to_field="iname") + recipe = models.ForeignKey(Recipe, models.CASCADE, to_field="rname") # Model for #23839 @@ -1008,7 +1043,7 @@ class ImplicitlyGeneratedPK(models.Model): class ReferencedByGenRel(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() - content_object = GenericForeignKey('content_type', 'object_id') + content_object = GenericForeignKey("content_type", "object_id") class GenRelReference(models.Model): @@ -1024,7 +1059,9 @@ class ParentWithUUIDPK(models.Model): class RelatedWithUUIDPKModel(models.Model): - parent = models.ForeignKey(ParentWithUUIDPK, on_delete=models.SET_NULL, null=True, blank=True) + parent = models.ForeignKey( + ParentWithUUIDPK, on_delete=models.SET_NULL, null=True, blank=True + ) class Author(models.Model): @@ -1038,6 +1075,7 @@ class Authorship(models.Model): class UserProxy(User): """Proxy a model with a different app_label.""" + class Meta: proxy = True @@ -1054,5 +1092,9 @@ class Héllo(models.Model): class Box(models.Model): title = models.CharField(max_length=100) - next_box = models.ForeignKey("self", null=True, on_delete=models.SET_NULL, blank=True) - next_box = models.ForeignKey("self", null=True, on_delete=models.SET_NULL, blank=True) + next_box = models.ForeignKey( + "self", null=True, on_delete=models.SET_NULL, blank=True + ) + next_box = models.ForeignKey( + "self", null=True, on_delete=models.SET_NULL, blank=True + ) diff --git a/tests/admin_views/test_actions.py b/tests/admin_views/test_actions.py index 6c5aa5ad53..2bdcff44ae 100644 --- a/tests/admin_views/test_actions.py +++ b/tests/admin_views/test_actions.py @@ -12,19 +12,29 @@ from django.urls import reverse from .admin import SubscriberAdmin from .forms import MediaActionForm from .models import ( - Actor, Answer, Book, ExternalSubscriber, Question, Subscriber, + Actor, + Answer, + Book, + ExternalSubscriber, + Question, + Subscriber, UnchangeableObject, ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminActionsTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = ExternalSubscriber.objects.create(name='John Doe', email='john@example.org') - cls.s2 = Subscriber.objects.create(name='Max Mustermann', email='max@example.org') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = ExternalSubscriber.objects.create( + name="John Doe", email="john@example.org" + ) + cls.s2 = Subscriber.objects.create( + name="Max Mustermann", email="max@example.org" + ) def setUp(self): self.client.force_login(self.superuser) @@ -33,44 +43,56 @@ class AdminActionsTest(TestCase): """A custom action defined in a ModelAdmin method.""" action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'mail_admin', - 'index': 0, + "action": "mail_admin", + "index": 0, } - self.client.post(reverse('admin:admin_views_subscriber_changelist'), action_data) + self.client.post( + reverse("admin:admin_views_subscriber_changelist"), action_data + ) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'Greetings from a ModelAdmin action') + self.assertEqual(mail.outbox[0].subject, "Greetings from a ModelAdmin action") def test_model_admin_default_delete_action(self): action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk, self.s2.pk], - 'action': 'delete_selected', - 'index': 0, + "action": "delete_selected", + "index": 0, } delete_confirmation_data = { ACTION_CHECKBOX_NAME: [self.s1.pk, self.s2.pk], - 'action': 'delete_selected', - 'post': 'yes', + "action": "delete_selected", + "post": "yes", } - confirmation = self.client.post(reverse('admin:admin_views_subscriber_changelist'), action_data) + confirmation = self.client.post( + reverse("admin:admin_views_subscriber_changelist"), action_data + ) self.assertIsInstance(confirmation, TemplateResponse) - self.assertContains(confirmation, 'Are you sure you want to delete the selected subscribers?') - self.assertContains(confirmation, '

Summary

') - self.assertContains(confirmation, '
  • Subscribers: 2
  • ') - self.assertContains(confirmation, '
  • External subscribers: 1
  • ') + self.assertContains( + confirmation, "Are you sure you want to delete the selected subscribers?" + ) + self.assertContains(confirmation, "

    Summary

    ") + self.assertContains(confirmation, "
  • Subscribers: 2
  • ") + self.assertContains(confirmation, "
  • External subscribers: 1
  • ") self.assertContains(confirmation, ACTION_CHECKBOX_NAME, count=2) - self.client.post(reverse('admin:admin_views_subscriber_changelist'), delete_confirmation_data) + self.client.post( + reverse("admin:admin_views_subscriber_changelist"), delete_confirmation_data + ) self.assertEqual(Subscriber.objects.count(), 0) def test_default_delete_action_nonexistent_pk(self): self.assertFalse(Subscriber.objects.filter(id=9998).exists()) action_data = { - ACTION_CHECKBOX_NAME: ['9998'], - 'action': 'delete_selected', - 'index': 0, + ACTION_CHECKBOX_NAME: ["9998"], + "action": "delete_selected", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_subscriber_changelist'), action_data) - self.assertContains(response, 'Are you sure you want to delete the selected subscribers?') - self.assertContains(response, '', html=True) + response = self.client.post( + reverse("admin:admin_views_subscriber_changelist"), action_data + ) + self.assertContains( + response, "Are you sure you want to delete the selected subscribers?" + ) + self.assertContains(response, "", html=True) @override_settings(USE_THOUSAND_SEPARATOR=True, NUMBER_GROUPING=3) def test_non_localized_pk(self): @@ -81,11 +103,13 @@ class AdminActionsTest(TestCase): s = ExternalSubscriber.objects.create(id=9999) action_data = { ACTION_CHECKBOX_NAME: [s.pk, self.s2.pk], - 'action': 'delete_selected', - 'index': 0, + "action": "delete_selected", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_subscriber_changelist'), action_data) - self.assertTemplateUsed(response, 'admin/delete_selected_confirmation.html') + response = self.client.post( + reverse("admin:admin_views_subscriber_changelist"), action_data + ) + self.assertTemplateUsed(response, "admin/delete_selected_confirmation.html") self.assertContains(response, 'value="9999"') # Instead of 9,999 self.assertContains(response, 'value="%s"' % self.s2.pk) @@ -94,33 +118,43 @@ class AdminActionsTest(TestCase): The default delete action where some related objects are protected from deletion. """ - q1 = Question.objects.create(question='Why?') - a1 = Answer.objects.create(question=q1, answer='Because.') - a2 = Answer.objects.create(question=q1, answer='Yes.') - q2 = Question.objects.create(question='Wherefore?') + q1 = Question.objects.create(question="Why?") + a1 = Answer.objects.create(question=q1, answer="Because.") + a2 = Answer.objects.create(question=q1, answer="Yes.") + q2 = Question.objects.create(question="Wherefore?") action_data = { ACTION_CHECKBOX_NAME: [q1.pk, q2.pk], - 'action': 'delete_selected', - 'index': 0, + "action": "delete_selected", + "index": 0, } delete_confirmation_data = action_data.copy() - delete_confirmation_data['post'] = 'yes' - response = self.client.post(reverse('admin:admin_views_question_changelist'), action_data) - self.assertContains(response, 'would require deleting the following protected related objects') + delete_confirmation_data["post"] = "yes" + response = self.client.post( + reverse("admin:admin_views_question_changelist"), action_data + ) + self.assertContains( + response, "would require deleting the following protected related objects" + ) self.assertContains( response, - '
  • Answer: Because.
  • ' % reverse('admin:admin_views_answer_change', args=(a1.pk,)), - html=True + '
  • Answer: Because.
  • ' + % reverse("admin:admin_views_answer_change", args=(a1.pk,)), + html=True, ) self.assertContains( response, - '
  • Answer: Yes.
  • ' % reverse('admin:admin_views_answer_change', args=(a2.pk,)), - html=True + '
  • Answer: Yes.
  • ' + % reverse("admin:admin_views_answer_change", args=(a2.pk,)), + html=True, ) # A POST request to delete protected objects displays the page which # says the deletion is prohibited. - response = self.client.post(reverse('admin:admin_views_question_changelist'), delete_confirmation_data) - self.assertContains(response, 'would require deleting the following protected related objects') + response = self.client.post( + reverse("admin:admin_views_question_changelist"), delete_confirmation_data + ) + self.assertContains( + response, "would require deleting the following protected related objects" + ) self.assertEqual(Question.objects.count(), 2) def test_model_admin_default_delete_action_no_change_url(self): @@ -131,58 +165,68 @@ class AdminActionsTest(TestCase): obj = UnchangeableObject.objects.create() action_data = { ACTION_CHECKBOX_NAME: obj.pk, - 'action': 'delete_selected', - 'index': '0', + "action": "delete_selected", + "index": "0", } - response = self.client.post(reverse('admin:admin_views_unchangeableobject_changelist'), action_data) + response = self.client.post( + reverse("admin:admin_views_unchangeableobject_changelist"), action_data + ) # No 500 caused by NoReverseMatch. The page doesn't display a link to # the nonexistent change page. - self.assertContains(response, '
  • Unchangeable object: %s
  • ' % obj, 1, html=True) + self.assertContains( + response, "
  • Unchangeable object: %s
  • " % obj, 1, html=True + ) def test_delete_queryset_hook(self): delete_confirmation_data = { ACTION_CHECKBOX_NAME: [self.s1.pk, self.s2.pk], - 'action': 'delete_selected', - 'post': 'yes', - 'index': 0, + "action": "delete_selected", + "post": "yes", + "index": 0, } SubscriberAdmin.overridden = False - self.client.post(reverse('admin:admin_views_subscriber_changelist'), delete_confirmation_data) + self.client.post( + reverse("admin:admin_views_subscriber_changelist"), delete_confirmation_data + ) # SubscriberAdmin.delete_queryset() sets overridden to True. self.assertIs(SubscriberAdmin.overridden, True) self.assertEqual(Subscriber.objects.all().count(), 0) def test_delete_selected_uses_get_deleted_objects(self): """The delete_selected action uses ModelAdmin.get_deleted_objects().""" - book = Book.objects.create(name='Test Book') + book = Book.objects.create(name="Test Book") data = { ACTION_CHECKBOX_NAME: [book.pk], - 'action': 'delete_selected', - 'index': 0, + "action": "delete_selected", + "index": 0, } - response = self.client.post(reverse('admin2:admin_views_book_changelist'), data) + response = self.client.post(reverse("admin2:admin_views_book_changelist"), data) # BookAdmin.get_deleted_objects() returns custom text. - self.assertContains(response, 'a deletable object') + self.assertContains(response, "a deletable object") def test_custom_function_mail_action(self): """A custom action may be defined in a function.""" action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'external_mail', - 'index': 0, + "action": "external_mail", + "index": 0, } - self.client.post(reverse('admin:admin_views_externalsubscriber_changelist'), action_data) + self.client.post( + reverse("admin:admin_views_externalsubscriber_changelist"), action_data + ) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'Greetings from a function action') + self.assertEqual(mail.outbox[0].subject, "Greetings from a function action") def test_custom_function_action_with_redirect(self): """Another custom action defined in a function.""" action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'redirect_to', - 'index': 0, + "action": "redirect_to", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_externalsubscriber_changelist'), action_data) + response = self.client.post( + reverse("admin:admin_views_externalsubscriber_changelist"), action_data + ) self.assertEqual(response.status_code, 302) def test_default_redirect(self): @@ -192,10 +236,10 @@ class AdminActionsTest(TestCase): """ action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'external_mail', - 'index': 0, + "action": "external_mail", + "index": 0, } - url = reverse('admin:admin_views_externalsubscriber_changelist') + '?o=1' + url = reverse("admin:admin_views_externalsubscriber_changelist") + "?o=1" response = self.client.post(url, action_data) self.assertRedirects(response, url) @@ -203,29 +247,37 @@ class AdminActionsTest(TestCase): """A custom action may return a StreamingHttpResponse.""" action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'download', - 'index': 0, + "action": "download", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_externalsubscriber_changelist'), action_data) - content = b''.join(response.streaming_content) - self.assertEqual(content, b'This is the content of the file') + response = self.client.post( + reverse("admin:admin_views_externalsubscriber_changelist"), action_data + ) + content = b"".join(response.streaming_content) + self.assertEqual(content, b"This is the content of the file") self.assertEqual(response.status_code, 200) def test_custom_function_action_no_perm_response(self): """A custom action may returns an HttpResponse with a 403 code.""" action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'no_perm', - 'index': 0, + "action": "no_perm", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_externalsubscriber_changelist'), action_data) + response = self.client.post( + reverse("admin:admin_views_externalsubscriber_changelist"), action_data + ) self.assertEqual(response.status_code, 403) - self.assertEqual(response.content, b'No permission to perform this action') + self.assertEqual(response.content, b"No permission to perform this action") def test_actions_ordering(self): """Actions are ordered as expected.""" - response = self.client.get(reverse('admin:admin_views_externalsubscriber_changelist')) - self.assertContains(response, '''