diff options
author | django-bot <ops@djangoproject.com> | 2022-02-03 20:24:19 +0100 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-02-07 20:37:05 +0100 |
commit | 9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch) | |
tree | f0506b668a013d0063e5fba3dbf4863b466713ba /tests/admin_views | |
parent | f68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff) | |
download | django-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz |
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/admin_views')
-rw-r--r-- | tests/admin_views/admin.py | 727 | ||||
-rw-r--r-- | tests/admin_views/custom_has_permission_admin.py | 14 | ||||
-rw-r--r-- | tests/admin_views/customadmin.py | 33 | ||||
-rw-r--r-- | tests/admin_views/forms.py | 11 | ||||
-rw-r--r-- | tests/admin_views/models.py | 238 | ||||
-rw-r--r-- | tests/admin_views/test_actions.py | 414 | ||||
-rw-r--r-- | tests/admin_views/test_adminsite.py | 72 | ||||
-rw-r--r-- | tests/admin_views/test_autocomplete_view.py | 442 | ||||
-rw-r--r-- | tests/admin_views/test_forms.py | 25 | ||||
-rw-r--r-- | tests/admin_views/test_history_view.py | 79 | ||||
-rw-r--r-- | tests/admin_views/test_multidb.py | 38 | ||||
-rw-r--r-- | tests/admin_views/test_nav_sidebar.py | 187 | ||||
-rw-r--r-- | tests/admin_views/test_templatetags.py | 147 | ||||
-rw-r--r-- | tests/admin_views/tests.py | 5981 | ||||
-rw-r--r-- | tests/admin_views/urls.py | 42 | ||||
-rw-r--r-- | tests/admin_views/views.py | 2 |
16 files changed, 5122 insertions, 3330 deletions
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('<span style="color: #ff00ff;">{}</span>', 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, '<h2>Summary</h2>') - self.assertContains(confirmation, '<li>Subscribers: 2</li>') - self.assertContains(confirmation, '<li>External subscribers: 1</li>') + self.assertContains( + confirmation, "Are you sure you want to delete the selected subscribers?" + ) + self.assertContains(confirmation, "<h2>Summary</h2>") + self.assertContains(confirmation, "<li>Subscribers: 2</li>") + self.assertContains(confirmation, "<li>External subscribers: 1</li>") 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, '<ul></ul>', 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, "<ul></ul>", 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, - '<li>Answer: <a href="%s">Because.</a></li>' % reverse('admin:admin_views_answer_change', args=(a1.pk,)), - html=True + '<li>Answer: <a href="%s">Because.</a></li>' + % reverse("admin:admin_views_answer_change", args=(a1.pk,)), + html=True, ) self.assertContains( response, - '<li>Answer: <a href="%s">Yes.</a></li>' % reverse('admin:admin_views_answer_change', args=(a2.pk,)), - html=True + '<li>Answer: <a href="%s">Yes.</a></li>' + % 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, '<li>Unchangeable object: %s</li>' % obj, 1, html=True) + self.assertContains( + response, "<li>Unchangeable object: %s</li>" % 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, '''<label>Action: <select name="action" required> + response = self.client.get( + reverse("admin:admin_views_externalsubscriber_changelist") + ) + self.assertContains( + response, + """<label>Action: <select name="action" required> <option value="" selected>---------</option> <option value="delete_selected">Delete selected external subscribers</option> @@ -234,15 +286,20 @@ subscribers</option> action)</option> <option value="download">Download subscription</option> <option value="no_perm">No permission to run</option> -</select>''', html=True) +</select>""", + html=True, + ) def test_model_without_action(self): """A ModelAdmin might not have any actions.""" - response = self.client.get(reverse('admin:admin_views_oldsubscriber_changelist')) - self.assertIsNone(response.context['action_form']) + response = self.client.get( + reverse("admin:admin_views_oldsubscriber_changelist") + ) + self.assertIsNone(response.context["action_form"]) self.assertNotContains( - response, '<input type="checkbox" class="action-select"', - msg_prefix='Found an unexpected action toggle checkboxbox in response' + response, + '<input type="checkbox" class="action-select"', + msg_prefix="Found an unexpected action toggle checkboxbox in response", ) self.assertNotContains(response, '<input type="checkbox" class="action-select"') @@ -250,18 +307,21 @@ action)</option> """ A ModelAdmin without any actions still has jQuery included on the page. """ - response = self.client.get(reverse('admin:admin_views_oldsubscriber_changelist')) - self.assertIsNone(response.context['action_form']) + response = self.client.get( + reverse("admin:admin_views_oldsubscriber_changelist") + ) + self.assertIsNone(response.context["action_form"]) self.assertContains( - response, 'jquery.min.js', - msg_prefix='jQuery missing from admin pages for model with no admin actions' + response, + "jquery.min.js", + msg_prefix="jQuery missing from admin pages for model with no admin actions", ) def test_action_column_class(self): """The checkbox column class is present in the response.""" - response = self.client.get(reverse('admin:admin_views_subscriber_changelist')) - self.assertIsNotNone(response.context['action_form']) - self.assertContains(response, 'action-checkbox-column') + response = self.client.get(reverse("admin:admin_views_subscriber_changelist")) + self.assertIsNotNone(response.context["action_form"]) + self.assertContains(response, "action-checkbox-column") def test_multiple_actions_form(self): """ @@ -270,24 +330,26 @@ action)</option> action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], # Two different actions selected on the two forms... - 'action': ['external_mail', 'delete_selected'], + "action": ["external_mail", "delete_selected"], # ...but "go" was clicked on the top form. - 'index': 0 + "index": 0, } - self.client.post(reverse('admin:admin_views_externalsubscriber_changelist'), action_data) + self.client.post( + reverse("admin:admin_views_externalsubscriber_changelist"), action_data + ) # The action sends mail rather than deletes. 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_media_from_actions_form(self): """ The action form's media is included in the changelist view's media. """ - response = self.client.get(reverse('admin:admin_views_subscriber_changelist')) + response = self.client.get(reverse("admin:admin_views_subscriber_changelist")) media_path = MediaActionForm.Media.js[0] - self.assertIsInstance(response.context['action_form'], MediaActionForm) - self.assertIn('media', response.context) - self.assertIn(media_path, response.context['media']._js) + self.assertIsInstance(response.context["action_form"], MediaActionForm) + self.assertIn("media", response.context) + self.assertIn(media_path, response.context["media"]._js) self.assertContains(response, media_path) def test_user_message_on_none_selected(self): @@ -296,14 +358,14 @@ action)</option> """ action_data = { ACTION_CHECKBOX_NAME: [], - 'action': 'delete_selected', - 'index': 0, + "action": "delete_selected", + "index": 0, } - url = reverse('admin:admin_views_subscriber_changelist') + url = reverse("admin:admin_views_subscriber_changelist") response = self.client.post(url, action_data) self.assertRedirects(response, url, fetch_redirect_response=False) response = self.client.get(response.url) - msg = 'Items must be selected in order to perform actions on them. No items have been changed.' + msg = "Items must be selected in order to perform actions on them. No items have been changed." self.assertContains(response, msg) self.assertEqual(Subscriber.objects.count(), 2) @@ -313,28 +375,28 @@ action)</option> """ action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk, self.s2.pk], - 'action': '', - 'index': 0, + "action": "", + "index": 0, } - url = reverse('admin:admin_views_subscriber_changelist') + url = reverse("admin:admin_views_subscriber_changelist") response = self.client.post(url, action_data) self.assertRedirects(response, url, fetch_redirect_response=False) response = self.client.get(response.url) - self.assertContains(response, 'No action selected.') + self.assertContains(response, "No action selected.") self.assertEqual(Subscriber.objects.count(), 2) def test_selection_counter(self): """The selection counter is there.""" - response = self.client.get(reverse('admin:admin_views_subscriber_changelist')) - self.assertContains(response, '0 of 2 selected') + response = self.client.get(reverse("admin:admin_views_subscriber_changelist")) + self.assertContains(response, "0 of 2 selected") def test_popup_actions(self): - """ Actions aren't shown in popups.""" - changelist_url = reverse('admin:admin_views_subscriber_changelist') + """Actions aren't shown in popups.""" + changelist_url = reverse("admin:admin_views_subscriber_changelist") response = self.client.get(changelist_url) - self.assertIsNotNone(response.context['action_form']) - response = self.client.get(changelist_url + '?%s' % IS_POPUP_VAR) - self.assertIsNone(response.context['action_form']) + self.assertIsNotNone(response.context["action_form"]) + response = self.client.get(changelist_url + "?%s" % IS_POPUP_VAR) + self.assertIsNone(response.context["action_form"]) def test_popup_template_response_on_add(self): """ @@ -342,78 +404,90 @@ action)</option> easy customization. """ response = self.client.post( - reverse('admin:admin_views_actor_add') + '?%s=1' % IS_POPUP_VAR, - {'name': 'Troy McClure', 'age': '55', IS_POPUP_VAR: '1'} + reverse("admin:admin_views_actor_add") + "?%s=1" % IS_POPUP_VAR, + {"name": "Troy McClure", "age": "55", IS_POPUP_VAR: "1"}, ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.template_name, [ - 'admin/admin_views/actor/popup_response.html', - 'admin/admin_views/popup_response.html', - 'admin/popup_response.html', - ]) - self.assertTemplateUsed(response, 'admin/popup_response.html') + self.assertEqual( + response.template_name, + [ + "admin/admin_views/actor/popup_response.html", + "admin/admin_views/popup_response.html", + "admin/popup_response.html", + ], + ) + self.assertTemplateUsed(response, "admin/popup_response.html") def test_popup_template_response_on_change(self): - instance = Actor.objects.create(name='David Tennant', age=45) + instance = Actor.objects.create(name="David Tennant", age=45) response = self.client.post( - reverse('admin:admin_views_actor_change', args=(instance.pk,)) + '?%s=1' % IS_POPUP_VAR, - {'name': 'David Tennant', 'age': '46', IS_POPUP_VAR: '1'} + reverse("admin:admin_views_actor_change", args=(instance.pk,)) + + "?%s=1" % IS_POPUP_VAR, + {"name": "David Tennant", "age": "46", IS_POPUP_VAR: "1"}, ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.template_name, [ - 'admin/admin_views/actor/popup_response.html', - 'admin/admin_views/popup_response.html', - 'admin/popup_response.html', - ]) - self.assertTemplateUsed(response, 'admin/popup_response.html') + self.assertEqual( + response.template_name, + [ + "admin/admin_views/actor/popup_response.html", + "admin/admin_views/popup_response.html", + "admin/popup_response.html", + ], + ) + self.assertTemplateUsed(response, "admin/popup_response.html") def test_popup_template_response_on_delete(self): - instance = Actor.objects.create(name='David Tennant', age=45) + instance = Actor.objects.create(name="David Tennant", age=45) response = self.client.post( - reverse('admin:admin_views_actor_delete', args=(instance.pk,)) + '?%s=1' % IS_POPUP_VAR, - {IS_POPUP_VAR: '1'} + reverse("admin:admin_views_actor_delete", args=(instance.pk,)) + + "?%s=1" % IS_POPUP_VAR, + {IS_POPUP_VAR: "1"}, ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.template_name, [ - 'admin/admin_views/actor/popup_response.html', - 'admin/admin_views/popup_response.html', - 'admin/popup_response.html', - ]) - self.assertTemplateUsed(response, 'admin/popup_response.html') + self.assertEqual( + response.template_name, + [ + "admin/admin_views/actor/popup_response.html", + "admin/admin_views/popup_response.html", + "admin/popup_response.html", + ], + ) + self.assertTemplateUsed(response, "admin/popup_response.html") def test_popup_template_escaping(self): - popup_response_data = json.dumps({ - 'new_value': 'new_value\\', - 'obj': 'obj\\', - 'value': 'value\\', - }) + popup_response_data = json.dumps( + { + "new_value": "new_value\\", + "obj": "obj\\", + "value": "value\\", + } + ) context = { - 'popup_response_data': popup_response_data, + "popup_response_data": popup_response_data, } - output = render_to_string('admin/popup_response.html', context) - self.assertIn( - r'"value\\"', output - ) - self.assertIn( - r'"new_value\\"', output - ) - self.assertIn( - r'"obj\\"', output - ) + output = render_to_string("admin/popup_response.html", context) + self.assertIn(r""value\\"", output) + self.assertIn(r""new_value\\"", output) + self.assertIn(r""obj\\"", output) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminActionsPermissionTests(TestCase): - @classmethod def setUpTestData(cls): - 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.s1 = ExternalSubscriber.objects.create( + name="John Doe", email="john@example.org" + ) + cls.s2 = Subscriber.objects.create( + name="Max Mustermann", email="max@example.org" + ) cls.user = User.objects.create_user( - username='user', password='secret', email='user@example.com', + username="user", + password="secret", + email="user@example.com", is_staff=True, ) - permission = Permission.objects.get(codename='change_subscriber') + permission = Permission.objects.get(codename="change_subscriber") cls.user.user_permissions.add(permission) def setUp(self): @@ -426,25 +500,27 @@ class AdminActionsPermissionTests(TestCase): """ action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'delete_selected', + "action": "delete_selected", } - url = reverse('admin:admin_views_subscriber_changelist') + url = reverse("admin:admin_views_subscriber_changelist") response = self.client.post(url, action_data) self.assertRedirects(response, url, fetch_redirect_response=False) response = self.client.get(response.url) - self.assertContains(response, 'No action selected.') + self.assertContains(response, "No action selected.") def test_model_admin_no_delete_permission_externalsubscriber(self): """ Permission is denied if the user doesn't have delete permission for a related model (ExternalSubscriber). """ - permission = Permission.objects.get(codename='delete_subscriber') + permission = Permission.objects.get(codename="delete_subscriber") self.user.user_permissions.add(permission) delete_confirmation_data = { ACTION_CHECKBOX_NAME: [self.s1.pk, self.s2.pk], - 'action': 'delete_selected', - 'post': 'yes', + "action": "delete_selected", + "post": "yes", } - response = self.client.post(reverse('admin:admin_views_subscriber_changelist'), delete_confirmation_data) + response = self.client.post( + reverse("admin:admin_views_subscriber_changelist"), delete_confirmation_data + ) self.assertEqual(response.status_code, 403) diff --git a/tests/admin_views/test_adminsite.py b/tests/admin_views/test_adminsite.py index 4874bc48c8..68a32567d8 100644 --- a/tests/admin_views/test_adminsite.py +++ b/tests/admin_views/test_adminsite.py @@ -12,71 +12,76 @@ site.register(User) site.register(Article) urlpatterns = [ - path('test_admin/admin/', site.urls), + path("test_admin/admin/", site.urls), ] -@override_settings(ROOT_URLCONF='admin_views.test_adminsite') +@override_settings(ROOT_URLCONF="admin_views.test_adminsite") class SiteEachContextTest(TestCase): """ Check each_context contains the documented variables and that available_apps context variable structure is the expected one. """ + request_factory = RequestFactory() @classmethod def setUpTestData(cls): - cls.u1 = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.u1 = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): - request = self.request_factory.get(reverse('test_adminsite:index')) + request = self.request_factory.get(reverse("test_adminsite:index")) request.user = self.u1 self.ctx = site.each_context(request) def test_each_context(self): ctx = self.ctx - self.assertEqual(ctx['site_header'], 'Django administration') - self.assertEqual(ctx['site_title'], 'Django site admin') - self.assertEqual(ctx['site_url'], '/') - self.assertIs(ctx['has_permission'], True) + self.assertEqual(ctx["site_header"], "Django administration") + self.assertEqual(ctx["site_title"], "Django site admin") + self.assertEqual(ctx["site_url"], "/") + self.assertIs(ctx["has_permission"], True) def test_each_context_site_url_with_script_name(self): - request = self.request_factory.get(reverse('test_adminsite:index'), SCRIPT_NAME='/my-script-name/') + request = self.request_factory.get( + reverse("test_adminsite:index"), SCRIPT_NAME="/my-script-name/" + ) request.user = self.u1 - self.assertEqual(site.each_context(request)['site_url'], '/my-script-name/') + self.assertEqual(site.each_context(request)["site_url"], "/my-script-name/") def test_available_apps(self): ctx = self.ctx - apps = ctx['available_apps'] + apps = ctx["available_apps"] # we have registered two models from two different apps self.assertEqual(len(apps), 2) # admin_views.Article admin_views = apps[0] - self.assertEqual(admin_views['app_label'], 'admin_views') - self.assertEqual(len(admin_views['models']), 1) - article = admin_views['models'][0] - self.assertEqual(article['object_name'], 'Article') - self.assertEqual(article['model'], Article) + self.assertEqual(admin_views["app_label"], "admin_views") + self.assertEqual(len(admin_views["models"]), 1) + article = admin_views["models"][0] + self.assertEqual(article["object_name"], "Article") + self.assertEqual(article["model"], Article) # auth.User auth = apps[1] - self.assertEqual(auth['app_label'], 'auth') - self.assertEqual(len(auth['models']), 1) - user = auth['models'][0] - self.assertEqual(user['object_name'], 'User') - self.assertEqual(user['model'], User) + self.assertEqual(auth["app_label"], "auth") + self.assertEqual(len(auth["models"]), 1) + user = auth["models"][0] + self.assertEqual(user["object_name"], "User") + self.assertEqual(user["model"], User) - self.assertEqual(auth['app_url'], '/test_admin/admin/auth/') - self.assertIs(auth['has_module_perms'], True) + self.assertEqual(auth["app_url"], "/test_admin/admin/auth/") + self.assertIs(auth["has_module_perms"], True) - self.assertIn('perms', user) - self.assertIs(user['perms']['add'], True) - self.assertIs(user['perms']['change'], True) - self.assertIs(user['perms']['delete'], True) - self.assertEqual(user['admin_url'], '/test_admin/admin/auth/user/') - self.assertEqual(user['add_url'], '/test_admin/admin/auth/user/add/') - self.assertEqual(user['name'], 'Users') + self.assertIn("perms", user) + self.assertIs(user["perms"]["add"], True) + self.assertIs(user["perms"]["change"], True) + self.assertIs(user["perms"]["delete"], True) + self.assertEqual(user["admin_url"], "/test_admin/admin/auth/user/") + self.assertEqual(user["add_url"], "/test_admin/admin/auth/user/add/") + self.assertEqual(user["name"], "Users") class SiteActionsTests(SimpleTestCase): @@ -86,11 +91,12 @@ class SiteActionsTests(SimpleTestCase): def test_add_action(self): def test_action(): pass + self.site.add_action(test_action) - self.assertEqual(self.site.get_action('test_action'), test_action) + self.assertEqual(self.site.get_action("test_action"), test_action) def test_disable_action(self): - action_name = 'delete_selected' + action_name = "delete_selected" self.assertEqual(self.site._actions[action_name], delete_selected) self.site.disable_action(action_name) with self.assertRaises(KeyError): @@ -98,7 +104,7 @@ class SiteActionsTests(SimpleTestCase): def test_get_action(self): """AdminSite.get_action() returns an action even if it's disabled.""" - action_name = 'delete_selected' + action_name = "delete_selected" self.assertEqual(self.site.get_action(action_name), delete_selected) self.site.disable_action(action_name) self.assertEqual(self.site.get_action(action_name), delete_selected) diff --git a/tests/admin_views/test_autocomplete_view.py b/tests/admin_views/test_autocomplete_view.py index dfaba42009..febddc1547 100644 --- a/tests/admin_views/test_autocomplete_view.py +++ b/tests/admin_views/test_autocomplete_view.py @@ -14,8 +14,18 @@ from django.urls import reverse, reverse_lazy from .admin import AnswerAdmin, QuestionAdmin from .models import ( - Answer, Author, Authorship, Bonus, Book, Employee, Manager, Parent, - PKChild, Question, Toy, WorkHour, + Answer, + Author, + Authorship, + Bonus, + Book, + Employee, + Manager, + Parent, + PKChild, + Question, + Toy, + WorkHour, ) from .tests import AdminViewBasicTestCase @@ -23,30 +33,30 @@ PAGINATOR_SIZE = AutocompleteJsonView.paginate_by class AuthorAdmin(admin.ModelAdmin): - ordering = ['id'] - search_fields = ['id'] + ordering = ["id"] + search_fields = ["id"] class AuthorshipInline(admin.TabularInline): model = Authorship - autocomplete_fields = ['author'] + autocomplete_fields = ["author"] class BookAdmin(admin.ModelAdmin): inlines = [AuthorshipInline] -site = admin.AdminSite(name='autocomplete_admin') +site = admin.AdminSite(name="autocomplete_admin") site.register(Question, QuestionAdmin) site.register(Answer, AnswerAdmin) site.register(Author, AuthorAdmin) site.register(Book, BookAdmin) -site.register(Employee, search_fields=['name']) -site.register(WorkHour, autocomplete_fields=['employee']) -site.register(Manager, search_fields=['name']) -site.register(Bonus, autocomplete_fields=['recipient']) -site.register(PKChild, search_fields=['name']) -site.register(Toy, autocomplete_fields=['child']) +site.register(Employee, search_fields=["name"]) +site.register(WorkHour, autocomplete_fields=["employee"]) +site.register(Manager, search_fields=["name"]) +site.register(Bonus, autocomplete_fields=["recipient"]) +site.register(PKChild, search_fields=["name"]) +site.register(Toy, autocomplete_fields=["child"]) @contextmanager @@ -63,70 +73,87 @@ def model_admin(model, model_admin, admin_site=site): class AutocompleteJsonViewTests(AdminViewBasicTestCase): - as_view_args = {'admin_site': site} + as_view_args = {"admin_site": site} opts = { - 'app_label': Answer._meta.app_label, - 'model_name': Answer._meta.model_name, - 'field_name': 'question' + "app_label": Answer._meta.app_label, + "model_name": Answer._meta.model_name, + "field_name": "question", } factory = RequestFactory() - url = reverse_lazy('autocomplete_admin:autocomplete') + url = reverse_lazy("autocomplete_admin:autocomplete") @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( - username='user', password='secret', - email='user@example.com', is_staff=True, + username="user", + password="secret", + email="user@example.com", + is_staff=True, ) super().setUpTestData() def test_success(self): - q = Question.objects.create(question='Is this a question?') - request = self.factory.get(self.url, {'term': 'is', **self.opts}) + q = Question.objects.create(question="Is this a question?") + request = self.factory.get(self.url, {"term": "is", **self.opts}) request.user = self.superuser response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(q.pk), 'text': q.question}], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [{"id": str(q.pk), "text": q.question}], + "pagination": {"more": False}, + }, + ) def test_custom_to_field(self): - q = Question.objects.create(question='Is this a question?') - request = self.factory.get(self.url, {'term': 'is', **self.opts, 'field_name': 'question_with_to_field'}) + q = Question.objects.create(question="Is this a question?") + request = self.factory.get( + self.url, + {"term": "is", **self.opts, "field_name": "question_with_to_field"}, + ) request.user = self.superuser response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(q.uuid), 'text': q.question}], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [{"id": str(q.uuid), "text": q.question}], + "pagination": {"more": False}, + }, + ) def test_custom_to_field_permission_denied(self): - Question.objects.create(question='Is this a question?') - request = self.factory.get(self.url, {'term': 'is', **self.opts, 'field_name': 'question_with_to_field'}) + Question.objects.create(question="Is this a question?") + request = self.factory.get( + self.url, + {"term": "is", **self.opts, "field_name": "question_with_to_field"}, + ) request.user = self.user with self.assertRaises(PermissionDenied): AutocompleteJsonView.as_view(**self.as_view_args)(request) def test_custom_to_field_custom_pk(self): - q = Question.objects.create(question='Is this a question?') + q = Question.objects.create(question="Is this a question?") opts = { - 'app_label': Question._meta.app_label, - 'model_name': Question._meta.model_name, - 'field_name': 'related_questions', + "app_label": Question._meta.app_label, + "model_name": Question._meta.model_name, + "field_name": "related_questions", } - request = self.factory.get(self.url, {'term': 'is', **opts}) + request = self.factory.get(self.url, {"term": "is", **opts}) request.user = self.superuser response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(q.big_id), 'text': q.question}], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [{"id": str(q.big_id), "text": q.question}], + "pagination": {"more": False}, + }, + ) def test_to_field_resolution_with_mti(self): """ @@ -134,59 +161,75 @@ class AutocompleteJsonViewTests(AdminViewBasicTestCase): MTI. Tests for single and multi-level cases. """ tests = [ - (Employee, WorkHour, 'employee'), - (Manager, Bonus, 'recipient'), + (Employee, WorkHour, "employee"), + (Manager, Bonus, "recipient"), ] for Target, Remote, related_name in tests: - with self.subTest(target_model=Target, remote_model=Remote, related_name=related_name): - o = Target.objects.create(name="Frida Kahlo", gender=2, code="painter", alive=False) + with self.subTest( + target_model=Target, remote_model=Remote, related_name=related_name + ): + o = Target.objects.create( + name="Frida Kahlo", gender=2, code="painter", alive=False + ) opts = { - 'app_label': Remote._meta.app_label, - 'model_name': Remote._meta.model_name, - 'field_name': related_name, + "app_label": Remote._meta.app_label, + "model_name": Remote._meta.model_name, + "field_name": related_name, } - request = self.factory.get(self.url, {'term': 'frida', **opts}) + request = self.factory.get(self.url, {"term": "frida", **opts}) request.user = self.superuser response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(o.pk), 'text': o.name}], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [{"id": str(o.pk), "text": o.name}], + "pagination": {"more": False}, + }, + ) def test_to_field_resolution_with_fk_pk(self): p = Parent.objects.create(name="Bertie") c = PKChild.objects.create(parent=p, name="Anna") opts = { - 'app_label': Toy._meta.app_label, - 'model_name': Toy._meta.model_name, - 'field_name': 'child', + "app_label": Toy._meta.app_label, + "model_name": Toy._meta.model_name, + "field_name": "child", } - request = self.factory.get(self.url, {'term': 'anna', **opts}) + request = self.factory.get(self.url, {"term": "anna", **opts}) request.user = self.superuser response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(c.pk), 'text': c.name}], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [{"id": str(c.pk), "text": c.name}], + "pagination": {"more": False}, + }, + ) def test_field_does_not_exist(self): - request = self.factory.get(self.url, {'term': 'is', **self.opts, 'field_name': 'does_not_exist'}) + request = self.factory.get( + self.url, {"term": "is", **self.opts, "field_name": "does_not_exist"} + ) request.user = self.superuser with self.assertRaises(PermissionDenied): AutocompleteJsonView.as_view(**self.as_view_args)(request) def test_field_no_related_field(self): - request = self.factory.get(self.url, {'term': 'is', **self.opts, 'field_name': 'answer'}) + request = self.factory.get( + self.url, {"term": "is", **self.opts, "field_name": "answer"} + ) request.user = self.superuser with self.assertRaises(PermissionDenied): AutocompleteJsonView.as_view(**self.as_view_args)(request) def test_field_does_not_allowed(self): - request = self.factory.get(self.url, {'term': 'is', **self.opts, 'field_name': 'related_questions'}) + request = self.factory.get( + self.url, {"term": "is", **self.opts, "field_name": "related_questions"} + ) request.user = self.superuser with self.assertRaises(PermissionDenied): AutocompleteJsonView.as_view(**self.as_view_args)(request) @@ -194,23 +237,29 @@ class AutocompleteJsonViewTests(AdminViewBasicTestCase): def test_limit_choices_to(self): # Answer.question_with_to_field defines limit_choices_to to "those not # starting with 'not'". - q = Question.objects.create(question='Is this a question?') - Question.objects.create(question='Not a question.') - request = self.factory.get(self.url, {'term': 'is', **self.opts, 'field_name': 'question_with_to_field'}) + q = Question.objects.create(question="Is this a question?") + Question.objects.create(question="Not a question.") + request = self.factory.get( + self.url, + {"term": "is", **self.opts, "field_name": "question_with_to_field"}, + ) request.user = self.superuser response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(q.uuid), 'text': q.question}], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [{"id": str(q.uuid), "text": q.question}], + "pagination": {"more": False}, + }, + ) def test_must_be_logged_in(self): - response = self.client.get(self.url, {'term': '', **self.opts}) + response = self.client.get(self.url, {"term": "", **self.opts}) self.assertEqual(response.status_code, 200) self.client.logout() - response = self.client.get(self.url, {'term': '', **self.opts}) + response = self.client.get(self.url, {"term": "", **self.opts}) self.assertEqual(response.status_code, 302) def test_has_view_or_change_permission_required(self): @@ -218,16 +267,16 @@ class AutocompleteJsonViewTests(AdminViewBasicTestCase): Users require the change permission for the related model to the autocomplete view for it. """ - request = self.factory.get(self.url, {'term': 'is', **self.opts}) + request = self.factory.get(self.url, {"term": "is", **self.opts}) request.user = self.user with self.assertRaises(PermissionDenied): AutocompleteJsonView.as_view(**self.as_view_args)(request) - for permission in ('view', 'change'): + for permission in ("view", "change"): with self.subTest(permission=permission): self.user.user_permissions.clear() p = Permission.objects.get( content_type=ContentType.objects.get_for_model(Question), - codename='%s_question' % permission, + codename="%s_question" % permission, ) self.user.user_permissions.add(p) request.user = User.objects.get(pk=self.user.pk) @@ -239,106 +288,134 @@ class AutocompleteJsonViewTests(AdminViewBasicTestCase): Searching across model relations use QuerySet.distinct() to avoid duplicates. """ - q1 = Question.objects.create(question='question 1') - q2 = Question.objects.create(question='question 2') + q1 = Question.objects.create(question="question 1") + q2 = Question.objects.create(question="question 2") q2.related_questions.add(q1) - q3 = Question.objects.create(question='question 3') + q3 = Question.objects.create(question="question 3") q3.related_questions.add(q1) - request = self.factory.get(self.url, {'term': 'question', **self.opts}) + request = self.factory.get(self.url, {"term": "question", **self.opts}) request.user = self.superuser class DistinctQuestionAdmin(QuestionAdmin): - search_fields = ['related_questions__question', 'question'] + search_fields = ["related_questions__question", "question"] with model_admin(Question, DistinctQuestionAdmin): response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data['results']), 3) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual(len(data["results"]), 3) def test_missing_search_fields(self): class EmptySearchAdmin(QuestionAdmin): search_fields = [] with model_admin(Question, EmptySearchAdmin): - msg = 'EmptySearchAdmin must have search_fields for the autocomplete_view.' + msg = "EmptySearchAdmin must have search_fields for the autocomplete_view." with self.assertRaisesMessage(Http404, msg): - site.autocomplete_view(self.factory.get(self.url, {'term': '', **self.opts})) + site.autocomplete_view( + self.factory.get(self.url, {"term": "", **self.opts}) + ) def test_get_paginator(self): """Search results are paginated.""" + class PKOrderingQuestionAdmin(QuestionAdmin): - ordering = ['pk'] + ordering = ["pk"] - Question.objects.bulk_create(Question(question=str(i)) for i in range(PAGINATOR_SIZE + 10)) + Question.objects.bulk_create( + Question(question=str(i)) for i in range(PAGINATOR_SIZE + 10) + ) # The first page of results. - request = self.factory.get(self.url, {'term': '', **self.opts}) + request = self.factory.get(self.url, {"term": "", **self.opts}) request.user = self.superuser with model_admin(Question, PKOrderingQuestionAdmin): response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(q.pk), 'text': q.question} for q in Question.objects.all()[:PAGINATOR_SIZE]], - 'pagination': {'more': True}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [ + {"id": str(q.pk), "text": q.question} + for q in Question.objects.all()[:PAGINATOR_SIZE] + ], + "pagination": {"more": True}, + }, + ) # The second page of results. - request = self.factory.get(self.url, {'term': '', 'page': '2', **self.opts}) + request = self.factory.get(self.url, {"term": "", "page": "2", **self.opts}) request.user = self.superuser with model_admin(Question, PKOrderingQuestionAdmin): response = AutocompleteJsonView.as_view(**self.as_view_args)(request) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [{'id': str(q.pk), 'text': q.question} for q in Question.objects.all()[PAGINATOR_SIZE:]], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [ + {"id": str(q.pk), "text": q.question} + for q in Question.objects.all()[PAGINATOR_SIZE:] + ], + "pagination": {"more": False}, + }, + ) def test_serialize_result(self): class AutocompleteJsonSerializeResultView(AutocompleteJsonView): def serialize_result(self, obj, to_field_name): return { **super().serialize_result(obj, to_field_name), - 'posted': str(obj.posted), + "posted": str(obj.posted), } - Question.objects.create(question='Question 1', posted=datetime.date(2021, 8, 9)) - Question.objects.create(question='Question 2', posted=datetime.date(2021, 8, 7)) - request = self.factory.get(self.url, {'term': 'question', **self.opts}) + Question.objects.create(question="Question 1", posted=datetime.date(2021, 8, 9)) + Question.objects.create(question="Question 2", posted=datetime.date(2021, 8, 7)) + request = self.factory.get(self.url, {"term": "question", **self.opts}) request.user = self.superuser - response = AutocompleteJsonSerializeResultView.as_view(**self.as_view_args)(request) + response = AutocompleteJsonSerializeResultView.as_view(**self.as_view_args)( + request + ) self.assertEqual(response.status_code, 200) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(data, { - 'results': [ - {'id': str(q.pk), 'text': q.question, 'posted': str(q.posted)} - for q in Question.objects.order_by('-posted') - ], - 'pagination': {'more': False}, - }) + data = json.loads(response.content.decode("utf-8")) + self.assertEqual( + data, + { + "results": [ + {"id": str(q.pk), "text": q.question, "posted": str(q.posted)} + for q in Question.objects.order_by("-posted") + ], + "pagination": {"more": False}, + }, + ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class SeleniumTests(AdminSeleniumTestCase): - available_apps = ['admin_views'] + AdminSeleniumTestCase.available_apps + available_apps = ["admin_views"] + AdminSeleniumTestCase.available_apps def setUp(self): self.superuser = User.objects.create_superuser( - username='super', password='secret', email='super@example.com', + username="super", + password="secret", + email="super@example.com", + ) + self.admin_login( + username="super", + password="secret", + login_url=reverse("autocomplete_admin:index"), ) - self.admin_login(username='super', password='secret', login_url=reverse('autocomplete_admin:index')) @contextmanager def select2_ajax_wait(self, timeout=10): from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec + yield with self.disable_implicit_wait(): try: loading_element = self.selenium.find_element( - By.CSS_SELECTOR, - 'li.select2-results__option.loading-results' + By.CSS_SELECTOR, "li.select2-results__option.loading-results" ) except NoSuchElementException: pass @@ -349,109 +426,146 @@ class SeleniumTests(AdminSeleniumTestCase): from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select - self.selenium.get(self.live_server_url + reverse('autocomplete_admin:admin_views_answer_add')) - elem = self.selenium.find_element(By.CSS_SELECTOR, '.select2-selection') + + self.selenium.get( + self.live_server_url + reverse("autocomplete_admin:admin_views_answer_add") + ) + elem = self.selenium.find_element(By.CSS_SELECTOR, ".select2-selection") elem.click() # Open the autocomplete dropdown. - results = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results') + results = self.selenium.find_element(By.CSS_SELECTOR, ".select2-results") self.assertTrue(results.is_displayed()) - option = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results__option') - self.assertEqual(option.text, 'No results found') + option = self.selenium.find_element(By.CSS_SELECTOR, ".select2-results__option") + self.assertEqual(option.text, "No results found") elem.click() # Close the autocomplete dropdown. - q1 = Question.objects.create(question='Who am I?') - Question.objects.bulk_create(Question(question=str(i)) for i in range(PAGINATOR_SIZE + 10)) + q1 = Question.objects.create(question="Who am I?") + Question.objects.bulk_create( + Question(question=str(i)) for i in range(PAGINATOR_SIZE + 10) + ) elem.click() # Reopen the dropdown now that some objects exist. - result_container = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results') + result_container = self.selenium.find_element( + By.CSS_SELECTOR, ".select2-results" + ) self.assertTrue(result_container.is_displayed()) # PAGINATOR_SIZE results and "Loading more results". - self.assertCountSeleniumElements('.select2-results__option', PAGINATOR_SIZE + 1, root_element=result_container) - search = self.selenium.find_element(By.CSS_SELECTOR, '.select2-search__field') + self.assertCountSeleniumElements( + ".select2-results__option", + PAGINATOR_SIZE + 1, + root_element=result_container, + ) + search = self.selenium.find_element(By.CSS_SELECTOR, ".select2-search__field") # Load next page of results by scrolling to the bottom of the list. with self.select2_ajax_wait(): for _ in range(PAGINATOR_SIZE + 1): search.send_keys(Keys.ARROW_DOWN) # All objects are now loaded. self.assertCountSeleniumElements( - '.select2-results__option', + ".select2-results__option", PAGINATOR_SIZE + 11, root_element=result_container, ) # Limit the results with the search field. with self.select2_ajax_wait(): - search.send_keys('Who') + search.send_keys("Who") # Ajax request is delayed. self.assertTrue(result_container.is_displayed()) self.assertCountSeleniumElements( - '.select2-results__option', + ".select2-results__option", PAGINATOR_SIZE + 12, root_element=result_container, ) self.assertTrue(result_container.is_displayed()) - self.assertCountSeleniumElements('.select2-results__option', 1, root_element=result_container) + self.assertCountSeleniumElements( + ".select2-results__option", 1, root_element=result_container + ) # Select the result. search.send_keys(Keys.RETURN) - select = Select(self.selenium.find_element(By.ID, 'id_question')) - self.assertEqual(select.first_selected_option.get_attribute('value'), str(q1.pk)) + select = Select(self.selenium.find_element(By.ID, "id_question")) + self.assertEqual( + select.first_selected_option.get_attribute("value"), str(q1.pk) + ) def test_select_multiple(self): from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select - self.selenium.get(self.live_server_url + reverse('autocomplete_admin:admin_views_question_add')) - elem = self.selenium.find_element(By.CSS_SELECTOR, '.select2-selection') + + self.selenium.get( + self.live_server_url + + reverse("autocomplete_admin:admin_views_question_add") + ) + elem = self.selenium.find_element(By.CSS_SELECTOR, ".select2-selection") elem.click() # Open the autocomplete dropdown. - results = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results') + results = self.selenium.find_element(By.CSS_SELECTOR, ".select2-results") self.assertTrue(results.is_displayed()) - option = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results__option') - self.assertEqual(option.text, 'No results found') + option = self.selenium.find_element(By.CSS_SELECTOR, ".select2-results__option") + self.assertEqual(option.text, "No results found") elem.click() # Close the autocomplete dropdown. - Question.objects.create(question='Who am I?') - Question.objects.bulk_create(Question(question=str(i)) for i in range(PAGINATOR_SIZE + 10)) + Question.objects.create(question="Who am I?") + Question.objects.bulk_create( + Question(question=str(i)) for i in range(PAGINATOR_SIZE + 10) + ) elem.click() # Reopen the dropdown now that some objects exist. - result_container = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results') + result_container = self.selenium.find_element( + By.CSS_SELECTOR, ".select2-results" + ) self.assertTrue(result_container.is_displayed()) - self.assertCountSeleniumElements('.select2-results__option', PAGINATOR_SIZE + 1, root_element=result_container) - search = self.selenium.find_element(By.CSS_SELECTOR, '.select2-search__field') + self.assertCountSeleniumElements( + ".select2-results__option", + PAGINATOR_SIZE + 1, + root_element=result_container, + ) + search = self.selenium.find_element(By.CSS_SELECTOR, ".select2-search__field") # Load next page of results by scrolling to the bottom of the list. with self.select2_ajax_wait(): for _ in range(PAGINATOR_SIZE + 1): search.send_keys(Keys.ARROW_DOWN) - self.assertCountSeleniumElements('.select2-results__option', 31, root_element=result_container) + self.assertCountSeleniumElements( + ".select2-results__option", 31, root_element=result_container + ) # Limit the results with the search field. with self.select2_ajax_wait(): - search.send_keys('Who') + search.send_keys("Who") # Ajax request is delayed. self.assertTrue(result_container.is_displayed()) - self.assertCountSeleniumElements('.select2-results__option', 32, root_element=result_container) + self.assertCountSeleniumElements( + ".select2-results__option", 32, root_element=result_container + ) self.assertTrue(result_container.is_displayed()) - self.assertCountSeleniumElements('.select2-results__option', 1, root_element=result_container) + self.assertCountSeleniumElements( + ".select2-results__option", 1, root_element=result_container + ) # Select the result. search.send_keys(Keys.RETURN) # Reopen the dropdown and add the first result to the selection. elem.click() search.send_keys(Keys.ARROW_DOWN) search.send_keys(Keys.RETURN) - select = Select(self.selenium.find_element(By.ID, 'id_related_questions')) + select = Select(self.selenium.find_element(By.ID, "id_related_questions")) self.assertEqual(len(select.all_selected_options), 2) def test_inline_add_another_widgets(self): from selenium.webdriver.common.by import By def assertNoResults(row): - elem = row.find_element(By.CSS_SELECTOR, '.select2-selection') + elem = row.find_element(By.CSS_SELECTOR, ".select2-selection") elem.click() # Open the autocomplete dropdown. - results = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results') + results = self.selenium.find_element(By.CSS_SELECTOR, ".select2-results") self.assertTrue(results.is_displayed()) - option = self.selenium.find_element(By.CSS_SELECTOR, '.select2-results__option') - self.assertEqual(option.text, 'No results found') + option = self.selenium.find_element( + By.CSS_SELECTOR, ".select2-results__option" + ) + self.assertEqual(option.text, "No results found") # Autocomplete works in rows present when the page loads. - self.selenium.get(self.live_server_url + reverse('autocomplete_admin:admin_views_book_add')) - rows = self.selenium.find_elements(By.CSS_SELECTOR, '.dynamic-authorship_set') + self.selenium.get( + self.live_server_url + reverse("autocomplete_admin:admin_views_book_add") + ) + rows = self.selenium.find_elements(By.CSS_SELECTOR, ".dynamic-authorship_set") self.assertEqual(len(rows), 3) assertNoResults(rows[0]) # Autocomplete works in rows added using the "Add another" button. - self.selenium.find_element(By.LINK_TEXT, 'Add another Authorship').click() - rows = self.selenium.find_elements(By.CSS_SELECTOR, '.dynamic-authorship_set') + self.selenium.find_element(By.LINK_TEXT, "Add another Authorship").click() + rows = self.selenium.find_elements(By.CSS_SELECTOR, ".dynamic-authorship_set") self.assertEqual(len(rows), 4) assertNoResults(rows[-1]) diff --git a/tests/admin_views/test_forms.py b/tests/admin_views/test_forms.py index f9a56c02af..91d504b4f9 100644 --- a/tests/admin_views/test_forms.py +++ b/tests/admin_views/test_forms.py @@ -8,28 +8,35 @@ from .admin import ArticleForm # To verify that the login form rejects inactive users, use an authentication # backend that allows them. -@override_settings(AUTHENTICATION_BACKENDS=['django.contrib.auth.backends.AllowAllUsersModelBackend']) +@override_settings( + AUTHENTICATION_BACKENDS=["django.contrib.auth.backends.AllowAllUsersModelBackend"] +) class AdminAuthenticationFormTests(TestCase): @classmethod def setUpTestData(cls): - User.objects.create_user(username='inactive', password='password', is_active=False) + User.objects.create_user( + username="inactive", password="password", is_active=False + ) def test_inactive_user(self): data = { - 'username': 'inactive', - 'password': 'password', + "username": "inactive", + "password": "password", } form = AdminAuthenticationForm(None, data) - self.assertEqual(form.non_field_errors(), ['This account is inactive.']) + self.assertEqual(form.non_field_errors(), ["This account is inactive."]) class AdminFormTests(SimpleTestCase): def test_repr(self): fieldsets = ( - ('My fields', { - 'classes': ['collapse'], - 'fields': ('url', 'title', 'content', 'sites'), - }), + ( + "My fields", + { + "classes": ["collapse"], + "fields": ("url", "title", "content", "sites"), + }, + ), ) form = ArticleForm() admin_form = AdminForm(form, fieldsets, {}) diff --git a/tests/admin_views/test_history_view.py b/tests/admin_views/test_history_view.py index c8d678476f..ddfdd56ca6 100644 --- a/tests/admin_views/test_history_view.py +++ b/tests/admin_views/test_history_view.py @@ -9,13 +9,14 @@ from django.urls import reverse from .models import City, State -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminHistoryViewTests(TestCase): - @classmethod def setUpTestData(cls): cls.superuser = User.objects.create_superuser( - username='super', password='secret', email='super@example.com', + username="super", + password="secret", + email="super@example.com", ) def setUp(self): @@ -26,35 +27,39 @@ class AdminHistoryViewTests(TestCase): Admin's model history change messages use form labels instead of field names. """ - state = State.objects.create(name='My State Name') - city = City.objects.create(name='My City Name', state=state) + state = State.objects.create(name="My State Name") + city = City.objects.create(name="My City Name", state=state) change_dict = { - 'name': 'My State Name 2', - 'nolabel_form_field': True, - 'city_set-0-name': 'My City name 2', - 'city_set-0-id': city.pk, - 'city_set-TOTAL_FORMS': '3', - 'city_set-INITIAL_FORMS': '1', - 'city_set-MAX_NUM_FORMS': '0', + "name": "My State Name 2", + "nolabel_form_field": True, + "city_set-0-name": "My City name 2", + "city_set-0-id": city.pk, + "city_set-TOTAL_FORMS": "3", + "city_set-INITIAL_FORMS": "1", + "city_set-MAX_NUM_FORMS": "0", } - state_change_url = reverse('admin:admin_views_state_change', args=(state.pk,)) + state_change_url = reverse("admin:admin_views_state_change", args=(state.pk,)) self.client.post(state_change_url, change_dict) - logentry = LogEntry.objects.filter(content_type__model__iexact='state').latest('id') + logentry = LogEntry.objects.filter(content_type__model__iexact="state").latest( + "id" + ) self.assertEqual( logentry.get_change_message(), - 'Changed State name (from form’s Meta.labels), ' - 'nolabel_form_field and not_a_form_field. ' - 'Changed City verbose_name for city “%s”.' % city + "Changed State name (from form’s Meta.labels), " + "nolabel_form_field and not_a_form_field. " + "Changed City verbose_name for city “%s”." % city, ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class SeleniumTests(AdminSeleniumTestCase): - available_apps = ['admin_views'] + AdminSeleniumTestCase.available_apps + available_apps = ["admin_views"] + AdminSeleniumTestCase.available_apps def setUp(self): self.superuser = User.objects.create_superuser( - username='super', password='secret', email='super@example.com', + username="super", + password="secret", + email="super@example.com", ) content_type_pk = ContentType.objects.get_for_model(User).pk for i in range(1, 1101): @@ -64,34 +69,38 @@ class SeleniumTests(AdminSeleniumTestCase): self.superuser.pk, repr(self.superuser), CHANGE, - change_message=f'Changed something {i}', + change_message=f"Changed something {i}", ) self.admin_login( - username='super', password='secret', login_url=reverse('admin:index'), + username="super", + password="secret", + login_url=reverse("admin:index"), ) def test_pagination(self): from selenium.webdriver.common.by import By - user_history_url = reverse('admin:auth_user_history', args=(self.superuser.pk,)) + user_history_url = reverse("admin:auth_user_history", args=(self.superuser.pk,)) self.selenium.get(self.live_server_url + user_history_url) - paginator = self.selenium.find_element(By.CSS_SELECTOR, '.paginator') + paginator = self.selenium.find_element(By.CSS_SELECTOR, ".paginator") self.assertTrue(paginator.is_displayed()) - self.assertIn('%s entries' % LogEntry.objects.count(), paginator.text) + self.assertIn("%s entries" % LogEntry.objects.count(), paginator.text) self.assertIn(str(Paginator.ELLIPSIS), paginator.text) # The current page. - current_page_link = self.selenium.find_element(By.CSS_SELECTOR, 'span.this-page') - self.assertEqual(current_page_link.text, '1') + current_page_link = self.selenium.find_element( + By.CSS_SELECTOR, "span.this-page" + ) + self.assertEqual(current_page_link.text, "1") # The last page. - last_page_link = self.selenium.find_element(By.CSS_SELECTOR, '.end') - self.assertTrue(last_page_link.text, '20') + last_page_link = self.selenium.find_element(By.CSS_SELECTOR, ".end") + self.assertTrue(last_page_link.text, "20") # Select the second page. - pages = paginator.find_elements(By.TAG_NAME, 'a') + pages = paginator.find_elements(By.TAG_NAME, "a") second_page_link = pages[0] - self.assertEqual(second_page_link.text, '2') + self.assertEqual(second_page_link.text, "2") second_page_link.click() - self.assertIn('?p=2', self.selenium.current_url) - rows = self.selenium.find_elements(By.CSS_SELECTOR, '#change-history tbody tr') - self.assertIn('Changed something 101', rows[0].text) - self.assertIn('Changed something 200', rows[-1].text) + self.assertIn("?p=2", self.selenium.current_url) + rows = self.selenium.find_elements(By.CSS_SELECTOR, "#change-history tbody tr") + self.assertIn("Changed something 101", rows[0].text) + self.assertIn("Changed something 200", rows[-1].text) diff --git a/tests/admin_views/test_multidb.py b/tests/admin_views/test_multidb.py index 7ff1f7f663..da5b6b9a59 100644 --- a/tests/admin_views/test_multidb.py +++ b/tests/admin_views/test_multidb.py @@ -17,17 +17,17 @@ class Router: db_for_write = db_for_read -site = admin.AdminSite(name='test_adminsite') +site = admin.AdminSite(name="test_adminsite") site.register(Book) urlpatterns = [ - path('admin/', site.urls), + path("admin/", site.urls), ] -@override_settings(ROOT_URLCONF=__name__, DATABASE_ROUTERS=['%s.Router' % __name__]) +@override_settings(ROOT_URLCONF=__name__, DATABASE_ROUTERS=["%s.Router" % __name__]) class MultiDatabaseTests(TestCase): - databases = {'default', 'other'} + databases = {"default", "other"} @classmethod def setUpTestData(cls): @@ -36,44 +36,52 @@ class MultiDatabaseTests(TestCase): for db in cls.databases: Router.target_db = db cls.superusers[db] = User.objects.create_superuser( - username='admin', password='something', email='test@test.org', + username="admin", + password="something", + email="test@test.org", ) - b = Book(name='Test Book') + b = Book(name="Test Book") b.save(using=db) cls.test_book_ids[db] = b.id - @mock.patch('django.contrib.admin.options.transaction') + @mock.patch("django.contrib.admin.options.transaction") def test_add_view(self, mock): for db in self.databases: with self.subTest(db=db): Router.target_db = db self.client.force_login(self.superusers[db]) self.client.post( - reverse('test_adminsite:admin_views_book_add'), - {'name': 'Foobar: 5th edition'}, + reverse("test_adminsite:admin_views_book_add"), + {"name": "Foobar: 5th edition"}, ) mock.atomic.assert_called_with(using=db) - @mock.patch('django.contrib.admin.options.transaction') + @mock.patch("django.contrib.admin.options.transaction") def test_change_view(self, mock): for db in self.databases: with self.subTest(db=db): Router.target_db = db self.client.force_login(self.superusers[db]) self.client.post( - reverse('test_adminsite:admin_views_book_change', args=[self.test_book_ids[db]]), - {'name': 'Test Book 2: Test more'}, + reverse( + "test_adminsite:admin_views_book_change", + args=[self.test_book_ids[db]], + ), + {"name": "Test Book 2: Test more"}, ) mock.atomic.assert_called_with(using=db) - @mock.patch('django.contrib.admin.options.transaction') + @mock.patch("django.contrib.admin.options.transaction") def test_delete_view(self, mock): for db in self.databases: with self.subTest(db=db): Router.target_db = db self.client.force_login(self.superusers[db]) self.client.post( - reverse('test_adminsite:admin_views_book_delete', args=[self.test_book_ids[db]]), - {'post': 'yes'}, + reverse( + "test_adminsite:admin_views_book_delete", + args=[self.test_book_ids[db]], + ), + {"post": "yes"}, ) mock.atomic.assert_called_with(using=db) diff --git a/tests/admin_views/test_nav_sidebar.py b/tests/admin_views/test_nav_sidebar.py index 329fe34b28..a58193c550 100644 --- a/tests/admin_views/test_nav_sidebar.py +++ b/tests/admin_views/test_nav_sidebar.py @@ -15,165 +15,204 @@ class AdminSiteWithoutSidebar(admin.AdminSite): enable_nav_sidebar = False -site_with_sidebar = AdminSiteWithSidebar(name='test_with_sidebar') -site_without_sidebar = AdminSiteWithoutSidebar(name='test_without_sidebar') +site_with_sidebar = AdminSiteWithSidebar(name="test_with_sidebar") +site_without_sidebar = AdminSiteWithoutSidebar(name="test_without_sidebar") site_with_sidebar.register(User) site_with_sidebar.register(Héllo) urlpatterns = [ - path('test_sidebar/admin/', site_with_sidebar.urls), - path('test_wihout_sidebar/admin/', site_without_sidebar.urls), + path("test_sidebar/admin/", site_with_sidebar.urls), + path("test_wihout_sidebar/admin/", site_without_sidebar.urls), ] -@override_settings(ROOT_URLCONF='admin_views.test_nav_sidebar') +@override_settings(ROOT_URLCONF="admin_views.test_nav_sidebar") class AdminSidebarTests(TestCase): @classmethod def setUpTestData(cls): cls.superuser = User.objects.create_superuser( - username='super', - password='secret', - email='super@example.com', + username="super", + password="secret", + email="super@example.com", ) def setUp(self): self.client.force_login(self.superuser) def test_sidebar_not_on_index(self): - response = self.client.get(reverse('test_with_sidebar:index')) + response = self.client.get(reverse("test_with_sidebar:index")) self.assertContains(response, '<div class="main" id="main">') self.assertNotContains(response, '<nav class="sticky" id="nav-sidebar">') def test_sidebar_disabled(self): - response = self.client.get(reverse('test_without_sidebar:index')) + response = self.client.get(reverse("test_without_sidebar:index")) self.assertNotContains(response, '<nav class="sticky" id="nav-sidebar">') def test_sidebar_unauthenticated(self): self.client.logout() - response = self.client.get(reverse('test_with_sidebar:login')) + response = self.client.get(reverse("test_with_sidebar:login")) self.assertNotContains(response, '<nav class="sticky" id="nav-sidebar">') def test_sidebar_aria_current_page(self): - url = reverse('test_with_sidebar:auth_user_changelist') + url = reverse("test_with_sidebar:auth_user_changelist") response = self.client.get(url) self.assertContains(response, '<nav class="sticky" id="nav-sidebar">') - self.assertContains(response, '<a href="%s" aria-current="page">Users</a>' % url) + self.assertContains( + response, '<a href="%s" aria-current="page">Users</a>' % url + ) @override_settings( - TEMPLATES=[{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }] + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ] ) def test_sidebar_aria_current_page_missing_without_request_context_processor(self): - url = reverse('test_with_sidebar:auth_user_changelist') + url = reverse("test_with_sidebar:auth_user_changelist") response = self.client.get(url) self.assertContains(response, '<nav class="sticky" id="nav-sidebar">') # Does not include aria-current attribute. self.assertContains(response, '<a href="%s">Users</a>' % url) - self.assertNotContains(response, 'aria-current') + self.assertNotContains(response, "aria-current") @override_settings(DEBUG=True) def test_included_app_list_template_context_fully_set(self): # All context variables should be set when rendering the sidebar. - url = reverse('test_with_sidebar:auth_user_changelist') - with self.assertNoLogs('django.template', 'DEBUG'): + url = reverse("test_with_sidebar:auth_user_changelist") + with self.assertNoLogs("django.template", "DEBUG"): self.client.get(url) def test_sidebar_model_name_non_ascii(self): - url = reverse('test_with_sidebar:admin_views_héllo_changelist') + url = reverse("test_with_sidebar:admin_views_héllo_changelist") response = self.client.get(url) - self.assertContains(response, '<div class="app-admin_views module current-app">') + self.assertContains( + response, '<div class="app-admin_views module current-app">' + ) self.assertContains(response, '<tr class="model-héllo current-model">') self.assertContains( response, '<th scope="row">' '<a href="/test_sidebar/admin/admin_views/h%C3%A9llo/" aria-current="page">' - 'Héllos</a></th>' + "Héllos</a></th>", ) -@override_settings(ROOT_URLCONF='admin_views.test_nav_sidebar') +@override_settings(ROOT_URLCONF="admin_views.test_nav_sidebar") class SeleniumTests(AdminSeleniumTestCase): - available_apps = ['admin_views'] + AdminSeleniumTestCase.available_apps + available_apps = ["admin_views"] + AdminSeleniumTestCase.available_apps def setUp(self): self.superuser = User.objects.create_superuser( - username='super', - password='secret', - email='super@example.com', + username="super", + password="secret", + email="super@example.com", + ) + self.admin_login( + username="super", + password="secret", + login_url=reverse("test_with_sidebar:index"), + ) + self.selenium.execute_script( + "localStorage.removeItem('django.admin.navSidebarIsOpen')" ) - self.admin_login(username='super', password='secret', login_url=reverse('test_with_sidebar:index')) - self.selenium.execute_script("localStorage.removeItem('django.admin.navSidebarIsOpen')") def test_sidebar_starts_open(self): from selenium.webdriver.common.by import By - self.selenium.get(self.live_server_url + reverse('test_with_sidebar:auth_user_changelist')) - main_element = self.selenium.find_element(By.CSS_SELECTOR, '#main') - self.assertIn('shifted', main_element.get_attribute('class').split()) + + self.selenium.get( + self.live_server_url + reverse("test_with_sidebar:auth_user_changelist") + ) + main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main") + self.assertIn("shifted", main_element.get_attribute("class").split()) def test_sidebar_can_be_closed(self): from selenium.webdriver.common.by import By - self.selenium.get(self.live_server_url + reverse('test_with_sidebar:auth_user_changelist')) - toggle_button = self.selenium.find_element(By.CSS_SELECTOR, '#toggle-nav-sidebar') - self.assertEqual(toggle_button.tag_name, 'button') - self.assertEqual(toggle_button.get_attribute('aria-label'), 'Toggle navigation') - for link in self.selenium.find_elements(By.CSS_SELECTOR, '#nav-sidebar a'): - self.assertEqual(link.get_attribute('tabIndex'), '0') + + self.selenium.get( + self.live_server_url + reverse("test_with_sidebar:auth_user_changelist") + ) + toggle_button = self.selenium.find_element( + By.CSS_SELECTOR, "#toggle-nav-sidebar" + ) + self.assertEqual(toggle_button.tag_name, "button") + self.assertEqual(toggle_button.get_attribute("aria-label"), "Toggle navigation") + for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"): + self.assertEqual(link.get_attribute("tabIndex"), "0") toggle_button.click() # Hidden sidebar is not reachable via keyboard navigation. - for link in self.selenium.find_elements(By.CSS_SELECTOR, '#nav-sidebar a'): - self.assertEqual(link.get_attribute('tabIndex'), '-1') - main_element = self.selenium.find_element(By.CSS_SELECTOR, '#main') - self.assertNotIn('shifted', main_element.get_attribute('class').split()) + for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"): + self.assertEqual(link.get_attribute("tabIndex"), "-1") + main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main") + self.assertNotIn("shifted", main_element.get_attribute("class").split()) def test_sidebar_state_persists(self): from selenium.webdriver.common.by import By - self.selenium.get(self.live_server_url + reverse('test_with_sidebar:auth_user_changelist')) - self.assertIsNone(self.selenium.execute_script("return localStorage.getItem('django.admin.navSidebarIsOpen')")) - toggle_button = self.selenium.find_element(By.CSS_SELECTOR, '#toggle-nav-sidebar') + + self.selenium.get( + self.live_server_url + reverse("test_with_sidebar:auth_user_changelist") + ) + self.assertIsNone( + self.selenium.execute_script( + "return localStorage.getItem('django.admin.navSidebarIsOpen')" + ) + ) + toggle_button = self.selenium.find_element( + By.CSS_SELECTOR, "#toggle-nav-sidebar" + ) toggle_button.click() self.assertEqual( - self.selenium.execute_script("return localStorage.getItem('django.admin.navSidebarIsOpen')"), - 'false', + self.selenium.execute_script( + "return localStorage.getItem('django.admin.navSidebarIsOpen')" + ), + "false", ) - self.selenium.get(self.live_server_url + reverse('test_with_sidebar:auth_user_changelist')) - main_element = self.selenium.find_element(By.CSS_SELECTOR, '#main') - self.assertNotIn('shifted', main_element.get_attribute('class').split()) + self.selenium.get( + self.live_server_url + reverse("test_with_sidebar:auth_user_changelist") + ) + main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main") + self.assertNotIn("shifted", main_element.get_attribute("class").split()) - toggle_button = self.selenium.find_element(By.CSS_SELECTOR, '#toggle-nav-sidebar') + toggle_button = self.selenium.find_element( + By.CSS_SELECTOR, "#toggle-nav-sidebar" + ) # Hidden sidebar is not reachable via keyboard navigation. - for link in self.selenium.find_elements(By.CSS_SELECTOR, '#nav-sidebar a'): - self.assertEqual(link.get_attribute('tabIndex'), '-1') + for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"): + self.assertEqual(link.get_attribute("tabIndex"), "-1") toggle_button.click() - for link in self.selenium.find_elements(By.CSS_SELECTOR, '#nav-sidebar a'): - self.assertEqual(link.get_attribute('tabIndex'), '0') + for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"): + self.assertEqual(link.get_attribute("tabIndex"), "0") self.assertEqual( - self.selenium.execute_script("return localStorage.getItem('django.admin.navSidebarIsOpen')"), - 'true', + self.selenium.execute_script( + "return localStorage.getItem('django.admin.navSidebarIsOpen')" + ), + "true", ) - self.selenium.get(self.live_server_url + reverse('test_with_sidebar:auth_user_changelist')) - main_element = self.selenium.find_element(By.CSS_SELECTOR, '#main') - self.assertIn('shifted', main_element.get_attribute('class').split()) + self.selenium.get( + self.live_server_url + reverse("test_with_sidebar:auth_user_changelist") + ) + main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main") + self.assertIn("shifted", main_element.get_attribute("class").split()) def test_sidebar_filter_persists(self): from selenium.webdriver.common.by import By + self.selenium.get( - self.live_server_url + - reverse('test_with_sidebar:auth_user_changelist') + self.live_server_url + reverse("test_with_sidebar:auth_user_changelist") ) filter_value_script = ( "return sessionStorage.getItem('django.admin.navSidebarFilterValue')" ) self.assertIsNone(self.selenium.execute_script(filter_value_script)) - filter_input = self.selenium.find_element(By.CSS_SELECTOR, '#nav-filter') - filter_input.send_keys('users') - self.assertEqual(self.selenium.execute_script(filter_value_script), 'users') + filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter") + filter_input.send_keys("users") + self.assertEqual(self.selenium.execute_script(filter_value_script), "users") diff --git a/tests/admin_views/test_templatetags.py b/tests/admin_views/test_templatetags.py index 14c3fcead5..a13133095b 100644 --- a/tests/admin_views/test_templatetags.py +++ b/tests/admin_views/test_templatetags.py @@ -20,24 +20,28 @@ class AdminTemplateTagsTest(AdminViewBasicTestCase): """ submit_row template tag should pass whole context. """ - request = self.request_factory.get(reverse('admin:auth_user_change', args=[self.superuser.pk])) + request = self.request_factory.get( + reverse("admin:auth_user_change", args=[self.superuser.pk]) + ) request.user = self.superuser admin = UserAdmin(User, site) - extra_context = {'extra': True} - response = admin.change_view(request, str(self.superuser.pk), extra_context=extra_context) + extra_context = {"extra": True} + response = admin.change_view( + request, str(self.superuser.pk), extra_context=extra_context + ) template_context = submit_row(response.context_data) - self.assertIs(template_context['extra'], True) - self.assertIs(template_context['show_save'], True) + self.assertIs(template_context["extra"], True) + self.assertIs(template_context["show_save"], True) def test_override_show_save_and_add_another(self): request = self.request_factory.get( - reverse('admin:auth_user_change', args=[self.superuser.pk]), + reverse("admin:auth_user_change", args=[self.superuser.pk]), ) request.user = self.superuser admin = UserAdmin(User, site) for extra_context, expected_flag in ( ({}, True), # Default. - ({'show_save_and_add_another': False}, False), + ({"show_save_and_add_another": False}, False), ): with self.subTest(show_save_and_add_another=expected_flag): response = admin.change_view( @@ -46,7 +50,9 @@ class AdminTemplateTagsTest(AdminViewBasicTestCase): extra_context=extra_context, ) template_context = submit_row(response.context_data) - self.assertIs(template_context['show_save_and_add_another'], expected_flag) + self.assertIs( + template_context["show_save_and_add_another"], expected_flag + ) def test_override_change_form_template_tags(self): """ @@ -54,37 +60,43 @@ class AdminTemplateTagsTest(AdminViewBasicTestCase): admin/app_label/model/template.html. """ article = Article.objects.all()[0] - request = self.request_factory.get(reverse('admin:admin_views_article_change', args=[article.pk])) + request = self.request_factory.get( + reverse("admin:admin_views_article_change", args=[article.pk]) + ) request.user = self.superuser admin = ArticleAdmin(Article, site) - extra_context = {'show_publish': True, 'extra': True} - response = admin.change_view(request, str(article.pk), extra_context=extra_context) + extra_context = {"show_publish": True, "extra": True} + response = admin.change_view( + request, str(article.pk), extra_context=extra_context + ) response.render() - self.assertIs(response.context_data['show_publish'], True) - self.assertIs(response.context_data['extra'], True) + self.assertIs(response.context_data["show_publish"], True) + self.assertIs(response.context_data["extra"], True) self.assertContains(response, 'name="_save"') self.assertContains(response, 'name="_publish"') - self.assertContains(response, 'override-change_form_object_tools') - self.assertContains(response, 'override-prepopulated_fields_js') + self.assertContains(response, "override-change_form_object_tools") + self.assertContains(response, "override-prepopulated_fields_js") def test_override_change_list_template_tags(self): """ admin_list template tags follow the standard search pattern admin/app_label/model/template.html. """ - request = self.request_factory.get(reverse('admin:admin_views_article_changelist')) + request = self.request_factory.get( + reverse("admin:admin_views_article_changelist") + ) request.user = self.superuser admin = ArticleAdmin(Article, site) - admin.date_hierarchy = 'date' - admin.search_fields = ('title', 'content') + admin.date_hierarchy = "date" + admin.search_fields = ("title", "content") response = admin.changelist_view(request) response.render() - self.assertContains(response, 'override-actions') - self.assertContains(response, 'override-change_list_object_tools') - self.assertContains(response, 'override-change_list_results') - self.assertContains(response, 'override-date_hierarchy') - self.assertContains(response, 'override-pagination') - self.assertContains(response, 'override-search_form') + self.assertContains(response, "override-actions") + self.assertContains(response, "override-change_list_object_tools") + self.assertContains(response, "override-change_list_results") + self.assertContains(response, "override-date_hierarchy") + self.assertContains(response, "override-pagination") + self.assertContains(response, "override-search_form") class DateHierarchyTests(TestCase): @@ -92,11 +104,13 @@ class DateHierarchyTests(TestCase): @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def test_choice_links(self): modeladmin = ModelAdmin(Question, site) - modeladmin.date_hierarchy = 'posted' + modeladmin.date_hierarchy = "posted" posted_dates = ( datetime.date(2017, 10, 1), @@ -106,67 +120,82 @@ class DateHierarchyTests(TestCase): datetime.date(2017, 12, 31), datetime.date(2018, 2, 1), ) - Question.objects.bulk_create(Question(question='q', posted=posted) for posted in posted_dates) + Question.objects.bulk_create( + Question(question="q", posted=posted) for posted in posted_dates + ) tests = ( - ({}, [['year=2017'], ['year=2018']]), - ({'year': 2016}, []), - ({'year': 2017}, [['month=10', 'year=2017'], ['month=12', 'year=2017']]), - ({'year': 2017, 'month': 9}, []), - ({'year': 2017, 'month': 12}, [['day=15', 'month=12', 'year=2017'], ['day=31', 'month=12', 'year=2017']]), + ({}, [["year=2017"], ["year=2018"]]), + ({"year": 2016}, []), + ({"year": 2017}, [["month=10", "year=2017"], ["month=12", "year=2017"]]), + ({"year": 2017, "month": 9}, []), + ( + {"year": 2017, "month": 12}, + [ + ["day=15", "month=12", "year=2017"], + ["day=31", "month=12", "year=2017"], + ], + ), ) for query, expected_choices in tests: with self.subTest(query=query): - query = {'posted__%s' % q: val for q, val in query.items()} - request = self.factory.get('/', query) + query = {"posted__%s" % q: val for q, val in query.items()} + request = self.factory.get("/", query) request.user = self.superuser changelist = modeladmin.get_changelist_instance(request) spec = date_hierarchy(changelist) - choices = [choice['link'] for choice in spec['choices']] + choices = [choice["link"] for choice in spec["choices"]] expected_choices = [ - '&'.join('posted__%s' % c for c in choice) for choice in expected_choices + "&".join("posted__%s" % c for c in choice) + for choice in expected_choices + ] + expected_choices = [ + ("?" + choice) if choice else "" for choice in expected_choices ] - expected_choices = [('?' + choice) if choice else '' for choice in expected_choices] self.assertEqual(choices, expected_choices) def test_choice_links_datetime(self): modeladmin = ModelAdmin(Question, site) - modeladmin.date_hierarchy = 'expires' - Question.objects.bulk_create([ - Question(question='q1', expires=datetime.datetime(2017, 10, 1)), - Question(question='q2', expires=datetime.datetime(2017, 10, 1)), - Question(question='q3', expires=datetime.datetime(2017, 12, 15)), - Question(question='q4', expires=datetime.datetime(2017, 12, 15)), - Question(question='q5', expires=datetime.datetime(2017, 12, 31)), - Question(question='q6', expires=datetime.datetime(2018, 2, 1)), - ]) + modeladmin.date_hierarchy = "expires" + Question.objects.bulk_create( + [ + Question(question="q1", expires=datetime.datetime(2017, 10, 1)), + Question(question="q2", expires=datetime.datetime(2017, 10, 1)), + Question(question="q3", expires=datetime.datetime(2017, 12, 15)), + Question(question="q4", expires=datetime.datetime(2017, 12, 15)), + Question(question="q5", expires=datetime.datetime(2017, 12, 31)), + Question(question="q6", expires=datetime.datetime(2018, 2, 1)), + ] + ) tests = [ - ({}, [['year=2017'], ['year=2018']]), - ({'year': 2016}, []), + ({}, [["year=2017"], ["year=2018"]]), + ({"year": 2016}, []), ( - {'year': 2017}, [ - ['month=10', 'year=2017'], - ['month=12', 'year=2017'], + {"year": 2017}, + [ + ["month=10", "year=2017"], + ["month=12", "year=2017"], ], ), - ({'year': 2017, 'month': 9}, []), + ({"year": 2017, "month": 9}, []), ( - {'year': 2017, 'month': 12}, [ - ['day=15', 'month=12', 'year=2017'], - ['day=31', 'month=12', 'year=2017'], + {"year": 2017, "month": 12}, + [ + ["day=15", "month=12", "year=2017"], + ["day=31", "month=12", "year=2017"], ], ), ] for query, expected_choices in tests: with self.subTest(query=query): - query = {'expires__%s' % q: val for q, val in query.items()} - request = self.factory.get('/', query) + query = {"expires__%s" % q: val for q, val in query.items()} + request = self.factory.get("/", query) request.user = self.superuser changelist = modeladmin.get_changelist_instance(request) spec = date_hierarchy(changelist) - choices = [choice['link'] for choice in spec['choices']] + choices = [choice["link"] for choice in spec["choices"]] expected_choices = [ - '?' + '&'.join('expires__%s' % c for c in choice) + "?" + "&".join("expires__%s" % c for c in choice) for choice in expected_choices ] self.assertEqual(choices, expected_choices) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 6d73d56625..4935b58823 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -33,7 +33,10 @@ from django.core.files import temp as tempfile from django.forms.utils import ErrorList from django.template.response import TemplateResponse from django.test import ( - TestCase, modify_settings, override_settings, skipUnlessDBFeature, + TestCase, + modify_settings, + override_settings, + skipUnlessDBFeature, ) from django.test.utils import override_script_prefix from django.urls import NoReverseMatch, resolve, reverse @@ -46,23 +49,101 @@ from django.utils.http import urlencode from . import customadmin from .admin import CityAdmin, site, site2 from .models import ( - Actor, AdminOrderedAdminMethod, AdminOrderedCallable, AdminOrderedField, - AdminOrderedModelMethod, Album, Answer, Answer2, Article, BarAccount, Book, - Bookmark, Box, Category, Chapter, ChapterXtra1, ChapterXtra2, Character, - Child, Choice, City, Collector, Color, ComplexSortedPerson, CoverLetter, - CustomArticle, CyclicOne, CyclicTwo, DooHickey, Employee, EmptyModel, - Fabric, FancyDoodad, FieldOverridePost, FilteredManager, FooAccount, - FoodDelivery, FunkyTag, Gallery, Grommet, Inquisition, Language, Link, - MainPrepopulated, Media, ModelWithStringPrimaryKey, OtherStory, Paper, - Parent, ParentWithDependentChildren, ParentWithUUIDPK, Person, Persona, - Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, Podcast, Post, - PrePopulatedPost, Promo, Question, ReadablePizza, ReadOnlyPizza, - ReadOnlyRelatedField, Recommendation, Recommender, RelatedPrepopulated, - RelatedWithUUIDPKModel, Report, Restaurant, RowLevelChangePermissionModel, - SecretHideout, Section, ShortMessage, Simple, Song, State, Story, - SuperSecretHideout, SuperVillain, Telegram, TitleTranslation, Topping, - UnchangeableObject, UndeletableObject, UnorderedObject, UserProxy, Villain, - Vodcast, Whatsit, Widget, Worker, WorkHour, + Actor, + AdminOrderedAdminMethod, + AdminOrderedCallable, + AdminOrderedField, + AdminOrderedModelMethod, + Album, + Answer, + Answer2, + Article, + BarAccount, + Book, + Bookmark, + Box, + Category, + Chapter, + ChapterXtra1, + ChapterXtra2, + Character, + Child, + Choice, + City, + Collector, + Color, + ComplexSortedPerson, + CoverLetter, + CustomArticle, + CyclicOne, + CyclicTwo, + DooHickey, + Employee, + EmptyModel, + Fabric, + FancyDoodad, + FieldOverridePost, + FilteredManager, + FooAccount, + FoodDelivery, + FunkyTag, + Gallery, + Grommet, + Inquisition, + Language, + Link, + MainPrepopulated, + Media, + ModelWithStringPrimaryKey, + OtherStory, + Paper, + Parent, + ParentWithDependentChildren, + ParentWithUUIDPK, + Person, + Persona, + Picture, + Pizza, + Plot, + PlotDetails, + PluggableSearchPerson, + Podcast, + Post, + PrePopulatedPost, + Promo, + Question, + ReadablePizza, + ReadOnlyPizza, + ReadOnlyRelatedField, + Recommendation, + Recommender, + RelatedPrepopulated, + RelatedWithUUIDPKModel, + Report, + Restaurant, + RowLevelChangePermissionModel, + SecretHideout, + Section, + ShortMessage, + Simple, + Song, + State, + Story, + SuperSecretHideout, + SuperVillain, + Telegram, + TitleTranslation, + Topping, + UnchangeableObject, + UndeletableObject, + UnorderedObject, + UserProxy, + Villain, + Vodcast, + Whatsit, + Widget, + Worker, + WorkHour, ) ERROR_MESSAGE = "Please enter the correct username and password \ @@ -83,12 +164,13 @@ class AdminFieldExtractionMixin: """ Helper methods for extracting data from AdminForm. """ + def get_admin_form_fields(self, response): """ Return a list of AdminFields for the AdminForm in the response. """ fields = [] - for fieldset in response.context['adminform']: + for fieldset in response.context["adminform"]: for field_line in fieldset: fields.extend(field_line) return fields @@ -105,51 +187,64 @@ class AdminFieldExtractionMixin: """ admin_readonly_fields = self.get_admin_readonly_fields(response) for field in admin_readonly_fields: - if field.field['name'] == field_name: + if field.field["name"] == field_name: return field -@override_settings(ROOT_URLCONF='admin_views.urls', USE_I18N=True, LANGUAGE_CODE='en') +@override_settings(ROOT_URLCONF="admin_views.urls", USE_I18N=True, LANGUAGE_CODE="en") class AdminViewBasicTestCase(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', + content="<p>Middle content</p>", date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1, - title='Article 1', + title="Article 1", ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', + content="<p>Oldest content</p>", date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1, - title='Article 2', + title="Article 2", ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 - ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') - cls.color1 = Color.objects.create(value='Red', warm=True) - cls.color2 = Color.objects.create(value='Orange', warm=True) - cls.color3 = Color.objects.create(value='Blue', warm=False) - cls.color4 = Color.objects.create(value='Green', warm=False) - cls.fab1 = Fabric.objects.create(surface='x') - cls.fab2 = Fabric.objects.create(surface='y') - cls.fab3 = Fabric.objects.create(surface='plain') - cls.b1 = Book.objects.create(name='Book 1') - cls.b2 = Book.objects.create(name='Book 2') - cls.pro1 = Promo.objects.create(name='Promo 1', book=cls.b1) - cls.pro1 = Promo.objects.create(name='Promo 2', book=cls.b2) - cls.chap1 = Chapter.objects.create(title='Chapter 1', content='[ insert contents here ]', book=cls.b1) - cls.chap2 = Chapter.objects.create(title='Chapter 2', content='[ insert contents here ]', book=cls.b1) - cls.chap3 = Chapter.objects.create(title='Chapter 1', content='[ insert contents here ]', book=cls.b2) - cls.chap4 = Chapter.objects.create(title='Chapter 2', content='[ insert contents here ]', book=cls.b2) - cls.cx1 = ChapterXtra1.objects.create(chap=cls.chap1, xtra='ChapterXtra1 1') - cls.cx2 = ChapterXtra1.objects.create(chap=cls.chap3, xtra='ChapterXtra1 2') - Actor.objects.create(name='Palin', age=27) + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" + ) + cls.color1 = Color.objects.create(value="Red", warm=True) + cls.color2 = Color.objects.create(value="Orange", warm=True) + cls.color3 = Color.objects.create(value="Blue", warm=False) + cls.color4 = Color.objects.create(value="Green", warm=False) + cls.fab1 = Fabric.objects.create(surface="x") + cls.fab2 = Fabric.objects.create(surface="y") + cls.fab3 = Fabric.objects.create(surface="plain") + cls.b1 = Book.objects.create(name="Book 1") + cls.b2 = Book.objects.create(name="Book 2") + cls.pro1 = Promo.objects.create(name="Promo 1", book=cls.b1) + cls.pro1 = Promo.objects.create(name="Promo 2", book=cls.b2) + cls.chap1 = Chapter.objects.create( + title="Chapter 1", content="[ insert contents here ]", book=cls.b1 + ) + cls.chap2 = Chapter.objects.create( + title="Chapter 2", content="[ insert contents here ]", book=cls.b1 + ) + cls.chap3 = Chapter.objects.create( + title="Chapter 1", content="[ insert contents here ]", book=cls.b2 + ) + cls.chap4 = Chapter.objects.create( + title="Chapter 2", content="[ insert contents here ]", book=cls.b2 + ) + cls.cx1 = ChapterXtra1.objects.create(chap=cls.chap1, xtra="ChapterXtra1 1") + cls.cx2 = ChapterXtra1.objects.create(chap=cls.chap3, xtra="ChapterXtra1 2") + Actor.objects.create(name="Palin", age=27) # Post data for edit inline cls.inline_post_data = { @@ -204,7 +299,9 @@ class AdminViewBasicTestCase(TestCase): self.assertLess( response.content.index(text1.encode()), response.content.index(text2.encode()), - (failing_msg or '') + '\nResponse:\n' + response.content.decode(response.charset) + (failing_msg or "") + + "\nResponse:\n" + + response.content.decode(response.charset), ) @@ -213,7 +310,7 @@ class AdminViewBasicTest(AdminViewBasicTestCase): """ If you leave off the trailing slash, app should redirect and add it. """ - add_url = reverse('admin:admin_views_article_add') + add_url = reverse("admin:admin_views_article_add") response = self.client.get(add_url[:-1]) self.assertRedirects(response, add_url, status_code=301) @@ -221,22 +318,27 @@ class AdminViewBasicTest(AdminViewBasicTestCase): """ A smoke test to ensure GET on the add_view works. """ - response = self.client.get(reverse('admin:admin_views_section_add')) + response = self.client.get(reverse("admin:admin_views_section_add")) self.assertIsInstance(response, TemplateResponse) self.assertEqual(response.status_code, 200) def test_add_with_GET_args(self): - response = self.client.get(reverse('admin:admin_views_section_add'), {'name': 'My Section'}) + response = self.client.get( + reverse("admin:admin_views_section_add"), {"name": "My Section"} + ) self.assertContains( - response, 'value="My Section"', - msg_prefix="Couldn't find an input with the right value in the response" + response, + 'value="My Section"', + msg_prefix="Couldn't find an input with the right value in the response", ) def test_basic_edit_GET(self): """ A smoke test to ensure GET on the change_view works. """ - response = self.client.get(reverse('admin:admin_views_section_change', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)) + ) self.assertIsInstance(response, TemplateResponse) self.assertEqual(response.status_code, 200) @@ -246,11 +348,14 @@ class AdminViewBasicTest(AdminViewBasicTestCase): model with an integer PK field) redirects to the index page with a message saying the object doesn't exist. """ - response = self.client.get(reverse('admin:admin_views_section_change', args=(quote("abc/<b>"),)), follow=True) - self.assertRedirects(response, reverse('admin:index')) + response = self.client.get( + reverse("admin:admin_views_section_change", args=(quote("abc/<b>"),)), + follow=True, + ) + self.assertRedirects(response, reverse("admin:index")) self.assertEqual( - [m.message for m in response.context['messages']], - ['section with ID “abc/<b>” doesn’t exist. Perhaps it was deleted?'] + [m.message for m in response.context["messages"]], + ["section with ID “abc/<b>” doesn’t exist. Perhaps it was deleted?"], ) def test_basic_edit_GET_old_url_redirect(self): @@ -258,20 +363,26 @@ class AdminViewBasicTest(AdminViewBasicTestCase): The change URL changed in Django 1.9, but the old one still redirects. """ response = self.client.get( - reverse('admin:admin_views_section_change', args=(self.s1.pk,)).replace('change/', '') + reverse("admin:admin_views_section_change", args=(self.s1.pk,)).replace( + "change/", "" + ) + ) + self.assertRedirects( + response, reverse("admin:admin_views_section_change", args=(self.s1.pk,)) ) - self.assertRedirects(response, reverse('admin:admin_views_section_change', args=(self.s1.pk,))) def test_basic_inheritance_GET_string_PK(self): """ GET on the change_view (for inherited models) redirects to the index page with a message saying the object doesn't exist. """ - response = self.client.get(reverse('admin:admin_views_supervillain_change', args=('abc',)), follow=True) - self.assertRedirects(response, reverse('admin:index')) + response = self.client.get( + reverse("admin:admin_views_supervillain_change", args=("abc",)), follow=True + ) + self.assertRedirects(response, reverse("admin:index")) self.assertEqual( - [m.message for m in response.context['messages']], - ['super villain with ID “abc” doesn’t exist. Perhaps it was deleted?'] + [m.message for m in response.context["messages"]], + ["super villain with ID “abc” doesn’t exist. Perhaps it was deleted?"], ) def test_basic_add_POST(self): @@ -285,26 +396,26 @@ class AdminViewBasicTest(AdminViewBasicTestCase): "article_set-INITIAL_FORMS": "0", "article_set-MAX_NUM_FORMS": "0", } - response = self.client.post(reverse('admin:admin_views_section_add'), post_data) + response = self.client.post(reverse("admin:admin_views_section_add"), post_data) self.assertEqual(response.status_code, 302) # redirect somewhere def test_popup_add_POST(self): """HTTP response from a popup is properly escaped.""" post_data = { - IS_POPUP_VAR: '1', - 'title': 'title with a new\nline', - 'content': 'some content', - 'date_0': '2010-09-10', - 'date_1': '14:55:39', + IS_POPUP_VAR: "1", + "title": "title with a new\nline", + "content": "some content", + "date_0": "2010-09-10", + "date_1": "14:55:39", } - response = self.client.post(reverse('admin:admin_views_article_add'), post_data) - self.assertContains(response, 'title with a new\\nline') + response = self.client.post(reverse("admin:admin_views_article_add"), post_data) + self.assertContains(response, "title with a new\\nline") def test_basic_edit_POST(self): """ A smoke test to ensure POST on edit_view works. """ - url = reverse('admin:admin_views_section_change', args=(self.s1.pk,)) + url = reverse("admin:admin_views_section_change", args=(self.s1.pk,)) response = self.client.post(url, self.inline_post_data) self.assertEqual(response.status_code, 302) # redirect somewhere @@ -313,15 +424,19 @@ class AdminViewBasicTest(AdminViewBasicTestCase): Test "save as". """ post_data = self.inline_post_data.copy() - post_data.update({ - '_saveasnew': 'Save+as+new', - "article_set-1-section": "1", - "article_set-2-section": "1", - "article_set-3-section": "1", - "article_set-4-section": "1", - "article_set-5-section": "1", - }) - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), post_data) + post_data.update( + { + "_saveasnew": "Save+as+new", + "article_set-1-section": "1", + "article_set-2-section": "1", + "article_set-3-section": "1", + "article_set-4-section": "1", + "article_set-5-section": "1", + } + ) + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), post_data + ) self.assertEqual(response.status_code, 302) # redirect somewhere def test_edit_save_as_delete_inline(self): @@ -329,40 +444,50 @@ class AdminViewBasicTest(AdminViewBasicTestCase): Should be able to "Save as new" while also deleting an inline. """ post_data = self.inline_post_data.copy() - post_data.update({ - '_saveasnew': 'Save+as+new', - "article_set-1-section": "1", - "article_set-2-section": "1", - "article_set-2-DELETE": "1", - "article_set-3-section": "1", - }) - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), post_data) + post_data.update( + { + "_saveasnew": "Save+as+new", + "article_set-1-section": "1", + "article_set-2-section": "1", + "article_set-2-DELETE": "1", + "article_set-3-section": "1", + } + ) + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), post_data + ) self.assertEqual(response.status_code, 302) # started with 3 articles, one was deleted. - self.assertEqual(Section.objects.latest('id').article_set.count(), 2) + self.assertEqual(Section.objects.latest("id").article_set.count(), 2) def test_change_list_column_field_classes(self): - response = self.client.get(reverse('admin:admin_views_article_changelist')) + response = self.client.get(reverse("admin:admin_views_article_changelist")) # callables display the callable name. - self.assertContains(response, 'column-callable_year') - self.assertContains(response, 'field-callable_year') + self.assertContains(response, "column-callable_year") + self.assertContains(response, "field-callable_year") # lambdas display as "lambda" + index that they appear in list_display. - self.assertContains(response, 'column-lambda8') - self.assertContains(response, 'field-lambda8') + self.assertContains(response, "column-lambda8") + self.assertContains(response, "field-lambda8") def test_change_list_sorting_callable(self): """ Ensure we can sort on a list_display field that is a callable (column 2 is callable_year in ArticleAdmin) """ - response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': 2}) + response = self.client.get( + reverse("admin:admin_views_article_changelist"), {"o": 2} + ) self.assertContentBefore( - response, 'Oldest content', 'Middle content', - "Results of sorting on callable are out of order." + response, + "Oldest content", + "Middle content", + "Results of sorting on callable are out of order.", ) self.assertContentBefore( - response, 'Middle content', 'Newest content', - "Results of sorting on callable are out of order." + response, + "Middle content", + "Newest content", + "Results of sorting on callable are out of order.", ) def test_change_list_sorting_property(self): @@ -370,61 +495,71 @@ class AdminViewBasicTest(AdminViewBasicTestCase): Sort on a list_display field that is a property (column 10 is a property in Article model). """ - response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': 10}) + response = self.client.get( + reverse("admin:admin_views_article_changelist"), {"o": 10} + ) self.assertContentBefore( response, - 'Oldest content', - 'Middle content', - 'Results of sorting on property are out of order.', + "Oldest content", + "Middle content", + "Results of sorting on property are out of order.", ) self.assertContentBefore( response, - 'Middle content', - 'Newest content', - 'Results of sorting on property are out of order.', + "Middle content", + "Newest content", + "Results of sorting on property are out of order.", ) def test_change_list_sorting_callable_query_expression(self): """Query expressions may be used for admin_order_field.""" tests = [ - ('order_by_expression', 9), - ('order_by_f_expression', 12), - ('order_by_orderby_expression', 13), + ("order_by_expression", 9), + ("order_by_f_expression", 12), + ("order_by_orderby_expression", 13), ] for admin_order_field, index in tests: with self.subTest(admin_order_field): response = self.client.get( - reverse('admin:admin_views_article_changelist'), - {'o': index}, + reverse("admin:admin_views_article_changelist"), + {"o": index}, ) self.assertContentBefore( - response, 'Oldest content', 'Middle content', - 'Results of sorting on callable are out of order.' + response, + "Oldest content", + "Middle content", + "Results of sorting on callable are out of order.", ) self.assertContentBefore( - response, 'Middle content', 'Newest content', - 'Results of sorting on callable are out of order.' + response, + "Middle content", + "Newest content", + "Results of sorting on callable are out of order.", ) def test_change_list_sorting_callable_query_expression_reverse(self): tests = [ - ('order_by_expression', -9), - ('order_by_f_expression', -12), - ('order_by_orderby_expression', -13), + ("order_by_expression", -9), + ("order_by_f_expression", -12), + ("order_by_orderby_expression", -13), ] for admin_order_field, index in tests: with self.subTest(admin_order_field): response = self.client.get( - reverse('admin:admin_views_article_changelist'), - {'o': index}, + reverse("admin:admin_views_article_changelist"), + {"o": index}, ) self.assertContentBefore( - response, 'Middle content', 'Oldest content', - 'Results of sorting on callable are out of order.' + response, + "Middle content", + "Oldest content", + "Results of sorting on callable are out of order.", ) self.assertContentBefore( - response, 'Newest content', 'Middle content', - 'Results of sorting on callable are out of order.' + response, + "Newest content", + "Middle content", + "Results of sorting on callable are out of order.", ) def test_change_list_sorting_model(self): @@ -432,14 +567,20 @@ class AdminViewBasicTest(AdminViewBasicTestCase): Ensure we can sort on a list_display field that is a Model method (column 3 is 'model_year' in ArticleAdmin) """ - response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': '-3'}) + response = self.client.get( + reverse("admin:admin_views_article_changelist"), {"o": "-3"} + ) self.assertContentBefore( - response, 'Newest content', 'Middle content', - "Results of sorting on Model method are out of order." + response, + "Newest content", + "Middle content", + "Results of sorting on Model method are out of order.", ) self.assertContentBefore( - response, 'Middle content', 'Oldest content', - "Results of sorting on Model method are out of order." + response, + "Middle content", + "Oldest content", + "Results of sorting on Model method are out of order.", ) def test_change_list_sorting_model_admin(self): @@ -447,14 +588,20 @@ class AdminViewBasicTest(AdminViewBasicTestCase): Ensure we can sort on a list_display field that is a ModelAdmin method (column 4 is 'modeladmin_year' in ArticleAdmin) """ - response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': '4'}) + response = self.client.get( + reverse("admin:admin_views_article_changelist"), {"o": "4"} + ) self.assertContentBefore( - response, 'Oldest content', 'Middle content', - "Results of sorting on ModelAdmin method are out of order." + response, + "Oldest content", + "Middle content", + "Results of sorting on ModelAdmin method are out of order.", ) self.assertContentBefore( - response, 'Middle content', 'Newest content', - "Results of sorting on ModelAdmin method are out of order." + response, + "Middle content", + "Newest content", + "Results of sorting on ModelAdmin method are out of order.", ) def test_change_list_sorting_model_admin_reverse(self): @@ -465,42 +612,58 @@ class AdminViewBasicTest(AdminViewBasicTestCase): """ td = '<td class="field-model_property_year">%s</td>' td_2000, td_2008, td_2009 = td % 2000, td % 2008, td % 2009 - response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': '6'}) + response = self.client.get( + reverse("admin:admin_views_article_changelist"), {"o": "6"} + ) self.assertContentBefore( - response, td_2009, td_2008, - "Results of sorting on ModelAdmin method are out of order." + response, + td_2009, + td_2008, + "Results of sorting on ModelAdmin method are out of order.", ) self.assertContentBefore( - response, td_2008, td_2000, - "Results of sorting on ModelAdmin method are out of order." + response, + td_2008, + td_2000, + "Results of sorting on ModelAdmin method are out of order.", ) # Let's make sure the ordering is right and that we don't get a # FieldError when we change to descending order - response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': '-6'}) + response = self.client.get( + reverse("admin:admin_views_article_changelist"), {"o": "-6"} + ) self.assertContentBefore( - response, td_2000, td_2008, - "Results of sorting on ModelAdmin method are out of order." + response, + td_2000, + td_2008, + "Results of sorting on ModelAdmin method are out of order.", ) self.assertContentBefore( - response, td_2008, td_2009, - "Results of sorting on ModelAdmin method are out of order." + response, + td_2008, + td_2009, + "Results of sorting on ModelAdmin method are out of order.", ) def test_change_list_sorting_multiple(self): p1 = Person.objects.create(name="Chris", gender=1, alive=True) p2 = Person.objects.create(name="Chris", gender=2, alive=True) p3 = Person.objects.create(name="Bob", gender=1, alive=True) - link1 = reverse('admin:admin_views_person_change', args=(p1.pk,)) - link2 = reverse('admin:admin_views_person_change', args=(p2.pk,)) - link3 = reverse('admin:admin_views_person_change', args=(p3.pk,)) + link1 = reverse("admin:admin_views_person_change", args=(p1.pk,)) + link2 = reverse("admin:admin_views_person_change", args=(p2.pk,)) + link3 = reverse("admin:admin_views_person_change", args=(p3.pk,)) # Sort by name, gender - response = self.client.get(reverse('admin:admin_views_person_changelist'), {'o': '1.2'}) + response = self.client.get( + reverse("admin:admin_views_person_changelist"), {"o": "1.2"} + ) self.assertContentBefore(response, link3, link1) self.assertContentBefore(response, link1, link2) # Sort by gender descending, name - response = self.client.get(reverse('admin:admin_views_person_changelist'), {'o': '-2.1'}) + response = self.client.get( + reverse("admin:admin_views_person_changelist"), {"o": "-2.1"} + ) self.assertContentBefore(response, link2, link3) self.assertContentBefore(response, link3, link1) @@ -514,27 +677,29 @@ class AdminViewBasicTest(AdminViewBasicTestCase): p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80) p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70) p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60) - link1 = reverse('admin:admin_views_person_change', args=(p1.pk,)) - link2 = reverse('admin:admin_views_person_change', args=(p2.pk,)) - link3 = reverse('admin:admin_views_person_change', args=(p3.pk,)) + link1 = reverse("admin:admin_views_person_change", args=(p1.pk,)) + link2 = reverse("admin:admin_views_person_change", args=(p2.pk,)) + link3 = reverse("admin:admin_views_person_change", args=(p3.pk,)) - response = self.client.get(reverse('admin:admin_views_person_changelist'), {}) + response = self.client.get(reverse("admin:admin_views_person_changelist"), {}) self.assertContentBefore(response, link3, link2) self.assertContentBefore(response, link2, link1) def test_change_list_sorting_model_meta(self): # Test ordering on Model Meta is respected - l1 = Language.objects.create(iso='ur', name='Urdu') - l2 = Language.objects.create(iso='ar', name='Arabic') - link1 = reverse('admin:admin_views_language_change', args=(quote(l1.pk),)) - link2 = reverse('admin:admin_views_language_change', args=(quote(l2.pk),)) + l1 = Language.objects.create(iso="ur", name="Urdu") + l2 = Language.objects.create(iso="ar", name="Arabic") + link1 = reverse("admin:admin_views_language_change", args=(quote(l1.pk),)) + link2 = reverse("admin:admin_views_language_change", args=(quote(l2.pk),)) - response = self.client.get(reverse('admin:admin_views_language_changelist'), {}) + response = self.client.get(reverse("admin:admin_views_language_changelist"), {}) self.assertContentBefore(response, link2, link1) # Test we can override with query string - response = self.client.get(reverse('admin:admin_views_language_changelist'), {'o': '-1'}) + response = self.client.get( + reverse("admin:admin_views_language_changelist"), {"o": "-1"} + ) self.assertContentBefore(response, link1, link2) def test_change_list_sorting_override_model_admin(self): @@ -542,10 +707,10 @@ class AdminViewBasicTest(AdminViewBasicTestCase): dt = datetime.datetime.now() p1 = Podcast.objects.create(name="A", release_date=dt) p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10)) - link1 = reverse('admin:admin_views_podcast_change', args=(p1.pk,)) - link2 = reverse('admin:admin_views_podcast_change', args=(p2.pk,)) + link1 = reverse("admin:admin_views_podcast_change", args=(p1.pk,)) + link2 = reverse("admin:admin_views_podcast_change", args=(p2.pk,)) - response = self.client.get(reverse('admin:admin_views_podcast_changelist'), {}) + response = self.client.get(reverse("admin:admin_views_podcast_changelist"), {}) self.assertContentBefore(response, link1, link2) def test_multiple_sort_same_field(self): @@ -554,26 +719,28 @@ class AdminViewBasicTest(AdminViewBasicTestCase): dt = datetime.datetime.now() p1 = Podcast.objects.create(name="A", release_date=dt) p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10)) - link1 = reverse('admin:admin_views_podcast_change', args=(quote(p1.pk),)) - link2 = reverse('admin:admin_views_podcast_change', args=(quote(p2.pk),)) + link1 = reverse("admin:admin_views_podcast_change", args=(quote(p1.pk),)) + link2 = reverse("admin:admin_views_podcast_change", args=(quote(p2.pk),)) - response = self.client.get(reverse('admin:admin_views_podcast_changelist'), {}) + response = self.client.get(reverse("admin:admin_views_podcast_changelist"), {}) self.assertContentBefore(response, link1, link2) p1 = ComplexSortedPerson.objects.create(name="Bob", age=10) p2 = ComplexSortedPerson.objects.create(name="Amy", age=20) - link1 = reverse('admin:admin_views_complexsortedperson_change', args=(p1.pk,)) - link2 = reverse('admin:admin_views_complexsortedperson_change', args=(p2.pk,)) + link1 = reverse("admin:admin_views_complexsortedperson_change", args=(p1.pk,)) + link2 = reverse("admin:admin_views_complexsortedperson_change", args=(p2.pk,)) - response = self.client.get(reverse('admin:admin_views_complexsortedperson_changelist'), {}) + response = self.client.get( + reverse("admin:admin_views_complexsortedperson_changelist"), {} + ) # Should have 5 columns (including action checkbox col) self.assertContains(response, '<th scope="col"', count=5) - self.assertContains(response, 'Name') - self.assertContains(response, 'Colored name') + self.assertContains(response, "Name") + self.assertContains(response, "Colored name") # Check order - self.assertContentBefore(response, 'Name', 'Colored name') + self.assertContentBefore(response, "Name", "Colored name") # Check sorting - should be by name self.assertContentBefore(response, link2, link1) @@ -584,146 +751,170 @@ class AdminViewBasicTest(AdminViewBasicTestCase): fields: field names, method on the model admin and model itself, and other callables. See #17252. """ - models = [(AdminOrderedField, 'adminorderedfield'), - (AdminOrderedModelMethod, 'adminorderedmodelmethod'), - (AdminOrderedAdminMethod, 'adminorderedadminmethod'), - (AdminOrderedCallable, 'adminorderedcallable')] + models = [ + (AdminOrderedField, "adminorderedfield"), + (AdminOrderedModelMethod, "adminorderedmodelmethod"), + (AdminOrderedAdminMethod, "adminorderedadminmethod"), + (AdminOrderedCallable, "adminorderedcallable"), + ] for model, url in models: - model.objects.create(stuff='The Last Item', order=3) - model.objects.create(stuff='The First Item', order=1) - model.objects.create(stuff='The Middle Item', order=2) - response = self.client.get(reverse('admin:admin_views_%s_changelist' % url), {}) + model.objects.create(stuff="The Last Item", order=3) + model.objects.create(stuff="The First Item", order=1) + model.objects.create(stuff="The Middle Item", order=2) + response = self.client.get( + reverse("admin:admin_views_%s_changelist" % url), {} + ) # Should have 3 columns including action checkbox col. self.assertContains(response, '<th scope="col"', count=3, msg_prefix=url) # Check if the correct column was selected. 2 is the index of the # 'order' column in the model admin's 'list_display' with 0 being # the implicit 'action_checkbox' and 1 being the column 'stuff'. - self.assertEqual(response.context['cl'].get_ordering_field_columns(), {2: 'asc'}) + self.assertEqual( + response.context["cl"].get_ordering_field_columns(), {2: "asc"} + ) # Check order of records. - self.assertContentBefore(response, 'The First Item', 'The Middle Item') - self.assertContentBefore(response, 'The Middle Item', 'The Last Item') + self.assertContentBefore(response, "The First Item", "The Middle Item") + self.assertContentBefore(response, "The Middle Item", "The Last Item") def test_has_related_field_in_list_display_fk(self): """Joins shouldn't be performed for <FK>_id fields in list display.""" - state = State.objects.create(name='Karnataka') - City.objects.create(state=state, name='Bangalore') - response = self.client.get(reverse('admin:admin_views_city_changelist'), {}) + state = State.objects.create(name="Karnataka") + City.objects.create(state=state, name="Bangalore") + response = self.client.get(reverse("admin:admin_views_city_changelist"), {}) - response.context['cl'].list_display = ['id', 'name', 'state'] - self.assertIs(response.context['cl'].has_related_field_in_list_display(), True) + response.context["cl"].list_display = ["id", "name", "state"] + self.assertIs(response.context["cl"].has_related_field_in_list_display(), True) - response.context['cl'].list_display = ['id', 'name', 'state_id'] - self.assertIs(response.context['cl'].has_related_field_in_list_display(), False) + response.context["cl"].list_display = ["id", "name", "state_id"] + self.assertIs(response.context["cl"].has_related_field_in_list_display(), False) def test_has_related_field_in_list_display_o2o(self): """Joins shouldn't be performed for <O2O>_id fields in list display.""" - media = Media.objects.create(name='Foo') + media = Media.objects.create(name="Foo") Vodcast.objects.create(media=media) - response = self.client.get(reverse('admin:admin_views_vodcast_changelist'), {}) + response = self.client.get(reverse("admin:admin_views_vodcast_changelist"), {}) - response.context['cl'].list_display = ['media'] - self.assertIs(response.context['cl'].has_related_field_in_list_display(), True) + response.context["cl"].list_display = ["media"] + self.assertIs(response.context["cl"].has_related_field_in_list_display(), True) - response.context['cl'].list_display = ['media_id'] - self.assertIs(response.context['cl'].has_related_field_in_list_display(), False) + response.context["cl"].list_display = ["media_id"] + self.assertIs(response.context["cl"].has_related_field_in_list_display(), False) def test_limited_filter(self): """Ensure admin changelist filters do not contain objects excluded via limit_choices_to. This also tests relation-spanning filters (e.g. 'color__value'). """ - response = self.client.get(reverse('admin:admin_views_thing_changelist')) + response = self.client.get(reverse("admin:admin_views_thing_changelist")) self.assertContains( - response, '<div id="changelist-filter">', - msg_prefix="Expected filter not found in changelist view" + response, + '<div id="changelist-filter">', + msg_prefix="Expected filter not found in changelist view", ) self.assertNotContains( - response, '<a href="?color__id__exact=3">Blue</a>', - msg_prefix="Changelist filter not correctly limited by limit_choices_to" + response, + '<a href="?color__id__exact=3">Blue</a>', + msg_prefix="Changelist filter not correctly limited by limit_choices_to", ) def test_relation_spanning_filters(self): - changelist_url = reverse('admin:admin_views_chapterxtra1_changelist') + changelist_url = reverse("admin:admin_views_chapterxtra1_changelist") response = self.client.get(changelist_url) self.assertContains(response, '<div id="changelist-filter">') filters = { - 'chap__id__exact': { - 'values': [c.id for c in Chapter.objects.all()], - 'test': lambda obj, value: obj.chap.id == value, + "chap__id__exact": { + "values": [c.id for c in Chapter.objects.all()], + "test": lambda obj, value: obj.chap.id == value, }, - 'chap__title': { - 'values': [c.title for c in Chapter.objects.all()], - 'test': lambda obj, value: obj.chap.title == value, + "chap__title": { + "values": [c.title for c in Chapter.objects.all()], + "test": lambda obj, value: obj.chap.title == value, }, - 'chap__book__id__exact': { - 'values': [b.id for b in Book.objects.all()], - 'test': lambda obj, value: obj.chap.book.id == value, + "chap__book__id__exact": { + "values": [b.id for b in Book.objects.all()], + "test": lambda obj, value: obj.chap.book.id == value, }, - 'chap__book__name': { - 'values': [b.name for b in Book.objects.all()], - 'test': lambda obj, value: obj.chap.book.name == value, + "chap__book__name": { + "values": [b.name for b in Book.objects.all()], + "test": lambda obj, value: obj.chap.book.name == value, }, - 'chap__book__promo__id__exact': { - 'values': [p.id for p in Promo.objects.all()], - 'test': lambda obj, value: obj.chap.book.promo_set.filter(id=value).exists(), + "chap__book__promo__id__exact": { + "values": [p.id for p in Promo.objects.all()], + "test": lambda obj, value: obj.chap.book.promo_set.filter( + id=value + ).exists(), }, - 'chap__book__promo__name': { - 'values': [p.name for p in Promo.objects.all()], - 'test': lambda obj, value: obj.chap.book.promo_set.filter(name=value).exists(), + "chap__book__promo__name": { + "values": [p.name for p in Promo.objects.all()], + "test": lambda obj, value: obj.chap.book.promo_set.filter( + name=value + ).exists(), }, # A forward relation (book) after a reverse relation (promo). - 'guest_author__promo__book__id__exact': { - 'values': [p.id for p in Book.objects.all()], - 'test': lambda obj, value: obj.guest_author.promo_set.filter(book=value).exists(), + "guest_author__promo__book__id__exact": { + "values": [p.id for p in Book.objects.all()], + "test": lambda obj, value: obj.guest_author.promo_set.filter( + book=value + ).exists(), }, } for filter_path, params in filters.items(): - for value in params['values']: + for value in params["values"]: query_string = urlencode({filter_path: value}) # ensure filter link exists self.assertContains(response, '<a href="?%s"' % query_string) # ensure link works - filtered_response = self.client.get('%s?%s' % (changelist_url, query_string)) + filtered_response = self.client.get( + "%s?%s" % (changelist_url, query_string) + ) self.assertEqual(filtered_response.status_code, 200) # ensure changelist contains only valid objects - for obj in filtered_response.context['cl'].queryset.all(): - self.assertTrue(params['test'](obj, value)) + for obj in filtered_response.context["cl"].queryset.all(): + self.assertTrue(params["test"](obj, value)) def test_incorrect_lookup_parameters(self): """Ensure incorrect lookup parameters are handled gracefully.""" - changelist_url = reverse('admin:admin_views_thing_changelist') - response = self.client.get(changelist_url, {'notarealfield': '5'}) - self.assertRedirects(response, '%s?e=1' % changelist_url) + changelist_url = reverse("admin:admin_views_thing_changelist") + response = self.client.get(changelist_url, {"notarealfield": "5"}) + self.assertRedirects(response, "%s?e=1" % changelist_url) # Spanning relationships through a nonexistent related object (Refs #16716) - response = self.client.get(changelist_url, {'notarealfield__whatever': '5'}) - self.assertRedirects(response, '%s?e=1' % changelist_url) + response = self.client.get(changelist_url, {"notarealfield__whatever": "5"}) + self.assertRedirects(response, "%s?e=1" % changelist_url) - response = self.client.get(changelist_url, {'color__id__exact': 'StringNotInteger!'}) - self.assertRedirects(response, '%s?e=1' % changelist_url) + response = self.client.get( + changelist_url, {"color__id__exact": "StringNotInteger!"} + ) + self.assertRedirects(response, "%s?e=1" % changelist_url) # Regression test for #18530 - response = self.client.get(changelist_url, {'pub_date__gte': 'foo'}) - self.assertRedirects(response, '%s?e=1' % changelist_url) + response = self.client.get(changelist_url, {"pub_date__gte": "foo"}) + self.assertRedirects(response, "%s?e=1" % changelist_url) def test_isnull_lookups(self): """Ensure is_null is handled correctly.""" - Article.objects.create(title="I Could Go Anywhere", content="Versatile", date=datetime.datetime.now()) - changelist_url = reverse('admin:admin_views_article_changelist') + Article.objects.create( + title="I Could Go Anywhere", + content="Versatile", + date=datetime.datetime.now(), + ) + changelist_url = reverse("admin:admin_views_article_changelist") response = self.client.get(changelist_url) - self.assertContains(response, '4 articles') - response = self.client.get(changelist_url, {'section__isnull': 'false'}) - self.assertContains(response, '3 articles') - response = self.client.get(changelist_url, {'section__isnull': '0'}) - self.assertContains(response, '3 articles') - response = self.client.get(changelist_url, {'section__isnull': 'true'}) - self.assertContains(response, '1 article') - response = self.client.get(changelist_url, {'section__isnull': '1'}) - self.assertContains(response, '1 article') + self.assertContains(response, "4 articles") + response = self.client.get(changelist_url, {"section__isnull": "false"}) + self.assertContains(response, "3 articles") + response = self.client.get(changelist_url, {"section__isnull": "0"}) + self.assertContains(response, "3 articles") + response = self.client.get(changelist_url, {"section__isnull": "true"}) + self.assertContains(response, "1 article") + response = self.client.get(changelist_url, {"section__isnull": "1"}) + self.assertContains(response, "1 article") def test_logout_and_password_change_URLs(self): - response = self.client.get(reverse('admin:admin_views_article_changelist')) - self.assertContains(response, '<a href="%s">' % reverse('admin:logout')) - self.assertContains(response, '<a href="%s">' % reverse('admin:password_change')) + response = self.client.get(reverse("admin:admin_views_article_changelist")) + self.assertContains(response, '<a href="%s">' % reverse("admin:logout")) + self.assertContains( + response, '<a href="%s">' % reverse("admin:password_change") + ) def test_named_group_field_choices_change_list(self): """ @@ -731,49 +922,64 @@ class AdminViewBasicTest(AdminViewBasicTestCase): for rows corresponding to instances of a model in which a named group has been used in the choices option of a field. """ - link1 = reverse('admin:admin_views_fabric_change', args=(self.fab1.pk,)) - link2 = reverse('admin:admin_views_fabric_change', args=(self.fab2.pk,)) - response = self.client.get(reverse('admin:admin_views_fabric_changelist')) + link1 = reverse("admin:admin_views_fabric_change", args=(self.fab1.pk,)) + link2 = reverse("admin:admin_views_fabric_change", args=(self.fab2.pk,)) + response = self.client.get(reverse("admin:admin_views_fabric_changelist")) fail_msg = ( "Changelist table isn't showing the right human-readable values " "set by a model field 'choices' option named group." ) - self.assertContains(response, '<a href="%s">Horizontal</a>' % link1, msg_prefix=fail_msg, html=True) - self.assertContains(response, '<a href="%s">Vertical</a>' % link2, msg_prefix=fail_msg, html=True) + self.assertContains( + response, + '<a href="%s">Horizontal</a>' % link1, + msg_prefix=fail_msg, + html=True, + ) + self.assertContains( + response, + '<a href="%s">Vertical</a>' % link2, + msg_prefix=fail_msg, + html=True, + ) def test_named_group_field_choices_filter(self): """ Ensures the filter UI shows correctly when at least one named group has been used in the choices option of a model field. """ - response = self.client.get(reverse('admin:admin_views_fabric_changelist')) + response = self.client.get(reverse("admin:admin_views_fabric_changelist")) fail_msg = ( "Changelist filter isn't showing options contained inside a model " "field 'choices' option named group." ) self.assertContains(response, '<div id="changelist-filter">') self.assertContains( - response, '<a href="?surface__exact=x">Horizontal</a>', - msg_prefix=fail_msg, html=True + response, + '<a href="?surface__exact=x">Horizontal</a>', + msg_prefix=fail_msg, + html=True, ) self.assertContains( - response, '<a href="?surface__exact=y">Vertical</a>', - msg_prefix=fail_msg, html=True + response, + '<a href="?surface__exact=y">Vertical</a>', + msg_prefix=fail_msg, + html=True, ) def test_change_list_null_boolean_display(self): Post.objects.create(public=None) - response = self.client.get(reverse('admin:admin_views_post_changelist')) - self.assertContains(response, 'icon-unknown.svg') + 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.' + "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)') + @admin.display(boolean=True, empty_value="(Missing)") def is_published(self, obj): return obj.publish_date is not None @@ -783,21 +989,21 @@ class AdminViewBasicTest(AdminViewBasicTestCase): if the default language is non-English but the selected language is English. See #13388 and #3594 for more details. """ - with self.settings(LANGUAGE_CODE='fr'), translation.override('en-us'): - response = self.client.get(reverse('admin:jsi18n')) - self.assertNotContains(response, 'Choisir une heure') + with self.settings(LANGUAGE_CODE="fr"), translation.override("en-us"): + response = self.client.get(reverse("admin:jsi18n")) + self.assertNotContains(response, "Choisir une heure") def test_i18n_language_non_english_fallback(self): """ Makes sure that the fallback language is still working properly in cases where the selected language cannot be found. """ - with self.settings(LANGUAGE_CODE='fr'), translation.override('none'): - response = self.client.get(reverse('admin:jsi18n')) - self.assertContains(response, 'Choisir une heure') + with self.settings(LANGUAGE_CODE="fr"), translation.override("none"): + response = self.client.get(reverse("admin:jsi18n")) + self.assertContains(response, "Choisir une heure") def test_jsi18n_with_context(self): - response = self.client.get(reverse('admin-extra-context:jsi18n')) + response = self.client.get(reverse("admin-extra-context:jsi18n")) self.assertEqual(response.status_code, 200) def test_jsi18n_format_fallback(self): @@ -805,96 +1011,125 @@ class AdminViewBasicTest(AdminViewBasicTestCase): The JavaScript i18n view doesn't return localized date/time formats when the selected language cannot be found. """ - with self.settings(LANGUAGE_CODE='ru'), translation.override('none'): - response = self.client.get(reverse('admin:jsi18n')) - self.assertNotContains(response, '%d.%m.%Y %H:%M:%S') - self.assertContains(response, '%Y-%m-%d %H:%M:%S') + with self.settings(LANGUAGE_CODE="ru"), translation.override("none"): + response = self.client.get(reverse("admin:jsi18n")) + self.assertNotContains(response, "%d.%m.%Y %H:%M:%S") + self.assertContains(response, "%Y-%m-%d %H:%M:%S") def test_disallowed_filtering(self): - with self.assertLogs('django.security.DisallowedModelAdminLookup', 'ERROR'): + with self.assertLogs("django.security.DisallowedModelAdminLookup", "ERROR"): response = self.client.get( - "%s?owner__email__startswith=fuzzy" % reverse('admin:admin_views_album_changelist') + "%s?owner__email__startswith=fuzzy" + % reverse("admin:admin_views_album_changelist") ) self.assertEqual(response.status_code, 400) # Filters are allowed if explicitly included in list_filter - response = self.client.get("%s?color__value__startswith=red" % reverse('admin:admin_views_thing_changelist')) + response = self.client.get( + "%s?color__value__startswith=red" + % reverse("admin:admin_views_thing_changelist") + ) self.assertEqual(response.status_code, 200) - response = self.client.get("%s?color__value=red" % reverse('admin:admin_views_thing_changelist')) + response = self.client.get( + "%s?color__value=red" % reverse("admin:admin_views_thing_changelist") + ) self.assertEqual(response.status_code, 200) # Filters should be allowed if they involve a local field without the # need to allow them in list_filter or date_hierarchy. - response = self.client.get("%s?age__gt=30" % reverse('admin:admin_views_person_changelist')) + response = self.client.get( + "%s?age__gt=30" % reverse("admin:admin_views_person_changelist") + ) self.assertEqual(response.status_code, 200) - e1 = Employee.objects.create(name='Anonymous', gender=1, age=22, alive=True, code='123') - e2 = Employee.objects.create(name='Visitor', gender=2, age=19, alive=True, code='124') + e1 = Employee.objects.create( + name="Anonymous", gender=1, age=22, alive=True, code="123" + ) + e2 = Employee.objects.create( + name="Visitor", gender=2, age=19, alive=True, code="124" + ) WorkHour.objects.create(datum=datetime.datetime.now(), employee=e1) WorkHour.objects.create(datum=datetime.datetime.now(), employee=e2) - response = self.client.get(reverse('admin:admin_views_workhour_changelist')) - self.assertContains(response, 'employee__person_ptr__exact') - response = self.client.get("%s?employee__person_ptr__exact=%d" % ( - reverse('admin:admin_views_workhour_changelist'), e1.pk) + response = self.client.get(reverse("admin:admin_views_workhour_changelist")) + self.assertContains(response, "employee__person_ptr__exact") + response = self.client.get( + "%s?employee__person_ptr__exact=%d" + % (reverse("admin:admin_views_workhour_changelist"), e1.pk) ) self.assertEqual(response.status_code, 200) def test_disallowed_to_field(self): - url = reverse('admin:admin_views_section_changelist') - with self.assertLogs('django.security.DisallowedModelAdminToField', 'ERROR'): - response = self.client.get(url, {TO_FIELD_VAR: 'missing_field'}) + url = reverse("admin:admin_views_section_changelist") + with self.assertLogs("django.security.DisallowedModelAdminToField", "ERROR"): + response = self.client.get(url, {TO_FIELD_VAR: "missing_field"}) self.assertEqual(response.status_code, 400) # Specifying a field that is not referred by any other model registered # to this admin site should raise an exception. - with self.assertLogs('django.security.DisallowedModelAdminToField', 'ERROR'): - response = self.client.get(reverse('admin:admin_views_section_changelist'), {TO_FIELD_VAR: 'name'}) + with self.assertLogs("django.security.DisallowedModelAdminToField", "ERROR"): + response = self.client.get( + reverse("admin:admin_views_section_changelist"), {TO_FIELD_VAR: "name"} + ) self.assertEqual(response.status_code, 400) # #23839 - Primary key should always be allowed, even if the referenced model isn't registered. - response = self.client.get(reverse('admin:admin_views_notreferenced_changelist'), {TO_FIELD_VAR: 'id'}) + response = self.client.get( + reverse("admin:admin_views_notreferenced_changelist"), {TO_FIELD_VAR: "id"} + ) self.assertEqual(response.status_code, 200) # #23915 - Specifying a field referenced by another model though a m2m should be allowed. - response = self.client.get(reverse('admin:admin_views_recipe_changelist'), {TO_FIELD_VAR: 'rname'}) + response = self.client.get( + reverse("admin:admin_views_recipe_changelist"), {TO_FIELD_VAR: "rname"} + ) self.assertEqual(response.status_code, 200) # #23604, #23915 - Specifying a field referenced through a reverse m2m relationship should be allowed. - response = self.client.get(reverse('admin:admin_views_ingredient_changelist'), {TO_FIELD_VAR: 'iname'}) + response = self.client.get( + reverse("admin:admin_views_ingredient_changelist"), {TO_FIELD_VAR: "iname"} + ) self.assertEqual(response.status_code, 200) # #23329 - Specifying a field that is not referred by any other model directly registered # to this admin site but registered through inheritance should be allowed. - response = self.client.get(reverse('admin:admin_views_referencedbyparent_changelist'), {TO_FIELD_VAR: 'name'}) + response = self.client.get( + reverse("admin:admin_views_referencedbyparent_changelist"), + {TO_FIELD_VAR: "name"}, + ) self.assertEqual(response.status_code, 200) # #23431 - Specifying a field that is only referred to by a inline of a registered # model should be allowed. - response = self.client.get(reverse('admin:admin_views_referencedbyinline_changelist'), {TO_FIELD_VAR: 'name'}) + response = self.client.get( + reverse("admin:admin_views_referencedbyinline_changelist"), + {TO_FIELD_VAR: "name"}, + ) self.assertEqual(response.status_code, 200) # #25622 - Specifying a field of a model only referred by a generic # relation should raise DisallowedModelAdminToField. - url = reverse('admin:admin_views_referencedbygenrel_changelist') - with self.assertLogs('django.security.DisallowedModelAdminToField', 'ERROR'): - response = self.client.get(url, {TO_FIELD_VAR: 'object_id'}) + url = reverse("admin:admin_views_referencedbygenrel_changelist") + with self.assertLogs("django.security.DisallowedModelAdminToField", "ERROR"): + response = self.client.get(url, {TO_FIELD_VAR: "object_id"}) self.assertEqual(response.status_code, 400) # We also want to prevent the add, change, and delete views from # leaking a disallowed field value. - with self.assertLogs('django.security.DisallowedModelAdminToField', 'ERROR'): - response = self.client.post(reverse('admin:admin_views_section_add'), {TO_FIELD_VAR: 'name'}) + with self.assertLogs("django.security.DisallowedModelAdminToField", "ERROR"): + response = self.client.post( + reverse("admin:admin_views_section_add"), {TO_FIELD_VAR: "name"} + ) self.assertEqual(response.status_code, 400) section = Section.objects.create() - url = reverse('admin:admin_views_section_change', args=(section.pk,)) - with self.assertLogs('django.security.DisallowedModelAdminToField', 'ERROR'): - response = self.client.post(url, {TO_FIELD_VAR: 'name'}) + url = reverse("admin:admin_views_section_change", args=(section.pk,)) + with self.assertLogs("django.security.DisallowedModelAdminToField", "ERROR"): + response = self.client.post(url, {TO_FIELD_VAR: "name"}) self.assertEqual(response.status_code, 400) - url = reverse('admin:admin_views_section_delete', args=(section.pk,)) - with self.assertLogs('django.security.DisallowedModelAdminToField', 'ERROR'): - response = self.client.post(url, {TO_FIELD_VAR: 'name'}) + url = reverse("admin:admin_views_section_delete", args=(section.pk,)) + with self.assertLogs("django.security.DisallowedModelAdminToField", "ERROR"): + response = self.client.post(url, {TO_FIELD_VAR: "name"}) self.assertEqual(response.status_code, 400) def test_allowed_filtering_15103(self): @@ -904,7 +1139,9 @@ class AdminViewBasicTest(AdminViewBasicTestCase): can break. """ # Filters should be allowed if they are defined on a ForeignKey pointing to this model - url = "%s?leader__name=Palin&leader__age=27" % reverse('admin:admin_views_inquisition_changelist') + url = "%s?leader__name=Palin&leader__age=27" % reverse( + "admin:admin_views_inquisition_changelist" + ) response = self.client.get(url) self.assertEqual(response.status_code, 200) @@ -913,7 +1150,9 @@ class AdminViewBasicTest(AdminViewBasicTestCase): Regression test for ticket 20664 - ensure the pk is properly quoted. """ actor = Actor.objects.create(name="Palin", age=27) - response = self.client.get("%s?%s" % (reverse('admin:admin_views_actor_changelist'), IS_POPUP_VAR)) + response = self.client.get( + "%s?%s" % (reverse("admin:admin_views_actor_changelist"), IS_POPUP_VAR) + ) self.assertContains(response, 'data-popup-opener="%s"' % actor.pk) def test_hide_change_password(self): @@ -922,14 +1161,15 @@ class AdminViewBasicTest(AdminViewBasicTestCase): does not have a usable password set. (against 9bea85795705d015cdadc82c68b99196a8554f5c) """ - user = User.objects.get(username='super') + user = User.objects.get(username="super") user.set_unusable_password() user.save() self.client.force_login(user) - response = self.client.get(reverse('admin:index')) + response = self.client.get(reverse("admin:index")) self.assertNotContains( - response, reverse('admin:password_change'), - msg_prefix='The "change password" link should not be displayed if a user does not have a usable password.' + response, + reverse("admin:password_change"), + msg_prefix='The "change password" link should not be displayed if a user does not have a usable password.', ) def test_change_view_with_show_delete_extra_context(self): @@ -937,20 +1177,31 @@ class AdminViewBasicTest(AdminViewBasicTestCase): The 'show_delete' context variable in the admin's change view controls the display of the delete button. """ - instance = UndeletableObject.objects.create(name='foo') - response = self.client.get(reverse('admin:admin_views_undeletableobject_change', args=(instance.pk,))) - self.assertNotContains(response, 'deletelink') + instance = UndeletableObject.objects.create(name="foo") + response = self.client.get( + reverse("admin:admin_views_undeletableobject_change", args=(instance.pk,)) + ) + self.assertNotContains(response, "deletelink") def test_change_view_logs_m2m_field_changes(self): """Changes to ManyToManyFields are included in the object's history.""" - pizza = ReadablePizza.objects.create(name='Cheese') - cheese = Topping.objects.create(name='cheese') - post_data = {'name': pizza.name, 'toppings': [cheese.pk]} - response = self.client.post(reverse('admin:admin_views_readablepizza_change', args=(pizza.pk,)), post_data) - self.assertRedirects(response, reverse('admin:admin_views_readablepizza_changelist')) - pizza_ctype = ContentType.objects.get_for_model(ReadablePizza, for_concrete_model=False) - log = LogEntry.objects.filter(content_type=pizza_ctype, object_id=pizza.pk).first() - self.assertEqual(log.get_change_message(), 'Changed Toppings.') + pizza = ReadablePizza.objects.create(name="Cheese") + cheese = Topping.objects.create(name="cheese") + post_data = {"name": pizza.name, "toppings": [cheese.pk]} + response = self.client.post( + reverse("admin:admin_views_readablepizza_change", args=(pizza.pk,)), + post_data, + ) + self.assertRedirects( + response, reverse("admin:admin_views_readablepizza_changelist") + ) + pizza_ctype = ContentType.objects.get_for_model( + ReadablePizza, for_concrete_model=False + ) + log = LogEntry.objects.filter( + content_type=pizza_ctype, object_id=pizza.pk + ).first() + self.assertEqual(log.get_change_message(), "Changed Toppings.") def test_allows_attributeerror_to_bubble_up(self): """ @@ -960,7 +1211,7 @@ class AdminViewBasicTest(AdminViewBasicTestCase): """ Simple.objects.create() with self.assertRaises(AttributeError): - self.client.get(reverse('admin:admin_views_simple_changelist')) + self.client.get(reverse("admin:admin_views_simple_changelist")) def test_changelist_with_no_change_url(self): """ @@ -968,282 +1219,337 @@ class AdminViewBasicTest(AdminViewBasicTestCase): for change_view is removed from get_urls (#20934). """ o = UnchangeableObject.objects.create() - response = self.client.get(reverse('admin:admin_views_unchangeableobject_changelist')) + response = self.client.get( + reverse("admin:admin_views_unchangeableobject_changelist") + ) # Check the format of the shown object -- shouldn't contain a change link - self.assertContains(response, '<th class="field-__str__">%s</th>' % o, html=True) + self.assertContains( + response, '<th class="field-__str__">%s</th>' % o, html=True + ) def test_invalid_appindex_url(self): """ #21056 -- URL reversing shouldn't work for nonexistent apps. """ - good_url = '/test_admin/admin/admin_views/' - confirm_good_url = reverse('admin:app_list', - kwargs={'app_label': 'admin_views'}) + good_url = "/test_admin/admin/admin_views/" + confirm_good_url = reverse( + "admin:app_list", kwargs={"app_label": "admin_views"} + ) self.assertEqual(good_url, confirm_good_url) with self.assertRaises(NoReverseMatch): - reverse('admin:app_list', kwargs={'app_label': 'this_should_fail'}) + reverse("admin:app_list", kwargs={"app_label": "this_should_fail"}) with self.assertRaises(NoReverseMatch): - reverse('admin:app_list', args=('admin_views2',)) + reverse("admin:app_list", args=("admin_views2",)) def test_resolve_admin_views(self): - index_match = resolve('/test_admin/admin4/') - list_match = resolve('/test_admin/admin4/auth/user/') + index_match = resolve("/test_admin/admin4/") + list_match = resolve("/test_admin/admin4/auth/user/") self.assertIs(index_match.func.admin_site, customadmin.simple_site) - self.assertIsInstance(list_match.func.model_admin, customadmin.CustomPwdTemplateUserAdmin) + self.assertIsInstance( + list_match.func.model_admin, customadmin.CustomPwdTemplateUserAdmin + ) def test_adminsite_display_site_url(self): """ #13749 - Admin should display link to front-end site 'View site' """ - url = reverse('admin:index') + url = reverse("admin:index") response = self.client.get(url) - self.assertEqual(response.context['site_url'], '/my-site-url/') + self.assertEqual(response.context["site_url"], "/my-site-url/") self.assertContains(response, '<a href="/my-site-url/">View site</a>') def test_date_hierarchy_empty_queryset(self): self.assertIs(Question.objects.exists(), False) - response = self.client.get(reverse('admin:admin_views_answer2_changelist')) + response = self.client.get(reverse("admin:admin_views_answer2_changelist")) self.assertEqual(response.status_code, 200) - @override_settings(TIME_ZONE='America/Sao_Paulo', USE_TZ=True) + @override_settings(TIME_ZONE="America/Sao_Paulo", USE_TZ=True) def test_date_hierarchy_timezone_dst(self): # This datetime doesn't exist in this timezone due to DST. - for date in make_aware_datetimes(datetime.datetime(2016, 10, 16, 15), 'America/Sao_Paulo'): + for date in make_aware_datetimes( + datetime.datetime(2016, 10, 16, 15), "America/Sao_Paulo" + ): with self.subTest(repr(date.tzinfo)): - q = Question.objects.create(question='Why?', expires=date) - Answer2.objects.create(question=q, answer='Because.') - response = self.client.get(reverse('admin:admin_views_answer2_changelist')) - self.assertContains(response, 'question__expires__day=16') - self.assertContains(response, 'question__expires__month=10') - self.assertContains(response, 'question__expires__year=2016') - - @override_settings(TIME_ZONE='America/Los_Angeles', USE_TZ=True) + q = Question.objects.create(question="Why?", expires=date) + Answer2.objects.create(question=q, answer="Because.") + response = self.client.get( + reverse("admin:admin_views_answer2_changelist") + ) + self.assertContains(response, "question__expires__day=16") + self.assertContains(response, "question__expires__month=10") + self.assertContains(response, "question__expires__year=2016") + + @override_settings(TIME_ZONE="America/Los_Angeles", USE_TZ=True) def test_date_hierarchy_local_date_differ_from_utc(self): # This datetime is 2017-01-01 in UTC. - for date in make_aware_datetimes(datetime.datetime(2016, 12, 31, 16), 'America/Los_Angeles'): + for date in make_aware_datetimes( + datetime.datetime(2016, 12, 31, 16), "America/Los_Angeles" + ): with self.subTest(repr(date.tzinfo)): - q = Question.objects.create(question='Why?', expires=date) - Answer2.objects.create(question=q, answer='Because.') - response = self.client.get(reverse('admin:admin_views_answer2_changelist')) - self.assertContains(response, 'question__expires__day=31') - self.assertContains(response, 'question__expires__month=12') - self.assertContains(response, 'question__expires__year=2016') + q = Question.objects.create(question="Why?", expires=date) + Answer2.objects.create(question=q, answer="Because.") + response = self.client.get( + reverse("admin:admin_views_answer2_changelist") + ) + self.assertContains(response, "question__expires__day=31") + self.assertContains(response, "question__expires__month=12") + self.assertContains(response, "question__expires__year=2016") def test_sortable_by_columns_subset(self): - expected_sortable_fields = ('date', 'callable_year') + expected_sortable_fields = ("date", "callable_year") expected_not_sortable_fields = ( - 'content', 'model_year', 'modeladmin_year', 'model_year_reversed', - 'section', + "content", + "model_year", + "modeladmin_year", + "model_year_reversed", + "section", ) - response = self.client.get(reverse('admin6:admin_views_article_changelist')) + response = self.client.get(reverse("admin6:admin_views_article_changelist")) for field_name in expected_sortable_fields: - self.assertContains(response, '<th scope="col" class="sortable column-%s">' % field_name) + self.assertContains( + response, '<th scope="col" class="sortable column-%s">' % field_name + ) for field_name in expected_not_sortable_fields: - self.assertContains(response, '<th scope="col" class="column-%s">' % field_name) + self.assertContains( + response, '<th scope="col" class="column-%s">' % field_name + ) def test_get_sortable_by_columns_subset(self): - response = self.client.get(reverse('admin6:admin_views_actor_changelist')) + response = self.client.get(reverse("admin6:admin_views_actor_changelist")) self.assertContains(response, '<th scope="col" class="sortable column-age">') self.assertContains(response, '<th scope="col" class="column-name">') def test_sortable_by_no_column(self): - expected_not_sortable_fields = ('title', 'book') - response = self.client.get(reverse('admin6:admin_views_chapter_changelist')) + expected_not_sortable_fields = ("title", "book") + response = self.client.get(reverse("admin6:admin_views_chapter_changelist")) for field_name in expected_not_sortable_fields: - self.assertContains(response, '<th scope="col" class="column-%s">' % field_name) + self.assertContains( + response, '<th scope="col" class="column-%s">' % field_name + ) self.assertNotContains(response, '<th scope="col" class="sortable column') def test_get_sortable_by_no_column(self): - response = self.client.get(reverse('admin6:admin_views_color_changelist')) + response = self.client.get(reverse("admin6:admin_views_color_changelist")) self.assertContains(response, '<th scope="col" class="column-value">') self.assertNotContains(response, '<th scope="col" class="sortable column') def test_app_index_context(self): - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertContains( response, - '<title>Admin_Views administration | Django site admin</title>', + "<title>Admin_Views administration | Django site admin</title>", ) - self.assertEqual(response.context['title'], 'Admin_Views administration') - self.assertEqual(response.context['app_label'], 'admin_views') + self.assertEqual(response.context["title"], "Admin_Views administration") + self.assertEqual(response.context["app_label"], "admin_views") def test_change_view_subtitle_per_object(self): response = self.client.get( - reverse('admin:admin_views_article_change', args=(self.a1.pk,)), + reverse("admin:admin_views_article_change", args=(self.a1.pk,)), ) self.assertContains( response, - '<title>Article 1 | Change article | Django site admin</title>', + "<title>Article 1 | Change article | Django site admin</title>", ) - self.assertContains(response, '<h1>Change article</h1>') - self.assertContains(response, '<h2>Article 1</h2>') + self.assertContains(response, "<h1>Change article</h1>") + self.assertContains(response, "<h2>Article 1</h2>") response = self.client.get( - reverse('admin:admin_views_article_change', args=(self.a2.pk,)), + reverse("admin:admin_views_article_change", args=(self.a2.pk,)), ) self.assertContains( response, - '<title>Article 2 | Change article | Django site admin</title>', + "<title>Article 2 | Change article | Django site admin</title>", ) - self.assertContains(response, '<h1>Change article</h1>') - self.assertContains(response, '<h2>Article 2</h2>') + self.assertContains(response, "<h1>Change article</h1>") + self.assertContains(response, "<h2>Article 2</h2>") def test_view_subtitle_per_object(self): viewuser = User.objects.create_user( - username='viewuser', password='secret', is_staff=True, + username="viewuser", + password="secret", + is_staff=True, ) viewuser.user_permissions.add( - get_perm(Article, get_permission_codename('view', Article._meta)), + get_perm(Article, get_permission_codename("view", Article._meta)), ) self.client.force_login(viewuser) response = self.client.get( - reverse('admin:admin_views_article_change', args=(self.a1.pk,)), + reverse("admin:admin_views_article_change", args=(self.a1.pk,)), ) self.assertContains( response, - '<title>Article 1 | View article | Django site admin</title>', + "<title>Article 1 | View article | Django site admin</title>", ) - self.assertContains(response, '<h1>View article</h1>') - self.assertContains(response, '<h2>Article 1</h2>') + self.assertContains(response, "<h1>View article</h1>") + self.assertContains(response, "<h2>Article 1</h2>") response = self.client.get( - reverse('admin:admin_views_article_change', args=(self.a2.pk,)), + reverse("admin:admin_views_article_change", args=(self.a2.pk,)), ) self.assertContains( response, - '<title>Article 2 | View article | Django site admin</title>', + "<title>Article 2 | View article | Django site admin</title>", ) - self.assertContains(response, '<h1>View article</h1>') - self.assertContains(response, '<h2>Article 2</h2>') + self.assertContains(response, "<h1>View article</h1>") + self.assertContains(response, "<h2>Article 2</h2>") def test_formset_kwargs_can_be_overridden(self): - response = self.client.get(reverse('admin:admin_views_city_add')) - self.assertContains(response, 'overridden_name') + response = self.client.get(reverse("admin:admin_views_city_add")) + self.assertContains(response, "overridden_name") def test_render_views_no_subtitle(self): tests = [ - reverse('admin:index'), - reverse('admin:password_change'), - reverse('admin:app_list', args=('admin_views',)), - reverse('admin:admin_views_article_delete', args=(self.a1.pk,)), - reverse('admin:admin_views_article_history', args=(self.a1.pk,)), + reverse("admin:index"), + reverse("admin:password_change"), + reverse("admin:app_list", args=("admin_views",)), + reverse("admin:admin_views_article_delete", args=(self.a1.pk,)), + reverse("admin:admin_views_article_history", args=(self.a1.pk,)), # Login must be after logout. - reverse('admin:logout'), - reverse('admin:login'), + reverse("admin:logout"), + reverse("admin:login"), ] for url in tests: with self.subTest(url=url): - with self.assertNoLogs('django.template', 'DEBUG'): + with self.assertNoLogs("django.template", "DEBUG"): self.client.get(url) def test_render_delete_selected_confirmation_no_subtitle(self): post_data = { - 'action': 'delete_selected', - 'selected_across': '0', - 'index': '0', - '_selected_action': self.a1.pk, + "action": "delete_selected", + "selected_across": "0", + "index": "0", + "_selected_action": self.a1.pk, } - with self.assertNoLogs('django.template', 'DEBUG'): - self.client.post(reverse('admin:admin_views_article_changelist'), post_data) + with self.assertNoLogs("django.template", "DEBUG"): + self.client.post(reverse("admin:admin_views_article_changelist"), post_data) -@override_settings(TEMPLATES=[{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - # Put this app's and the shared tests templates dirs in DIRS to take precedence - # over the admin's templates dir. - 'DIRS': [ - os.path.join(os.path.dirname(__file__), 'templates'), - os.path.join(os.path.dirname(os.path.dirname(__file__)), 'templates'), - ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, -}]) +@override_settings( + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + # Put this app's and the shared tests templates dirs in DIRS to take precedence + # over the admin's templates dir. + "DIRS": [ + os.path.join(os.path.dirname(__file__), "templates"), + os.path.join(os.path.dirname(os.path.dirname(__file__)), "templates"), + ], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ] +) class AdminCustomTemplateTests(AdminViewBasicTestCase): def test_custom_model_admin_templates(self): # Test custom change list template with custom extra context - response = self.client.get(reverse('admin:admin_views_customarticle_changelist')) + response = self.client.get( + reverse("admin:admin_views_customarticle_changelist") + ) self.assertContains(response, "var hello = 'Hello!';") - self.assertTemplateUsed(response, 'custom_admin/change_list.html') + self.assertTemplateUsed(response, "custom_admin/change_list.html") # Test custom add form template - response = self.client.get(reverse('admin:admin_views_customarticle_add')) - self.assertTemplateUsed(response, 'custom_admin/add_form.html') + response = self.client.get(reverse("admin:admin_views_customarticle_add")) + self.assertTemplateUsed(response, "custom_admin/add_form.html") # Add an article so we can test delete, change, and history views - post = self.client.post(reverse('admin:admin_views_customarticle_add'), { - 'content': '<p>great article</p>', - 'date_0': '2008-03-18', - 'date_1': '10:54:39' - }) - self.assertRedirects(post, reverse('admin:admin_views_customarticle_changelist')) + post = self.client.post( + reverse("admin:admin_views_customarticle_add"), + { + "content": "<p>great article</p>", + "date_0": "2008-03-18", + "date_1": "10:54:39", + }, + ) + self.assertRedirects( + post, reverse("admin:admin_views_customarticle_changelist") + ) self.assertEqual(CustomArticle.objects.all().count(), 1) article_pk = CustomArticle.objects.all()[0].pk # Test custom delete, change, and object history templates # Test custom change form template - response = self.client.get(reverse('admin:admin_views_customarticle_change', args=(article_pk,))) - self.assertTemplateUsed(response, 'custom_admin/change_form.html') - response = self.client.get(reverse('admin:admin_views_customarticle_delete', args=(article_pk,))) - self.assertTemplateUsed(response, 'custom_admin/delete_confirmation.html') - response = self.client.post(reverse('admin:admin_views_customarticle_changelist'), data={ - 'index': 0, - 'action': ['delete_selected'], - '_selected_action': ['1'], - }) - self.assertTemplateUsed(response, 'custom_admin/delete_selected_confirmation.html') - response = self.client.get(reverse('admin:admin_views_customarticle_history', args=(article_pk,))) - self.assertTemplateUsed(response, 'custom_admin/object_history.html') + response = self.client.get( + reverse("admin:admin_views_customarticle_change", args=(article_pk,)) + ) + self.assertTemplateUsed(response, "custom_admin/change_form.html") + response = self.client.get( + reverse("admin:admin_views_customarticle_delete", args=(article_pk,)) + ) + self.assertTemplateUsed(response, "custom_admin/delete_confirmation.html") + response = self.client.post( + reverse("admin:admin_views_customarticle_changelist"), + data={ + "index": 0, + "action": ["delete_selected"], + "_selected_action": ["1"], + }, + ) + self.assertTemplateUsed( + response, "custom_admin/delete_selected_confirmation.html" + ) + response = self.client.get( + reverse("admin:admin_views_customarticle_history", args=(article_pk,)) + ) + self.assertTemplateUsed(response, "custom_admin/object_history.html") # A custom popup response template may be specified by # ModelAdmin.popup_response_template. - response = self.client.post(reverse('admin:admin_views_customarticle_add') + '?%s=1' % IS_POPUP_VAR, { - 'content': '<p>great article</p>', - 'date_0': '2008-03-18', - 'date_1': '10:54:39', - IS_POPUP_VAR: '1' - }) - self.assertEqual(response.template_name, 'custom_admin/popup_response.html') + response = self.client.post( + reverse("admin:admin_views_customarticle_add") + "?%s=1" % IS_POPUP_VAR, + { + "content": "<p>great article</p>", + "date_0": "2008-03-18", + "date_1": "10:54:39", + IS_POPUP_VAR: "1", + }, + ) + self.assertEqual(response.template_name, "custom_admin/popup_response.html") def test_extended_bodyclass_template_change_form(self): """ The admin/change_form.html template uses block.super in the bodyclass block. """ - response = self.client.get(reverse('admin:admin_views_section_add')) - self.assertContains(response, 'bodyclass_consistency_check ') + response = self.client.get(reverse("admin:admin_views_section_add")) + self.assertContains(response, "bodyclass_consistency_check ") def test_change_password_template(self): - user = User.objects.get(username='super') - response = self.client.get(reverse('admin:auth_user_password_change', args=(user.id,))) + user = User.objects.get(username="super") + response = self.client.get( + reverse("admin:auth_user_password_change", args=(user.id,)) + ) # The auth/user/change_password.html template uses super in the # bodyclass block. - self.assertContains(response, 'bodyclass_consistency_check ') + self.assertContains(response, "bodyclass_consistency_check ") # When a site has multiple passwords in the browser's password manager, # a browser pop up asks which user the new password is for. To prevent # this, the username is added to the change password form. - self.assertContains(response, '<input type="text" name="username" value="super" class="hidden">') + self.assertContains( + response, '<input type="text" name="username" value="super" class="hidden">' + ) def test_extended_bodyclass_template_index(self): """ The admin/index.html template uses block.super in the bodyclass block. """ - response = self.client.get(reverse('admin:index')) - self.assertContains(response, 'bodyclass_consistency_check ') + response = self.client.get(reverse("admin:index")) + self.assertContains(response, "bodyclass_consistency_check ") def test_extended_bodyclass_change_list(self): """ The admin/change_list.html' template uses block.super in the bodyclass block. """ - response = self.client.get(reverse('admin:admin_views_article_changelist')) - self.assertContains(response, 'bodyclass_consistency_check ') + response = self.client.get(reverse("admin:admin_views_article_changelist")) + self.assertContains(response, "bodyclass_consistency_check ") def test_extended_bodyclass_template_login(self): """ @@ -1251,8 +1557,8 @@ class AdminCustomTemplateTests(AdminViewBasicTestCase): bodyclass block. """ self.client.logout() - response = self.client.get(reverse('admin:login')) - self.assertContains(response, 'bodyclass_consistency_check ') + response = self.client.get(reverse("admin:login")) + self.assertContains(response, "bodyclass_consistency_check ") def test_extended_bodyclass_template_delete_confirmation(self): """ @@ -1260,8 +1566,8 @@ class AdminCustomTemplateTests(AdminViewBasicTestCase): block.super in the bodyclass block. """ group = Group.objects.create(name="foogroup") - response = self.client.get(reverse('admin:auth_group_delete', args=(group.id,))) - self.assertContains(response, 'bodyclass_consistency_check ') + response = self.client.get(reverse("admin:auth_group_delete", args=(group.id,))) + self.assertContains(response, "bodyclass_consistency_check ") def test_extended_bodyclass_template_delete_selected_confirmation(self): """ @@ -1270,41 +1576,51 @@ class AdminCustomTemplateTests(AdminViewBasicTestCase): """ group = Group.objects.create(name="foogroup") post_data = { - 'action': 'delete_selected', - 'selected_across': '0', - 'index': '0', - '_selected_action': group.id + "action": "delete_selected", + "selected_across": "0", + "index": "0", + "_selected_action": group.id, } - response = self.client.post(reverse('admin:auth_group_changelist'), post_data) - self.assertEqual(response.context['site_header'], 'Django administration') - self.assertContains(response, 'bodyclass_consistency_check ') + response = self.client.post(reverse("admin:auth_group_changelist"), post_data) + self.assertEqual(response.context["site_header"], "Django administration") + self.assertContains(response, "bodyclass_consistency_check ") def test_filter_with_custom_template(self): """ A custom template can be used to render an admin filter. """ - response = self.client.get(reverse('admin:admin_views_color2_changelist')) - self.assertTemplateUsed(response, 'custom_filter_template.html') + response = self.client.get(reverse("admin:admin_views_color2_changelist")) + self.assertTemplateUsed(response, "custom_filter_template.html") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewFormUrlTest(TestCase): current_app = "admin3" @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') def setUp(self): self.client.force_login(self.superuser) @@ -1314,10 +1630,16 @@ class AdminViewFormUrlTest(TestCase): change_view has form_url in response.context """ response = self.client.get( - reverse('admin:admin_views_section_change', args=(self.s1.pk,), current_app=self.current_app) + reverse( + "admin:admin_views_section_change", + args=(self.s1.pk,), + current_app=self.current_app, + ) ) - self.assertIn('form_url', response.context, msg='form_url not present in response.context') - self.assertEqual(response.context['form_url'], 'pony') + self.assertIn( + "form_url", response.context, msg="form_url not present in response.context" + ) + self.assertEqual(response.context["form_url"], "pony") def test_initial_data_can_be_overridden(self): """ @@ -1325,8 +1647,8 @@ class AdminViewFormUrlTest(TestCase): ModelAdmin class. Usually, the initial value is set via the GET params. """ response = self.client.get( - reverse('admin:admin_views_restaurant_add', current_app=self.current_app), - {'name': 'test_value'} + reverse("admin:admin_views_restaurant_add", current_app=self.current_app), + {"name": "test_value"}, ) # this would be the usual behaviour self.assertNotContains(response, 'value="test_value"') @@ -1334,12 +1656,13 @@ class AdminViewFormUrlTest(TestCase): self.assertContains(response, 'value="overridden_value"') -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminJavaScriptTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -1349,54 +1672,66 @@ class AdminJavaScriptTest(TestCase): The minified versions of the JS files are only used when DEBUG is False. """ with override_settings(DEBUG=False): - response = self.client.get(reverse('admin:admin_views_section_add')) - self.assertNotContains(response, 'vendor/jquery/jquery.js') - self.assertContains(response, 'vendor/jquery/jquery.min.js') - self.assertContains(response, 'prepopulate.js') - self.assertContains(response, 'actions.js') - self.assertContains(response, 'collapse.js') - self.assertContains(response, 'inlines.js') + response = self.client.get(reverse("admin:admin_views_section_add")) + self.assertNotContains(response, "vendor/jquery/jquery.js") + self.assertContains(response, "vendor/jquery/jquery.min.js") + self.assertContains(response, "prepopulate.js") + self.assertContains(response, "actions.js") + self.assertContains(response, "collapse.js") + self.assertContains(response, "inlines.js") with override_settings(DEBUG=True): - response = self.client.get(reverse('admin:admin_views_section_add')) - self.assertContains(response, 'vendor/jquery/jquery.js') - self.assertNotContains(response, 'vendor/jquery/jquery.min.js') - self.assertContains(response, 'prepopulate.js') - self.assertContains(response, 'actions.js') - self.assertContains(response, 'collapse.js') - self.assertContains(response, 'inlines.js') + response = self.client.get(reverse("admin:admin_views_section_add")) + self.assertContains(response, "vendor/jquery/jquery.js") + self.assertNotContains(response, "vendor/jquery/jquery.min.js") + self.assertContains(response, "prepopulate.js") + self.assertContains(response, "actions.js") + self.assertContains(response, "collapse.js") + self.assertContains(response, "inlines.js") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class SaveAsTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.per1 = Person.objects.create(name='John Mauchly', gender=1, alive=True) + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.per1 = Person.objects.create(name="John Mauchly", gender=1, alive=True) def setUp(self): self.client.force_login(self.superuser) def test_save_as_duplication(self): """'save as' creates a new person""" - post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42} - response = self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), post_data) - self.assertEqual(len(Person.objects.filter(name='John M')), 1) + post_data = {"_saveasnew": "", "name": "John M", "gender": 1, "age": 42} + response = self.client.post( + reverse("admin:admin_views_person_change", args=(self.per1.pk,)), post_data + ) + self.assertEqual(len(Person.objects.filter(name="John M")), 1) self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1) - new_person = Person.objects.latest('id') - self.assertRedirects(response, reverse('admin:admin_views_person_change', args=(new_person.pk,))) + new_person = Person.objects.latest("id") + self.assertRedirects( + response, reverse("admin:admin_views_person_change", args=(new_person.pk,)) + ) def test_save_as_continue_false(self): """ Saving a new object using "Save as new" redirects to the changelist instead of the change view when ModelAdmin.save_as_continue=False. """ - post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42} - url = reverse('admin:admin_views_person_change', args=(self.per1.pk,), current_app=site2.name) + post_data = {"_saveasnew": "", "name": "John M", "gender": 1, "age": 42} + url = reverse( + "admin:admin_views_person_change", + args=(self.per1.pk,), + current_app=site2.name, + ) response = self.client.post(url, post_data) - self.assertEqual(len(Person.objects.filter(name='John M')), 1) + self.assertEqual(len(Person.objects.filter(name="John M")), 1) self.assertEqual(len(Person.objects.filter(id=self.per1.pk)), 1) - self.assertRedirects(response, reverse('admin:admin_views_person_changelist', current_app=site2.name)) + self.assertRedirects( + response, + reverse("admin:admin_views_person_changelist", current_app=site2.name), + ) def test_save_as_new_with_validation_errors(self): """ @@ -1404,124 +1739,142 @@ class SaveAsTests(TestCase): you only see the "Save as new" button and not the other save buttons, and that only the "Save as" button is visible. """ - response = self.client.post(reverse('admin:admin_views_person_change', args=(self.per1.pk,)), { - '_saveasnew': '', - 'gender': 'invalid', - '_addanother': 'fail', - }) - self.assertContains(response, 'Please correct the errors below.') - self.assertFalse(response.context['show_save_and_add_another']) - self.assertFalse(response.context['show_save_and_continue']) - self.assertTrue(response.context['show_save_as_new']) + response = self.client.post( + reverse("admin:admin_views_person_change", args=(self.per1.pk,)), + { + "_saveasnew": "", + "gender": "invalid", + "_addanother": "fail", + }, + ) + self.assertContains(response, "Please correct the errors below.") + self.assertFalse(response.context["show_save_and_add_another"]) + self.assertFalse(response.context["show_save_and_continue"]) + self.assertTrue(response.context["show_save_as_new"]) def test_save_as_new_with_validation_errors_with_inlines(self): - parent = Parent.objects.create(name='Father') - child = Child.objects.create(parent=parent, name='Child') - response = self.client.post(reverse('admin:admin_views_parent_change', args=(parent.pk,)), { - '_saveasnew': 'Save as new', - 'child_set-0-parent': parent.pk, - 'child_set-0-id': child.pk, - 'child_set-0-name': 'Child', - 'child_set-INITIAL_FORMS': 1, - 'child_set-MAX_NUM_FORMS': 1000, - 'child_set-MIN_NUM_FORMS': 0, - 'child_set-TOTAL_FORMS': 4, - 'name': '_invalid', - }) - self.assertContains(response, 'Please correct the error below.') - self.assertFalse(response.context['show_save_and_add_another']) - self.assertFalse(response.context['show_save_and_continue']) - self.assertTrue(response.context['show_save_as_new']) + parent = Parent.objects.create(name="Father") + child = Child.objects.create(parent=parent, name="Child") + response = self.client.post( + reverse("admin:admin_views_parent_change", args=(parent.pk,)), + { + "_saveasnew": "Save as new", + "child_set-0-parent": parent.pk, + "child_set-0-id": child.pk, + "child_set-0-name": "Child", + "child_set-INITIAL_FORMS": 1, + "child_set-MAX_NUM_FORMS": 1000, + "child_set-MIN_NUM_FORMS": 0, + "child_set-TOTAL_FORMS": 4, + "name": "_invalid", + }, + ) + self.assertContains(response, "Please correct the error below.") + self.assertFalse(response.context["show_save_and_add_another"]) + self.assertFalse(response.context["show_save_and_continue"]) + self.assertTrue(response.context["show_save_as_new"]) def test_save_as_new_with_inlines_with_validation_errors(self): - parent = Parent.objects.create(name='Father') - child = Child.objects.create(parent=parent, name='Child') - response = self.client.post(reverse('admin:admin_views_parent_change', args=(parent.pk,)), { - '_saveasnew': 'Save as new', - 'child_set-0-parent': parent.pk, - 'child_set-0-id': child.pk, - 'child_set-0-name': '_invalid', - 'child_set-INITIAL_FORMS': 1, - 'child_set-MAX_NUM_FORMS': 1000, - 'child_set-MIN_NUM_FORMS': 0, - 'child_set-TOTAL_FORMS': 4, - 'name': 'Father', - }) - self.assertContains(response, 'Please correct the error below.') - self.assertFalse(response.context['show_save_and_add_another']) - self.assertFalse(response.context['show_save_and_continue']) - self.assertTrue(response.context['show_save_as_new']) - - -@override_settings(ROOT_URLCONF='admin_views.urls') -class CustomModelAdminTest(AdminViewBasicTestCase): + parent = Parent.objects.create(name="Father") + child = Child.objects.create(parent=parent, name="Child") + response = self.client.post( + reverse("admin:admin_views_parent_change", args=(parent.pk,)), + { + "_saveasnew": "Save as new", + "child_set-0-parent": parent.pk, + "child_set-0-id": child.pk, + "child_set-0-name": "_invalid", + "child_set-INITIAL_FORMS": 1, + "child_set-MAX_NUM_FORMS": 1000, + "child_set-MIN_NUM_FORMS": 0, + "child_set-TOTAL_FORMS": 4, + "name": "Father", + }, + ) + self.assertContains(response, "Please correct the error below.") + self.assertFalse(response.context["show_save_and_add_another"]) + self.assertFalse(response.context["show_save_and_continue"]) + self.assertTrue(response.context["show_save_as_new"]) + +@override_settings(ROOT_URLCONF="admin_views.urls") +class CustomModelAdminTest(AdminViewBasicTestCase): def test_custom_admin_site_login_form(self): self.client.logout() - response = self.client.get(reverse('admin2:index'), follow=True) + response = self.client.get(reverse("admin2:index"), follow=True) self.assertIsInstance(response, TemplateResponse) self.assertEqual(response.status_code, 200) - login = self.client.post(reverse('admin2:login'), { - REDIRECT_FIELD_NAME: reverse('admin2:index'), - 'username': 'customform', - 'password': 'secret', - }, follow=True) + login = self.client.post( + reverse("admin2:login"), + { + REDIRECT_FIELD_NAME: reverse("admin2:index"), + "username": "customform", + "password": "secret", + }, + follow=True, + ) self.assertIsInstance(login, TemplateResponse) - self.assertContains(login, 'custom form error') - self.assertContains(login, 'path/to/media.css') + self.assertContains(login, "custom form error") + self.assertContains(login, "path/to/media.css") def test_custom_admin_site_login_template(self): self.client.logout() - response = self.client.get(reverse('admin2:index'), follow=True) + response = self.client.get(reverse("admin2:index"), follow=True) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/login.html') - self.assertContains(response, 'Hello from a custom login template') + self.assertTemplateUsed(response, "custom_admin/login.html") + self.assertContains(response, "Hello from a custom login template") def test_custom_admin_site_logout_template(self): - response = self.client.get(reverse('admin2:logout')) + response = self.client.get(reverse("admin2:logout")) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/logout.html') - self.assertContains(response, 'Hello from a custom logout template') + self.assertTemplateUsed(response, "custom_admin/logout.html") + self.assertContains(response, "Hello from a custom logout template") def test_custom_admin_site_index_view_and_template(self): - response = self.client.get(reverse('admin2:index')) + response = self.client.get(reverse("admin2:index")) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/index.html') - self.assertContains(response, 'Hello from a custom index template *bar*') + self.assertTemplateUsed(response, "custom_admin/index.html") + self.assertContains(response, "Hello from a custom index template *bar*") def test_custom_admin_site_app_index_view_and_template(self): - response = self.client.get(reverse('admin2:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin2:app_list", args=("admin_views",))) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/app_index.html') - self.assertContains(response, 'Hello from a custom app_index template') + self.assertTemplateUsed(response, "custom_admin/app_index.html") + self.assertContains(response, "Hello from a custom app_index template") def test_custom_admin_site_password_change_template(self): - response = self.client.get(reverse('admin2:password_change')) + response = self.client.get(reverse("admin2:password_change")) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/password_change_form.html') - self.assertContains(response, 'Hello from a custom password change form template') + self.assertTemplateUsed(response, "custom_admin/password_change_form.html") + self.assertContains( + response, "Hello from a custom password change form template" + ) def test_custom_admin_site_password_change_with_extra_context(self): - response = self.client.get(reverse('admin2:password_change')) + response = self.client.get(reverse("admin2:password_change")) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/password_change_form.html') - self.assertContains(response, 'eggs') + self.assertTemplateUsed(response, "custom_admin/password_change_form.html") + self.assertContains(response, "eggs") def test_custom_admin_site_password_change_done_template(self): - response = self.client.get(reverse('admin2:password_change_done')) + response = self.client.get(reverse("admin2:password_change_done")) self.assertIsInstance(response, TemplateResponse) - self.assertTemplateUsed(response, 'custom_admin/password_change_done.html') - self.assertContains(response, 'Hello from a custom password change done template') + self.assertTemplateUsed(response, "custom_admin/password_change_done.html") + self.assertContains( + response, "Hello from a custom password change done template" + ) def test_custom_admin_site_view(self): self.client.force_login(self.superuser) - response = self.client.get(reverse('admin2:my_view')) + response = self.client.get(reverse("admin2:my_view")) self.assertEqual(response.content, b"Django is a magical pony!") def test_pwd_change_custom_template(self): self.client.force_login(self.superuser) - su = User.objects.get(username='super') - response = self.client.get(reverse('admin4:auth_user_password_change', args=(su.pk,))) + su = User.objects.get(username="super") + response = self.client.get( + reverse("admin4:auth_user_password_change", args=(su.pk,)) + ) self.assertEqual(response.status_code, 200) @@ -1532,110 +1885,146 @@ def get_perm(Model, codename): @override_settings( - ROOT_URLCONF='admin_views.urls', + ROOT_URLCONF="admin_views.urls", # Test with the admin's documented list of required context processors. - TEMPLATES=[{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }], + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ], ) class AdminViewPermissionsTest(TestCase): """Tests for Admin Views Permissions.""" @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.viewuser = User.objects.create_user(username='viewuser', password='secret', is_staff=True) - cls.adduser = User.objects.create_user(username='adduser', password='secret', is_staff=True) - cls.changeuser = User.objects.create_user(username='changeuser', password='secret', is_staff=True) - cls.deleteuser = User.objects.create_user(username='deleteuser', password='secret', is_staff=True) - cls.joepublicuser = User.objects.create_user(username='joepublic', password='secret') - cls.nostaffuser = User.objects.create_user(username='nostaff', password='secret') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.viewuser = User.objects.create_user( + username="viewuser", password="secret", is_staff=True + ) + cls.adduser = User.objects.create_user( + username="adduser", password="secret", is_staff=True + ) + cls.changeuser = User.objects.create_user( + username="changeuser", password="secret", is_staff=True + ) + cls.deleteuser = User.objects.create_user( + username="deleteuser", password="secret", is_staff=True + ) + cls.joepublicuser = User.objects.create_user( + username="joepublic", password="secret" + ) + cls.nostaffuser = User.objects.create_user( + username="nostaff", password="secret" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1, + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, another_section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') # Setup permissions, for our users who can add, change, and delete. opts = Article._meta # User who can view Articles - cls.viewuser.user_permissions.add(get_perm(Article, get_permission_codename('view', opts))) + cls.viewuser.user_permissions.add( + get_perm(Article, get_permission_codename("view", opts)) + ) # User who can add Articles - cls.adduser.user_permissions.add(get_perm(Article, get_permission_codename('add', opts))) + cls.adduser.user_permissions.add( + get_perm(Article, get_permission_codename("add", opts)) + ) # User who can change Articles - cls.changeuser.user_permissions.add(get_perm(Article, get_permission_codename('change', opts))) - cls.nostaffuser.user_permissions.add(get_perm(Article, get_permission_codename('change', opts))) + cls.changeuser.user_permissions.add( + get_perm(Article, get_permission_codename("change", opts)) + ) + cls.nostaffuser.user_permissions.add( + get_perm(Article, get_permission_codename("change", opts)) + ) # User who can delete Articles - cls.deleteuser.user_permissions.add(get_perm(Article, get_permission_codename('delete', opts))) - cls.deleteuser.user_permissions.add(get_perm(Section, get_permission_codename('delete', Section._meta))) + cls.deleteuser.user_permissions.add( + get_perm(Article, get_permission_codename("delete", opts)) + ) + cls.deleteuser.user_permissions.add( + get_perm(Section, get_permission_codename("delete", Section._meta)) + ) # login POST dicts - cls.index_url = reverse('admin:index') + cls.index_url = reverse("admin:index") cls.super_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'super', - 'password': 'secret', + "username": "super", + "password": "secret", } cls.super_email_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'super@example.com', - 'password': 'secret', + "username": "super@example.com", + "password": "secret", } cls.super_email_bad_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'super@example.com', - 'password': 'notsecret', + "username": "super@example.com", + "password": "notsecret", } cls.adduser_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'adduser', - 'password': 'secret', + "username": "adduser", + "password": "secret", } cls.changeuser_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'changeuser', - 'password': 'secret', + "username": "changeuser", + "password": "secret", } cls.deleteuser_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'deleteuser', - 'password': 'secret', + "username": "deleteuser", + "password": "secret", } cls.nostaff_login = { - REDIRECT_FIELD_NAME: reverse('has_permission_admin:index'), - 'username': 'nostaff', - 'password': 'secret', + REDIRECT_FIELD_NAME: reverse("has_permission_admin:index"), + "username": "nostaff", + "password": "secret", } cls.joepublic_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'joepublic', - 'password': 'secret', + "username": "joepublic", + "password": "secret", } cls.viewuser_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'username': 'viewuser', - 'password': 'secret', + "username": "viewuser", + "password": "secret", } cls.no_username_login = { REDIRECT_FIELD_NAME: cls.index_url, - 'password': 'secret', + "password": "secret", } def test_login(self): @@ -1646,14 +2035,14 @@ class AdminViewPermissionsTest(TestCase): Unsuccessful attempts will continue to render the login page with a 200 status code. """ - login_url = '%s?next=%s' % (reverse('admin:login'), reverse('admin:index')) + login_url = "%s?next=%s" % (reverse("admin:login"), reverse("admin:index")) # Super User response = self.client.get(self.index_url) self.assertRedirects(response, login_url) login = self.client.post(login_url, self.super_login) self.assertRedirects(login, self.index_url) self.assertFalse(login.context) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Test if user enters email address response = self.client.get(self.index_url) @@ -1663,7 +2052,7 @@ class AdminViewPermissionsTest(TestCase): # only correct passwords get a username hint login = self.client.post(login_url, self.super_email_bad_login) self.assertContains(login, ERROR_MESSAGE) - new_user = User(username='jondoe', password='secret', email='super@example.com') + new_user = User(username="jondoe", password="secret", email="super@example.com") new_user.save() # check to ensure if there are multiple email addresses a user doesn't get a 500 login = self.client.post(login_url, self.super_email_login) @@ -1675,7 +2064,7 @@ class AdminViewPermissionsTest(TestCase): login = self.client.post(login_url, self.viewuser_login) self.assertRedirects(login, self.index_url) self.assertFalse(login.context) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Add User response = self.client.get(self.index_url) @@ -1683,7 +2072,7 @@ class AdminViewPermissionsTest(TestCase): login = self.client.post(login_url, self.adduser_login) self.assertRedirects(login, self.index_url) self.assertFalse(login.context) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Change User response = self.client.get(self.index_url) @@ -1691,7 +2080,7 @@ class AdminViewPermissionsTest(TestCase): login = self.client.post(login_url, self.changeuser_login) self.assertRedirects(login, self.index_url) self.assertFalse(login.context) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Delete User response = self.client.get(self.index_url) @@ -1699,7 +2088,7 @@ class AdminViewPermissionsTest(TestCase): login = self.client.post(login_url, self.deleteuser_login) self.assertRedirects(login, self.index_url) self.assertFalse(login.context) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Regular User should not be able to login. response = self.client.get(self.index_url) @@ -1712,60 +2101,67 @@ class AdminViewPermissionsTest(TestCase): self.assertEqual(response.status_code, 302) login = self.client.post(login_url, self.no_username_login) self.assertEqual(login.status_code, 200) - self.assertFormError(login, 'form', 'username', ['This field is required.']) + self.assertFormError(login, "form", "username", ["This field is required."]) def test_login_redirect_for_direct_get(self): """ Login redirect should be to the admin index page when going directly to /admin/login/. """ - response = self.client.get(reverse('admin:login')) + response = self.client.get(reverse("admin:login")) self.assertEqual(response.status_code, 200) - self.assertEqual(response.context[REDIRECT_FIELD_NAME], reverse('admin:index')) + self.assertEqual(response.context[REDIRECT_FIELD_NAME], reverse("admin:index")) def test_login_has_permission(self): # Regular User should not be able to login. - response = self.client.get(reverse('has_permission_admin:index')) + response = self.client.get(reverse("has_permission_admin:index")) self.assertEqual(response.status_code, 302) - login = self.client.post(reverse('has_permission_admin:login'), self.joepublic_login) - self.assertContains(login, 'permission denied') + login = self.client.post( + reverse("has_permission_admin:login"), self.joepublic_login + ) + self.assertContains(login, "permission denied") # User with permissions should be able to login. - response = self.client.get(reverse('has_permission_admin:index')) + response = self.client.get(reverse("has_permission_admin:index")) self.assertEqual(response.status_code, 302) - login = self.client.post(reverse('has_permission_admin:login'), self.nostaff_login) - self.assertRedirects(login, reverse('has_permission_admin:index')) + login = self.client.post( + reverse("has_permission_admin:login"), self.nostaff_login + ) + self.assertRedirects(login, reverse("has_permission_admin:index")) self.assertFalse(login.context) - self.client.get(reverse('has_permission_admin:logout')) + self.client.get(reverse("has_permission_admin:logout")) # Staff should be able to login. - response = self.client.get(reverse('has_permission_admin:index')) + response = self.client.get(reverse("has_permission_admin:index")) self.assertEqual(response.status_code, 302) - login = self.client.post(reverse('has_permission_admin:login'), { - REDIRECT_FIELD_NAME: reverse('has_permission_admin:index'), - 'username': 'deleteuser', - 'password': 'secret', - }) - self.assertRedirects(login, reverse('has_permission_admin:index')) + login = self.client.post( + reverse("has_permission_admin:login"), + { + REDIRECT_FIELD_NAME: reverse("has_permission_admin:index"), + "username": "deleteuser", + "password": "secret", + }, + ) + self.assertRedirects(login, reverse("has_permission_admin:index")) self.assertFalse(login.context) - self.client.get(reverse('has_permission_admin:logout')) + self.client.get(reverse("has_permission_admin:logout")) def test_login_successfully_redirects_to_original_URL(self): response = self.client.get(self.index_url) self.assertEqual(response.status_code, 302) - query_string = 'the-answer=42' - redirect_url = '%s?%s' % (self.index_url, query_string) + query_string = "the-answer=42" + redirect_url = "%s?%s" % (self.index_url, query_string) new_next = {REDIRECT_FIELD_NAME: redirect_url} post_data = self.super_login.copy() post_data.pop(REDIRECT_FIELD_NAME) login = self.client.post( - '%s?%s' % (reverse('admin:login'), urlencode(new_next)), - post_data) + "%s?%s" % (reverse("admin:login"), urlencode(new_next)), post_data + ) self.assertRedirects(login, redirect_url) def test_double_login_is_not_allowed(self): """Regression test for #19327""" - login_url = '%s?next=%s' % (reverse('admin:login'), reverse('admin:index')) + login_url = "%s?next=%s" % (reverse("admin:login"), reverse("admin:index")) response = self.client.get(self.index_url) self.assertEqual(response.status_code, 302) @@ -1788,7 +2184,7 @@ class AdminViewPermissionsTest(TestCase): login = self.client.post(login_url, self.super_login) self.assertRedirects(login, self.index_url) self.assertFalse(login.context) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) def test_login_page_notice_for_non_staff_users(self): """ @@ -1796,76 +2192,93 @@ class AdminViewPermissionsTest(TestCase): presented with the login page and a hint indicating that the current user doesn't have access to it. """ - hint_template = 'You are authenticated as {}' + hint_template = "You are authenticated as {}" # Anonymous user should not be shown the hint response = self.client.get(self.index_url, follow=True) - self.assertContains(response, 'login-form') - self.assertNotContains(response, hint_template.format(''), status_code=200) + self.assertContains(response, "login-form") + self.assertNotContains(response, hint_template.format(""), status_code=200) # Non-staff user should be shown the hint self.client.force_login(self.nostaffuser) response = self.client.get(self.index_url, follow=True) - self.assertContains(response, 'login-form') - self.assertContains(response, hint_template.format(self.nostaffuser.username), status_code=200) + self.assertContains(response, "login-form") + self.assertContains( + response, hint_template.format(self.nostaffuser.username), status_code=200 + ) def test_add_view(self): """Test add view restricts access and actually adds items.""" add_dict = { - 'title': 'Døm ikke', - 'content': '<p>great article</p>', - 'date_0': '2008-03-18', 'date_1': '10:54:39', - 'section': self.s1.pk, + "title": "Døm ikke", + "content": "<p>great article</p>", + "date_0": "2008-03-18", + "date_1": "10:54:39", + "section": self.s1.pk, } # Change User should not have access to add articles self.client.force_login(self.changeuser) # make sure the view removes test cookie self.assertIs(self.client.session.test_cookie_worked(), False) - response = self.client.get(reverse('admin:admin_views_article_add')) + response = self.client.get(reverse("admin:admin_views_article_add")) self.assertEqual(response.status_code, 403) # Try POST just to make sure - post = self.client.post(reverse('admin:admin_views_article_add'), add_dict) + post = self.client.post(reverse("admin:admin_views_article_add"), add_dict) self.assertEqual(post.status_code, 403) self.assertEqual(Article.objects.count(), 3) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # View User should not have access to add articles self.client.force_login(self.viewuser) - response = self.client.get(reverse('admin:admin_views_article_add')) + response = self.client.get(reverse("admin:admin_views_article_add")) self.assertEqual(response.status_code, 403) # Try POST just to make sure - post = self.client.post(reverse('admin:admin_views_article_add'), add_dict) + post = self.client.post(reverse("admin:admin_views_article_add"), add_dict) self.assertEqual(post.status_code, 403) self.assertEqual(Article.objects.count(), 3) # Now give the user permission to add but not change. - self.viewuser.user_permissions.add(get_perm(Article, get_permission_codename('add', Article._meta))) - response = self.client.get(reverse('admin:admin_views_article_add')) - self.assertEqual(response.context['title'], 'Add article') - self.assertContains(response, '<title>Add article | Django site admin</title>') - self.assertContains(response, '<input type="submit" value="Save and view" name="_continue">') - post = self.client.post(reverse('admin:admin_views_article_add'), add_dict, follow=False) + self.viewuser.user_permissions.add( + get_perm(Article, get_permission_codename("add", Article._meta)) + ) + response = self.client.get(reverse("admin:admin_views_article_add")) + self.assertEqual(response.context["title"], "Add article") + self.assertContains(response, "<title>Add article | Django site admin</title>") + self.assertContains( + response, '<input type="submit" value="Save and view" name="_continue">' + ) + post = self.client.post( + reverse("admin:admin_views_article_add"), add_dict, follow=False + ) self.assertEqual(post.status_code, 302) self.assertEqual(Article.objects.count(), 4) - article = Article.objects.latest('pk') - response = self.client.get(reverse('admin:admin_views_article_change', args=(article.pk,))) - self.assertContains(response, '<li class="success">The article “Døm ikke” was added successfully.</li>') + article = Article.objects.latest("pk") + response = self.client.get( + reverse("admin:admin_views_article_change", args=(article.pk,)) + ) + self.assertContains( + response, + '<li class="success">The article “Døm ikke” was added successfully.</li>', + ) article.delete() - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Add user may login and POST to add view, then redirect to admin root self.client.force_login(self.adduser) - addpage = self.client.get(reverse('admin:admin_views_article_add')) - change_list_link = '› <a href="%s">Articles</a>' % reverse('admin:admin_views_article_changelist') + addpage = self.client.get(reverse("admin:admin_views_article_add")) + change_list_link = '› <a href="%s">Articles</a>' % reverse( + "admin:admin_views_article_changelist" + ) self.assertNotContains( - addpage, change_list_link, - msg_prefix='User restricted to add permission is given link to change list view in breadcrumbs.' + addpage, + change_list_link, + msg_prefix="User restricted to add permission is given link to change list view in breadcrumbs.", ) - post = self.client.post(reverse('admin:admin_views_article_add'), add_dict) + post = self.client.post(reverse("admin:admin_views_article_add"), add_dict) self.assertRedirects(post, self.index_url) self.assertEqual(Article.objects.count(), 4) self.assertEqual(len(mail.outbox), 2) - self.assertEqual(mail.outbox[0].subject, 'Greetings from a created object') - self.client.get(reverse('admin:logout')) + self.assertEqual(mail.outbox[0].subject, "Greetings from a created object") + self.client.get(reverse("admin:logout")) # The addition was logged correctly addition_log = LogEntry.objects.all()[0] @@ -1880,15 +2293,16 @@ class AdminViewPermissionsTest(TestCase): # Super can add too, but is redirected to the change list view self.client.force_login(self.superuser) - addpage = self.client.get(reverse('admin:admin_views_article_add')) + addpage = self.client.get(reverse("admin:admin_views_article_add")) self.assertContains( - addpage, change_list_link, - msg_prefix='Unrestricted user is not given link to change list view in breadcrumbs.' + addpage, + change_list_link, + msg_prefix="Unrestricted user is not given link to change list view in breadcrumbs.", ) - post = self.client.post(reverse('admin:admin_views_article_add'), add_dict) - self.assertRedirects(post, reverse('admin:admin_views_article_changelist')) + post = self.client.post(reverse("admin:admin_views_article_add"), add_dict) + self.assertRedirects(post, reverse("admin:admin_views_article_changelist")) self.assertEqual(Article.objects.count(), 5) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # 8509 - if a normal user is already logged in, it is possible # to change user into the superuser without error @@ -1898,34 +2312,42 @@ class AdminViewPermissionsTest(TestCase): # make sure the view removes test cookie self.assertIs(self.client.session.test_cookie_worked(), False) - @mock.patch('django.contrib.admin.options.InlineModelAdmin.has_change_permission') + @mock.patch("django.contrib.admin.options.InlineModelAdmin.has_change_permission") def test_add_view_with_view_only_inlines(self, has_change_permission): """User with add permission to a section but view-only for inlines.""" - self.viewuser.user_permissions.add(get_perm(Section, get_permission_codename('add', Section._meta))) + self.viewuser.user_permissions.add( + get_perm(Section, get_permission_codename("add", Section._meta)) + ) self.client.force_login(self.viewuser) # Valid POST creates a new section. data = { - 'name': 'New obj', - 'article_set-TOTAL_FORMS': 0, - 'article_set-INITIAL_FORMS': 0, + "name": "New obj", + "article_set-TOTAL_FORMS": 0, + "article_set-INITIAL_FORMS": 0, } - response = self.client.post(reverse('admin:admin_views_section_add'), data) - self.assertRedirects(response, reverse('admin:index')) - self.assertEqual(Section.objects.latest('id').name, data['name']) + response = self.client.post(reverse("admin:admin_views_section_add"), data) + self.assertRedirects(response, reverse("admin:index")) + self.assertEqual(Section.objects.latest("id").name, data["name"]) # InlineModelAdmin.has_change_permission()'s obj argument is always # None during object add. - self.assertEqual([obj for (request, obj), _ in has_change_permission.call_args_list], [None, None]) + self.assertEqual( + [obj for (request, obj), _ in has_change_permission.call_args_list], + [None, None], + ) def test_change_view(self): """Change view should restrict access and allow users to edit items.""" change_dict = { - 'title': 'Ikke fordømt', - 'content': '<p>edited article</p>', - 'date_0': '2008-03-18', 'date_1': '10:54:39', - 'section': self.s1.pk, + "title": "Ikke fordømt", + "content": "<p>edited article</p>", + "date_0": "2008-03-18", + "date_1": "10:54:39", + "section": self.s1.pk, } - article_change_url = reverse('admin:admin_views_article_change', args=(self.a1.pk,)) - article_changelist_url = reverse('admin:admin_views_article_changelist') + article_change_url = reverse( + "admin:admin_views_article_change", args=(self.a1.pk,) + ) + article_changelist_url = reverse("admin:admin_views_article_changelist") # add user should not be able to view the list of article or change any of them self.client.force_login(self.adduser) @@ -1935,127 +2357,170 @@ class AdminViewPermissionsTest(TestCase): self.assertEqual(response.status_code, 403) post = self.client.post(article_change_url, change_dict) self.assertEqual(post.status_code, 403) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # view user can view articles but not make changes. self.client.force_login(self.viewuser) response = self.client.get(article_changelist_url) self.assertContains( response, - '<title>Select article to view | Django site admin</title>', + "<title>Select article to view | Django site admin</title>", ) - self.assertContains(response, '<h1>Select article to view</h1>') - self.assertEqual(response.context['title'], 'Select article to view') + self.assertContains(response, "<h1>Select article to view</h1>") + self.assertEqual(response.context["title"], "Select article to view") response = self.client.get(article_change_url) - self.assertContains(response, '<title>View article | Django site admin</title>') - self.assertContains(response, '<h1>View article</h1>') - self.assertContains(response, '<label>Extra form field:</label>') - self.assertContains(response, '<a href="/test_admin/admin/admin_views/article/" class="closelink">Close</a>') - self.assertEqual(response.context['title'], 'View article') + self.assertContains(response, "<title>View article | Django site admin</title>") + self.assertContains(response, "<h1>View article</h1>") + self.assertContains(response, "<label>Extra form field:</label>") + self.assertContains( + response, + '<a href="/test_admin/admin/admin_views/article/" class="closelink">Close</a>', + ) + self.assertEqual(response.context["title"], "View article") post = self.client.post(article_change_url, change_dict) self.assertEqual(post.status_code, 403) - self.assertEqual(Article.objects.get(pk=self.a1.pk).content, '<p>Middle content</p>') - self.client.get(reverse('admin:logout')) + self.assertEqual( + Article.objects.get(pk=self.a1.pk).content, "<p>Middle content</p>" + ) + self.client.get(reverse("admin:logout")) # change user can view all items and edit them self.client.force_login(self.changeuser) response = self.client.get(article_changelist_url) - self.assertEqual(response.context['title'], 'Select article to change') + self.assertEqual(response.context["title"], "Select article to change") self.assertContains( response, - '<title>Select article to change | Django site admin</title>', + "<title>Select article to change | Django site admin</title>", ) - self.assertContains(response, '<h1>Select article to change</h1>') + self.assertContains(response, "<h1>Select article to change</h1>") response = self.client.get(article_change_url) - self.assertEqual(response.context['title'], 'Change article') + self.assertEqual(response.context["title"], "Change article") self.assertContains( response, - '<title>Change article | Django site admin</title>', + "<title>Change article | Django site admin</title>", ) - self.assertContains(response, '<h1>Change article</h1>') + self.assertContains(response, "<h1>Change article</h1>") post = self.client.post(article_change_url, change_dict) self.assertRedirects(post, article_changelist_url) - self.assertEqual(Article.objects.get(pk=self.a1.pk).content, '<p>edited article</p>') + self.assertEqual( + Article.objects.get(pk=self.a1.pk).content, "<p>edited article</p>" + ) # one error in form should produce singular error message, multiple errors plural - change_dict['title'] = '' + change_dict["title"] = "" post = self.client.post(article_change_url, change_dict) self.assertContains( - post, 'Please correct the error below.', - msg_prefix='Singular error message not found in response to post with one error' + post, + "Please correct the error below.", + msg_prefix="Singular error message not found in response to post with one error", ) - change_dict['content'] = '' + change_dict["content"] = "" post = self.client.post(article_change_url, change_dict) self.assertContains( - post, 'Please correct the errors below.', - msg_prefix='Plural error message not found in response to post with multiple errors' + post, + "Please correct the errors below.", + msg_prefix="Plural error message not found in response to post with multiple errors", ) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # Test redirection when using row-level change permissions. Refs #11513. r1 = RowLevelChangePermissionModel.objects.create(id=1, name="odd id") r2 = RowLevelChangePermissionModel.objects.create(id=2, name="even id") - r3 = RowLevelChangePermissionModel.objects.create(id=3, name='odd id mult 3') - r6 = RowLevelChangePermissionModel.objects.create(id=6, name='even id mult 3') - change_url_1 = reverse('admin:admin_views_rowlevelchangepermissionmodel_change', args=(r1.pk,)) - change_url_2 = reverse('admin:admin_views_rowlevelchangepermissionmodel_change', args=(r2.pk,)) - change_url_3 = reverse('admin:admin_views_rowlevelchangepermissionmodel_change', args=(r3.pk,)) - change_url_6 = reverse('admin:admin_views_rowlevelchangepermissionmodel_change', args=(r6.pk,)) - logins = [self.superuser, self.viewuser, self.adduser, self.changeuser, self.deleteuser] + r3 = RowLevelChangePermissionModel.objects.create(id=3, name="odd id mult 3") + r6 = RowLevelChangePermissionModel.objects.create(id=6, name="even id mult 3") + change_url_1 = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_change", args=(r1.pk,) + ) + change_url_2 = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_change", args=(r2.pk,) + ) + change_url_3 = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_change", args=(r3.pk,) + ) + change_url_6 = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_change", args=(r6.pk,) + ) + logins = [ + self.superuser, + self.viewuser, + self.adduser, + self.changeuser, + self.deleteuser, + ] for login_user in logins: with self.subTest(login_user.username): self.client.force_login(login_user) response = self.client.get(change_url_1) self.assertEqual(response.status_code, 403) - response = self.client.post(change_url_1, {'name': 'changed'}) - self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') + response = self.client.post(change_url_1, {"name": "changed"}) + self.assertEqual( + RowLevelChangePermissionModel.objects.get(id=1).name, "odd id" + ) self.assertEqual(response.status_code, 403) response = self.client.get(change_url_2) self.assertEqual(response.status_code, 200) - response = self.client.post(change_url_2, {'name': 'changed'}) - self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') + response = self.client.post(change_url_2, {"name": "changed"}) + self.assertEqual( + RowLevelChangePermissionModel.objects.get(id=2).name, "changed" + ) self.assertRedirects(response, self.index_url) response = self.client.get(change_url_3) self.assertEqual(response.status_code, 200) - response = self.client.post(change_url_3, {'name': 'changed'}) + response = self.client.post(change_url_3, {"name": "changed"}) self.assertEqual(response.status_code, 403) - self.assertEqual(RowLevelChangePermissionModel.objects.get(id=3).name, 'odd id mult 3') + self.assertEqual( + RowLevelChangePermissionModel.objects.get(id=3).name, + "odd id mult 3", + ) response = self.client.get(change_url_6) self.assertEqual(response.status_code, 200) - response = self.client.post(change_url_6, {'name': 'changed'}) - self.assertEqual(RowLevelChangePermissionModel.objects.get(id=6).name, 'changed') + response = self.client.post(change_url_6, {"name": "changed"}) + self.assertEqual( + RowLevelChangePermissionModel.objects.get(id=6).name, "changed" + ) self.assertRedirects(response, self.index_url) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) for login_user in [self.joepublicuser, self.nostaffuser]: with self.subTest(login_user.username): self.client.force_login(login_user) response = self.client.get(change_url_1, follow=True) - self.assertContains(response, 'login-form') - response = self.client.post(change_url_1, {'name': 'changed'}, follow=True) - self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') - self.assertContains(response, 'login-form') + self.assertContains(response, "login-form") + response = self.client.post( + change_url_1, {"name": "changed"}, follow=True + ) + self.assertEqual( + RowLevelChangePermissionModel.objects.get(id=1).name, "odd id" + ) + self.assertContains(response, "login-form") response = self.client.get(change_url_2, follow=True) - self.assertContains(response, 'login-form') - response = self.client.post(change_url_2, {'name': 'changed again'}, follow=True) - self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') - self.assertContains(response, 'login-form') - self.client.get(reverse('admin:logout')) + self.assertContains(response, "login-form") + response = self.client.post( + change_url_2, {"name": "changed again"}, follow=True + ) + self.assertEqual( + RowLevelChangePermissionModel.objects.get(id=2).name, "changed" + ) + self.assertContains(response, "login-form") + self.client.get(reverse("admin:logout")) def test_change_view_without_object_change_permission(self): """ The object should be read-only if the user has permission to view it and change objects of that type but not to change the current object. """ - change_url = reverse('admin9:admin_views_article_change', args=(self.a1.pk,)) + change_url = reverse("admin9:admin_views_article_change", args=(self.a1.pk,)) self.client.force_login(self.viewuser) response = self.client.get(change_url) - self.assertEqual(response.context['title'], 'View article') - self.assertContains(response, '<title>View article | Django site admin</title>') - self.assertContains(response, '<h1>View article</h1>') - self.assertContains(response, '<a href="/test_admin/admin9/admin_views/article/" class="closelink">Close</a>') + self.assertEqual(response.context["title"], "View article") + self.assertContains(response, "<title>View article | Django site admin</title>") + self.assertContains(response, "<h1>View article</h1>") + self.assertContains( + response, + '<a href="/test_admin/admin9/admin_views/article/" class="closelink">Close</a>', + ) def test_change_view_save_as_new(self): """ @@ -2063,13 +2528,16 @@ class AdminViewPermissionsTest(TestCase): permission. """ change_dict_save_as_new = { - '_saveasnew': 'Save as new', - 'title': 'Ikke fordømt', - 'content': '<p>edited article</p>', - 'date_0': '2008-03-18', 'date_1': '10:54:39', - 'section': self.s1.pk, + "_saveasnew": "Save as new", + "title": "Ikke fordømt", + "content": "<p>edited article</p>", + "date_0": "2008-03-18", + "date_1": "10:54:39", + "section": self.s1.pk, } - article_change_url = reverse('admin:admin_views_article_change', args=(self.a1.pk,)) + article_change_url = reverse( + "admin:admin_views_article_change", args=(self.a1.pk,) + ) # Add user can perform "Save as new". article_count = Article.objects.count() @@ -2092,103 +2560,131 @@ class AdminViewPermissionsTest(TestCase): self.client.force_login(self.superuser) post = self.client.post(article_change_url, change_dict_save_as_new) self.assertEqual(Article.objects.count(), article_count + 1) - new_article = Article.objects.latest('id') - self.assertRedirects(post, reverse('admin:admin_views_article_change', args=(new_article.pk,))) + new_article = Article.objects.latest("id") + self.assertRedirects( + post, reverse("admin:admin_views_article_change", args=(new_article.pk,)) + ) def test_change_view_with_view_only_inlines(self): """ User with change permission to a section but view-only for inlines. """ - self.viewuser.user_permissions.add(get_perm(Section, get_permission_codename('change', Section._meta))) + self.viewuser.user_permissions.add( + get_perm(Section, get_permission_codename("change", Section._meta)) + ) self.client.force_login(self.viewuser) # GET shows inlines. - response = self.client.get(reverse('admin:admin_views_section_change', args=(self.s1.pk,))) - self.assertEqual(len(response.context['inline_admin_formsets']), 1) - formset = response.context['inline_admin_formsets'][0] + response = self.client.get( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)) + ) + self.assertEqual(len(response.context["inline_admin_formsets"]), 1) + formset = response.context["inline_admin_formsets"][0] self.assertEqual(len(formset.forms), 3) # Valid POST changes the name. data = { - 'name': 'Can edit name with view-only inlines', - 'article_set-TOTAL_FORMS': 3, - 'article_set-INITIAL_FORMS': 3 + "name": "Can edit name with view-only inlines", + "article_set-TOTAL_FORMS": 3, + "article_set-INITIAL_FORMS": 3, } - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), data) - self.assertRedirects(response, reverse('admin:admin_views_section_changelist')) - self.assertEqual(Section.objects.get(pk=self.s1.pk).name, data['name']) + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), data + ) + self.assertRedirects(response, reverse("admin:admin_views_section_changelist")) + self.assertEqual(Section.objects.get(pk=self.s1.pk).name, data["name"]) # Invalid POST reshows inlines. - del data['name'] - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), data) + del data["name"] + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), data + ) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['inline_admin_formsets']), 1) - formset = response.context['inline_admin_formsets'][0] + self.assertEqual(len(response.context["inline_admin_formsets"]), 1) + formset = response.context["inline_admin_formsets"][0] self.assertEqual(len(formset.forms), 3) def test_change_view_with_view_and_add_inlines(self): """User has view and add permissions on the inline model.""" - self.viewuser.user_permissions.add(get_perm(Section, get_permission_codename('change', Section._meta))) - self.viewuser.user_permissions.add(get_perm(Article, get_permission_codename('add', Article._meta))) + self.viewuser.user_permissions.add( + get_perm(Section, get_permission_codename("change", Section._meta)) + ) + self.viewuser.user_permissions.add( + get_perm(Article, get_permission_codename("add", Article._meta)) + ) self.client.force_login(self.viewuser) # GET shows inlines. - response = self.client.get(reverse('admin:admin_views_section_change', args=(self.s1.pk,))) - self.assertEqual(len(response.context['inline_admin_formsets']), 1) - formset = response.context['inline_admin_formsets'][0] + response = self.client.get( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)) + ) + self.assertEqual(len(response.context["inline_admin_formsets"]), 1) + formset = response.context["inline_admin_formsets"][0] self.assertEqual(len(formset.forms), 6) # Valid POST creates a new article. data = { - 'name': 'Can edit name with view-only inlines', - 'article_set-TOTAL_FORMS': 6, - 'article_set-INITIAL_FORMS': 3, - 'article_set-3-id': [''], - 'article_set-3-title': ['A title'], - 'article_set-3-content': ['Added content'], - 'article_set-3-date_0': ['2008-3-18'], - 'article_set-3-date_1': ['11:54:58'], - 'article_set-3-section': [str(self.s1.pk)], + "name": "Can edit name with view-only inlines", + "article_set-TOTAL_FORMS": 6, + "article_set-INITIAL_FORMS": 3, + "article_set-3-id": [""], + "article_set-3-title": ["A title"], + "article_set-3-content": ["Added content"], + "article_set-3-date_0": ["2008-3-18"], + "article_set-3-date_1": ["11:54:58"], + "article_set-3-section": [str(self.s1.pk)], } - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), data) - self.assertRedirects(response, reverse('admin:admin_views_section_changelist')) - self.assertEqual(Section.objects.get(pk=self.s1.pk).name, data['name']) + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), data + ) + self.assertRedirects(response, reverse("admin:admin_views_section_changelist")) + self.assertEqual(Section.objects.get(pk=self.s1.pk).name, data["name"]) self.assertEqual(Article.objects.count(), 4) # Invalid POST reshows inlines. - del data['name'] - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), data) + del data["name"] + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), data + ) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['inline_admin_formsets']), 1) - formset = response.context['inline_admin_formsets'][0] + self.assertEqual(len(response.context["inline_admin_formsets"]), 1) + formset = response.context["inline_admin_formsets"][0] self.assertEqual(len(formset.forms), 6) def test_change_view_with_view_and_delete_inlines(self): """User has view and delete permissions on the inline model.""" - self.viewuser.user_permissions.add(get_perm(Section, get_permission_codename('change', Section._meta))) + self.viewuser.user_permissions.add( + get_perm(Section, get_permission_codename("change", Section._meta)) + ) self.client.force_login(self.viewuser) data = { - 'name': 'Name is required.', - 'article_set-TOTAL_FORMS': 6, - 'article_set-INITIAL_FORMS': 3, - 'article_set-0-id': [str(self.a1.pk)], - 'article_set-0-DELETE': ['on'], + "name": "Name is required.", + "article_set-TOTAL_FORMS": 6, + "article_set-INITIAL_FORMS": 3, + "article_set-0-id": [str(self.a1.pk)], + "article_set-0-DELETE": ["on"], } # Inline POST details are ignored without delete permission. - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), data) - self.assertRedirects(response, reverse('admin:admin_views_section_changelist')) + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), data + ) + self.assertRedirects(response, reverse("admin:admin_views_section_changelist")) self.assertEqual(Article.objects.count(), 3) # Deletion successful when delete permission is added. - self.viewuser.user_permissions.add(get_perm(Article, get_permission_codename('delete', Article._meta))) + self.viewuser.user_permissions.add( + get_perm(Article, get_permission_codename("delete", Article._meta)) + ) data = { - 'name': 'Name is required.', - 'article_set-TOTAL_FORMS': 6, - 'article_set-INITIAL_FORMS': 3, - 'article_set-0-id': [str(self.a1.pk)], - 'article_set-0-DELETE': ['on'], + "name": "Name is required.", + "article_set-TOTAL_FORMS": 6, + "article_set-INITIAL_FORMS": 3, + "article_set-0-id": [str(self.a1.pk)], + "article_set-0-DELETE": ["on"], } - response = self.client.post(reverse('admin:admin_views_section_change', args=(self.s1.pk,)), data) - self.assertRedirects(response, reverse('admin:admin_views_section_changelist')) + response = self.client.post( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)), data + ) + self.assertRedirects(response, reverse("admin:admin_views_section_changelist")) self.assertEqual(Article.objects.count(), 2) def test_delete_view(self): """Delete view should restrict access and actually delete items.""" - delete_dict = {'post': 'yes'} - delete_url = reverse('admin:admin_views_article_delete', args=(self.a1.pk,)) + delete_dict = {"post": "yes"} + delete_url = reverse("admin:admin_views_article_delete", args=(self.a1.pk,)) # add user should not be able to delete articles self.client.force_login(self.adduser) @@ -2210,7 +2706,9 @@ class AdminViewPermissionsTest(TestCase): # Delete user can delete self.client.force_login(self.deleteuser) - response = self.client.get(reverse('admin:admin_views_section_delete', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_section_delete", args=(self.s1.pk,)) + ) self.assertContains(response, "<h2>Summary</h2>") self.assertContains(response, "<li>Articles: 3</li>") # test response contains link to related Article @@ -2224,7 +2722,7 @@ class AdminViewPermissionsTest(TestCase): self.assertRedirects(post, self.index_url) self.assertEqual(Article.objects.count(), 2) self.assertEqual(len(mail.outbox), 1) - self.assertEqual(mail.outbox[0].subject, 'Greetings from a deleted object') + self.assertEqual(mail.outbox[0].subject, "Greetings from a deleted object") article_ct = ContentType.objects.get_for_model(Article) logged = LogEntry.objects.get(content_type=article_ct, action_flag=DELETION) self.assertEqual(logged.object_id, str(self.a1.pk)) @@ -2234,82 +2732,110 @@ class AdminViewPermissionsTest(TestCase): The delete view allows users to delete collected objects without a 'delete' permission (ReadOnlyPizza.Meta.default_permissions is empty). """ - pizza = ReadOnlyPizza.objects.create(name='Double Cheese') - delete_url = reverse('admin:admin_views_readonlypizza_delete', args=(pizza.pk,)) + pizza = ReadOnlyPizza.objects.create(name="Double Cheese") + delete_url = reverse("admin:admin_views_readonlypizza_delete", args=(pizza.pk,)) self.client.force_login(self.adduser) response = self.client.get(delete_url) - self.assertContains(response, 'admin_views/readonlypizza/%s/' % pizza.pk) - self.assertContains(response, '<h2>Summary</h2>') - self.assertContains(response, '<li>Read only pizzas: 1</li>') - post = self.client.post(delete_url, {'post': 'yes'}) - self.assertRedirects(post, reverse('admin:admin_views_readonlypizza_changelist')) + self.assertContains(response, "admin_views/readonlypizza/%s/" % pizza.pk) + self.assertContains(response, "<h2>Summary</h2>") + self.assertContains(response, "<li>Read only pizzas: 1</li>") + post = self.client.post(delete_url, {"post": "yes"}) + self.assertRedirects( + post, reverse("admin:admin_views_readonlypizza_changelist") + ) self.assertEqual(ReadOnlyPizza.objects.count(), 0) def test_delete_view_nonexistent_obj(self): self.client.force_login(self.deleteuser) - url = reverse('admin:admin_views_article_delete', args=('nonexistent',)) + url = reverse("admin:admin_views_article_delete", args=("nonexistent",)) response = self.client.get(url, follow=True) - self.assertRedirects(response, reverse('admin:index')) + self.assertRedirects(response, reverse("admin:index")) self.assertEqual( - [m.message for m in response.context['messages']], - ['article with ID “nonexistent” doesn’t exist. Perhaps it was deleted?'] + [m.message for m in response.context["messages"]], + ["article with ID “nonexistent” doesn’t exist. Perhaps it was deleted?"], ) def test_history_view(self): """History view should restrict access.""" # add user should not be able to view the list of article or change any of them self.client.force_login(self.adduser) - response = self.client.get(reverse('admin:admin_views_article_history', args=(self.a1.pk,))) + response = self.client.get( + reverse("admin:admin_views_article_history", args=(self.a1.pk,)) + ) self.assertEqual(response.status_code, 403) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # view user can view all items self.client.force_login(self.viewuser) - response = self.client.get(reverse('admin:admin_views_article_history', args=(self.a1.pk,))) + response = self.client.get( + reverse("admin:admin_views_article_history", args=(self.a1.pk,)) + ) self.assertEqual(response.status_code, 200) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) # change user can view all items and edit them self.client.force_login(self.changeuser) - response = self.client.get(reverse('admin:admin_views_article_history', args=(self.a1.pk,))) + response = self.client.get( + reverse("admin:admin_views_article_history", args=(self.a1.pk,)) + ) self.assertEqual(response.status_code, 200) # Test redirection when using row-level change permissions. Refs #11513. rl1 = RowLevelChangePermissionModel.objects.create(id=1, name="odd id") rl2 = RowLevelChangePermissionModel.objects.create(id=2, name="even id") - logins = [self.superuser, self.viewuser, self.adduser, self.changeuser, self.deleteuser] + logins = [ + self.superuser, + self.viewuser, + self.adduser, + self.changeuser, + self.deleteuser, + ] for login_user in logins: with self.subTest(login_user.username): self.client.force_login(login_user) - url = reverse('admin:admin_views_rowlevelchangepermissionmodel_history', args=(rl1.pk,)) + url = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_history", + args=(rl1.pk,), + ) response = self.client.get(url) self.assertEqual(response.status_code, 403) - url = reverse('admin:admin_views_rowlevelchangepermissionmodel_history', args=(rl2.pk,)) + url = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_history", + args=(rl2.pk,), + ) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) for login_user in [self.joepublicuser, self.nostaffuser]: with self.subTest(login_user.username): self.client.force_login(login_user) - url = reverse('admin:admin_views_rowlevelchangepermissionmodel_history', args=(rl1.pk,)) + url = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_history", + args=(rl1.pk,), + ) response = self.client.get(url, follow=True) - self.assertContains(response, 'login-form') - url = reverse('admin:admin_views_rowlevelchangepermissionmodel_history', args=(rl2.pk,)) + self.assertContains(response, "login-form") + url = reverse( + "admin:admin_views_rowlevelchangepermissionmodel_history", + args=(rl2.pk,), + ) response = self.client.get(url, follow=True) - self.assertContains(response, 'login-form') + self.assertContains(response, "login-form") - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) def test_history_view_bad_url(self): self.client.force_login(self.changeuser) - response = self.client.get(reverse('admin:admin_views_article_history', args=('foo',)), follow=True) - self.assertRedirects(response, reverse('admin:index')) + response = self.client.get( + reverse("admin:admin_views_article_history", args=("foo",)), follow=True + ) + self.assertRedirects(response, reverse("admin:index")) self.assertEqual( - [m.message for m in response.context['messages']], - ['article with ID “foo” doesn’t exist. Perhaps it was deleted?'] + [m.message for m in response.context["messages"]], + ["article with ID “foo” doesn’t exist. Perhaps it was deleted?"], ) def test_conditionally_show_add_section_link(self): @@ -2319,13 +2845,13 @@ class AdminViewPermissionsTest(TestCase): """ self.client.force_login(self.adduser) # The user can't add sections yet, so they shouldn't see the "add section" link. - url = reverse('admin:admin_views_article_add') - add_link_text = 'add_id_section' + url = reverse("admin:admin_views_article_add") + add_link_text = "add_id_section" response = self.client.get(url) self.assertNotContains(response, add_link_text) # Allow the user to add sections too. Now they can see the "add section" link. - user = User.objects.get(username='adduser') - perm = get_perm(Section, get_permission_codename('add', Section._meta)) + user = User.objects.get(username="adduser") + perm = get_perm(Section, get_permission_codename("add", Section._meta)) user.user_permissions.add(perm) response = self.client.get(url) self.assertContains(response, add_link_text) @@ -2335,19 +2861,24 @@ class AdminViewPermissionsTest(TestCase): The foreign key widget should only show the "change related" button if the user has permission to change that related item. """ + def get_change_related(response): - return response.context['adminform'].form.fields['section'].widget.can_change_related + return ( + response.context["adminform"] + .form.fields["section"] + .widget.can_change_related + ) self.client.force_login(self.adduser) # The user can't change sections yet, so they shouldn't see the "change section" link. - url = reverse('admin:admin_views_article_add') - change_link_text = 'change_id_section' + url = reverse("admin:admin_views_article_add") + change_link_text = "change_id_section" response = self.client.get(url) self.assertFalse(get_change_related(response)) self.assertNotContains(response, change_link_text) # Allow the user to change sections too. Now they can see the "change section" link. - user = User.objects.get(username='adduser') - perm = get_perm(Section, get_permission_codename('change', Section._meta)) + user = User.objects.get(username="adduser") + perm = get_perm(Section, get_permission_codename("change", Section._meta)) user.user_permissions.add(perm) response = self.client.get(url) self.assertTrue(get_change_related(response)) @@ -2358,19 +2889,24 @@ class AdminViewPermissionsTest(TestCase): The foreign key widget should only show the "delete related" button if the user has permission to delete that related item. """ + def get_delete_related(response): - return response.context['adminform'].form.fields['sub_section'].widget.can_delete_related + return ( + response.context["adminform"] + .form.fields["sub_section"] + .widget.can_delete_related + ) self.client.force_login(self.adduser) # The user can't delete sections yet, so they shouldn't see the "delete section" link. - url = reverse('admin:admin_views_article_add') - delete_link_text = 'delete_id_sub_section' + url = reverse("admin:admin_views_article_add") + delete_link_text = "delete_id_sub_section" response = self.client.get(url) self.assertFalse(get_delete_related(response)) self.assertNotContains(response, delete_link_text) # Allow the user to delete sections too. Now they can see the "delete section" link. - user = User.objects.get(username='adduser') - perm = get_perm(Section, get_permission_codename('delete', Section._meta)) + user = User.objects.get(username="adduser") + perm = get_perm(Section, get_permission_codename("delete", Section._meta)) user.user_permissions.add(perm) response = self.client.get(url) self.assertTrue(get_delete_related(response)) @@ -2378,28 +2914,28 @@ class AdminViewPermissionsTest(TestCase): def test_disabled_permissions_when_logged_in(self): self.client.force_login(self.superuser) - superuser = User.objects.get(username='super') + superuser = User.objects.get(username="super") superuser.is_active = False superuser.save() response = self.client.get(self.index_url, follow=True) self.assertContains(response, 'id="login-form"') - self.assertNotContains(response, 'Log out') + self.assertNotContains(response, "Log out") - response = self.client.get(reverse('secure_view'), follow=True) + response = self.client.get(reverse("secure_view"), follow=True) self.assertContains(response, 'id="login-form"') def test_disabled_staff_permissions_when_logged_in(self): self.client.force_login(self.superuser) - superuser = User.objects.get(username='super') + superuser = User.objects.get(username="super") superuser.is_staff = False superuser.save() response = self.client.get(self.index_url, follow=True) self.assertContains(response, 'id="login-form"') - self.assertNotContains(response, 'Log out') + self.assertNotContains(response, "Log out") - response = self.client.get(reverse('secure_view'), follow=True) + response = self.client.get(reverse("secure_view"), follow=True) self.assertContains(response, 'id="login-form"') def test_app_list_permissions(self): @@ -2407,19 +2943,19 @@ class AdminViewPermissionsTest(TestCase): If a user has no module perms, the app list returns a 404. """ opts = Article._meta - change_user = User.objects.get(username='changeuser') - permission = get_perm(Article, get_permission_codename('change', opts)) + change_user = User.objects.get(username="changeuser") + permission = get_perm(Article, get_permission_codename("change", opts)) self.client.force_login(self.changeuser) # the user has no module permissions change_user.user_permissions.remove(permission) - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertEqual(response.status_code, 404) # the user now has module permissions change_user.user_permissions.add(permission) - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertEqual(response.status_code, 200) def test_shortcut_view_only_available_to_staff(self): @@ -2427,12 +2963,12 @@ class AdminViewPermissionsTest(TestCase): Only admin users should be able to use the admin shortcut view. """ model_ctype = ContentType.objects.get_for_model(ModelWithStringPrimaryKey) - obj = ModelWithStringPrimaryKey.objects.create(string_pk='foo') - shortcut_url = reverse('admin:view_on_site', args=(model_ctype.pk, obj.pk)) + obj = ModelWithStringPrimaryKey.objects.create(string_pk="foo") + shortcut_url = reverse("admin:view_on_site", args=(model_ctype.pk, obj.pk)) # Not logged in: we should see the login page. response = self.client.get(shortcut_url, follow=True) - self.assertTemplateUsed(response, 'admin/login.html') + self.assertTemplateUsed(response, "admin/login.html") # Logged in? Redirect. self.client.force_login(self.superuser) @@ -2440,7 +2976,7 @@ class AdminViewPermissionsTest(TestCase): # Can't use self.assertRedirects() because User.get_absolute_url() is silly. self.assertEqual(response.status_code, 302) # Domain may depend on contrib.sites tests also run - self.assertRegex(response.url, 'http://(testserver|example.com)/dummy/foo/') + self.assertRegex(response.url, "http://(testserver|example.com)/dummy/foo/") def test_has_module_permission(self): """ @@ -2450,32 +2986,32 @@ class AdminViewPermissionsTest(TestCase): """ self.client.force_login(self.superuser) response = self.client.get(self.index_url) - self.assertContains(response, 'admin_views') - self.assertContains(response, 'Articles') + self.assertContains(response, "admin_views") + self.assertContains(response, "Articles") self.client.logout() self.client.force_login(self.viewuser) response = self.client.get(self.index_url) - self.assertContains(response, 'admin_views') - self.assertContains(response, 'Articles') + self.assertContains(response, "admin_views") + self.assertContains(response, "Articles") self.client.logout() self.client.force_login(self.adduser) response = self.client.get(self.index_url) - self.assertContains(response, 'admin_views') - self.assertContains(response, 'Articles') + self.assertContains(response, "admin_views") + self.assertContains(response, "Articles") self.client.logout() self.client.force_login(self.changeuser) response = self.client.get(self.index_url) - self.assertContains(response, 'admin_views') - self.assertContains(response, 'Articles') + self.assertContains(response, "admin_views") + self.assertContains(response, "Articles") self.client.logout() self.client.force_login(self.deleteuser) response = self.client.get(self.index_url) - self.assertContains(response, 'admin_views') - self.assertContains(response, 'Articles') + self.assertContains(response, "admin_views") + self.assertContains(response, "Articles") def test_overriding_has_module_permission(self): """ @@ -2484,7 +3020,7 @@ class AdminViewPermissionsTest(TestCase): """ articles = Article._meta.verbose_name_plural.title() sections = Section._meta.verbose_name_plural.title() - index_url = reverse('admin7:index') + index_url = reverse("admin7:index") self.client.force_login(self.superuser) response = self.client.get(index_url) @@ -2494,19 +3030,19 @@ class AdminViewPermissionsTest(TestCase): self.client.force_login(self.viewuser) response = self.client.get(index_url) - self.assertNotContains(response, 'admin_views') + self.assertNotContains(response, "admin_views") self.assertNotContains(response, articles) self.client.logout() self.client.force_login(self.adduser) response = self.client.get(index_url) - self.assertNotContains(response, 'admin_views') + self.assertNotContains(response, "admin_views") self.assertNotContains(response, articles) self.client.logout() self.client.force_login(self.changeuser) response = self.client.get(index_url) - self.assertNotContains(response, 'admin_views') + self.assertNotContains(response, "admin_views") self.assertNotContains(response, articles) self.client.logout() @@ -2517,7 +3053,7 @@ class AdminViewPermissionsTest(TestCase): # The app list displays Sections but not Articles as the latter has # ModelAdmin.has_module_permission() = False. self.client.force_login(self.superuser) - response = self.client.get(reverse('admin7:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin7:app_list", args=("admin_views",))) self.assertContains(response, sections) self.assertNotContains(response, articles) @@ -2535,140 +3071,195 @@ class AdminViewPermissionsTest(TestCase): "date_1": "16:35:00", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_article_add'), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_article_add"), post_data, follow=True + ) self.assertContains( response, '<li class="success">The article “Fun & games” was added successfully.</li>', - html=True + html=True, ) @override_settings( - ROOT_URLCONF='admin_views.urls', - TEMPLATES=[{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }], + ROOT_URLCONF="admin_views.urls", + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ], ) class AdminViewProxyModelPermissionsTests(TestCase): """Tests for proxy models permissions in the admin.""" @classmethod def setUpTestData(cls): - cls.viewuser = User.objects.create_user(username='viewuser', password='secret', is_staff=True) - cls.adduser = User.objects.create_user(username='adduser', password='secret', is_staff=True) - cls.changeuser = User.objects.create_user(username='changeuser', password='secret', is_staff=True) - cls.deleteuser = User.objects.create_user(username='deleteuser', password='secret', is_staff=True) + cls.viewuser = User.objects.create_user( + username="viewuser", password="secret", is_staff=True + ) + cls.adduser = User.objects.create_user( + username="adduser", password="secret", is_staff=True + ) + cls.changeuser = User.objects.create_user( + username="changeuser", password="secret", is_staff=True + ) + cls.deleteuser = User.objects.create_user( + username="deleteuser", password="secret", is_staff=True + ) # Setup permissions. opts = UserProxy._meta - cls.viewuser.user_permissions.add(get_perm(UserProxy, get_permission_codename('view', opts))) - cls.adduser.user_permissions.add(get_perm(UserProxy, get_permission_codename('add', opts))) - cls.changeuser.user_permissions.add(get_perm(UserProxy, get_permission_codename('change', opts))) - cls.deleteuser.user_permissions.add(get_perm(UserProxy, get_permission_codename('delete', opts))) + cls.viewuser.user_permissions.add( + get_perm(UserProxy, get_permission_codename("view", opts)) + ) + cls.adduser.user_permissions.add( + get_perm(UserProxy, get_permission_codename("add", opts)) + ) + cls.changeuser.user_permissions.add( + get_perm(UserProxy, get_permission_codename("change", opts)) + ) + cls.deleteuser.user_permissions.add( + get_perm(UserProxy, get_permission_codename("delete", opts)) + ) # UserProxy instances. - cls.user_proxy = UserProxy.objects.create(username='user_proxy', password='secret') + cls.user_proxy = UserProxy.objects.create( + username="user_proxy", password="secret" + ) def test_add(self): self.client.force_login(self.adduser) - url = reverse('admin:admin_views_userproxy_add') + url = reverse("admin:admin_views_userproxy_add") data = { - 'username': 'can_add', - 'password': 'secret', - 'date_joined_0': '2019-01-15', - 'date_joined_1': '16:59:10', + "username": "can_add", + "password": "secret", + "date_joined_0": "2019-01-15", + "date_joined_1": "16:59:10", } response = self.client.post(url, data, follow=True) self.assertEqual(response.status_code, 200) - self.assertTrue(UserProxy.objects.filter(username='can_add').exists()) + self.assertTrue(UserProxy.objects.filter(username="can_add").exists()) def test_view(self): self.client.force_login(self.viewuser) - response = self.client.get(reverse('admin:admin_views_userproxy_changelist')) - self.assertContains(response, '<h1>Select user proxy to view</h1>') - response = self.client.get(reverse('admin:admin_views_userproxy_change', args=(self.user_proxy.pk,))) - self.assertContains(response, '<h1>View user proxy</h1>') + response = self.client.get(reverse("admin:admin_views_userproxy_changelist")) + self.assertContains(response, "<h1>Select user proxy to view</h1>") + response = self.client.get( + reverse("admin:admin_views_userproxy_change", args=(self.user_proxy.pk,)) + ) + self.assertContains(response, "<h1>View user proxy</h1>") self.assertContains(response, '<div class="readonly">user_proxy</div>') def test_change(self): self.client.force_login(self.changeuser) data = { - 'password': self.user_proxy.password, - 'username': self.user_proxy.username, - 'date_joined_0': self.user_proxy.date_joined.strftime('%Y-%m-%d'), - 'date_joined_1': self.user_proxy.date_joined.strftime('%H:%M:%S'), - 'first_name': 'first_name', + "password": self.user_proxy.password, + "username": self.user_proxy.username, + "date_joined_0": self.user_proxy.date_joined.strftime("%Y-%m-%d"), + "date_joined_1": self.user_proxy.date_joined.strftime("%H:%M:%S"), + "first_name": "first_name", } - url = reverse('admin:admin_views_userproxy_change', args=(self.user_proxy.pk,)) + url = reverse("admin:admin_views_userproxy_change", args=(self.user_proxy.pk,)) response = self.client.post(url, data) - self.assertRedirects(response, reverse('admin:admin_views_userproxy_changelist')) - self.assertEqual(UserProxy.objects.get(pk=self.user_proxy.pk).first_name, 'first_name') + self.assertRedirects( + response, reverse("admin:admin_views_userproxy_changelist") + ) + self.assertEqual( + UserProxy.objects.get(pk=self.user_proxy.pk).first_name, "first_name" + ) def test_delete(self): self.client.force_login(self.deleteuser) - url = reverse('admin:admin_views_userproxy_delete', args=(self.user_proxy.pk,)) - response = self.client.post(url, {'post': 'yes'}, follow=True) + url = reverse("admin:admin_views_userproxy_delete", args=(self.user_proxy.pk,)) + response = self.client.post(url, {"post": "yes"}, follow=True) self.assertEqual(response.status_code, 200) self.assertFalse(UserProxy.objects.filter(pk=self.user_proxy.pk).exists()) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewsNoUrlTest(TestCase): """Regression test for #17333""" @classmethod def setUpTestData(cls): # User who can change Reports - cls.changeuser = User.objects.create_user(username='changeuser', password='secret', is_staff=True) - cls.changeuser.user_permissions.add(get_perm(Report, get_permission_codename('change', Report._meta))) + cls.changeuser = User.objects.create_user( + username="changeuser", password="secret", is_staff=True + ) + cls.changeuser.user_permissions.add( + get_perm(Report, get_permission_codename("change", Report._meta)) + ) def test_no_standard_modeladmin_urls(self): """Admin index views don't break when user's ModelAdmin removes standard urls""" self.client.force_login(self.changeuser) - r = self.client.get(reverse('admin:index')) + r = self.client.get(reverse("admin:index")) # we shouldn't get a 500 error caused by a NoReverseMatch self.assertEqual(r.status_code, 200) - self.client.get(reverse('admin:logout')) + self.client.get(reverse("admin:logout")) -@skipUnlessDBFeature('can_defer_constraint_checks') -@override_settings(ROOT_URLCONF='admin_views.urls') +@skipUnlessDBFeature("can_defer_constraint_checks") +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewDeletedObjectsTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.deleteuser = User.objects.create_user(username='deleteuser', password='secret', is_staff=True) - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.deleteuser = User.objects.create_user( + username="deleteuser", password="secret", is_staff=True + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 - ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') - - cls.v1 = Villain.objects.create(name='Adam') - cls.v2 = Villain.objects.create(name='Sue') - cls.sv1 = SuperVillain.objects.create(name='Bob') - cls.pl1 = Plot.objects.create(name='World Domination', team_leader=cls.v1, contact=cls.v2) - cls.pl2 = Plot.objects.create(name='World Peace', team_leader=cls.v2, contact=cls.v2) - cls.pl3 = Plot.objects.create(name='Corn Conspiracy', team_leader=cls.v1, contact=cls.v1) - cls.pd1 = PlotDetails.objects.create(details='almost finished', plot=cls.pl1) - cls.sh1 = SecretHideout.objects.create(location='underground bunker', villain=cls.v1) - cls.sh2 = SecretHideout.objects.create(location='floating castle', villain=cls.sv1) - cls.ssh1 = SuperSecretHideout.objects.create(location='super floating castle!', supervillain=cls.sv1) - cls.cy1 = CyclicOne.objects.create(name='I am recursive', two_id=1) - cls.cy2 = CyclicTwo.objects.create(name='I am recursive too', one_id=1) + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" + ) + + cls.v1 = Villain.objects.create(name="Adam") + cls.v2 = Villain.objects.create(name="Sue") + cls.sv1 = SuperVillain.objects.create(name="Bob") + cls.pl1 = Plot.objects.create( + name="World Domination", team_leader=cls.v1, contact=cls.v2 + ) + cls.pl2 = Plot.objects.create( + name="World Peace", team_leader=cls.v2, contact=cls.v2 + ) + cls.pl3 = Plot.objects.create( + name="Corn Conspiracy", team_leader=cls.v1, contact=cls.v1 + ) + cls.pd1 = PlotDetails.objects.create(details="almost finished", plot=cls.pl1) + cls.sh1 = SecretHideout.objects.create( + location="underground bunker", villain=cls.v1 + ) + cls.sh2 = SecretHideout.objects.create( + location="floating castle", villain=cls.sv1 + ) + cls.ssh1 = SuperSecretHideout.objects.create( + location="super floating castle!", supervillain=cls.sv1 + ) + cls.cy1 = CyclicOne.objects.create(name="I am recursive", two_id=1) + cls.cy2 = CyclicTwo.objects.create(name="I am recursive too", one_id=1) def setUp(self): self.client.force_login(self.superuser) @@ -2680,12 +3271,15 @@ class AdminViewDeletedObjectsTest(TestCase): """ pattern = re.compile( r'<li>Plot: <a href="%s">World Domination</a>\s*<ul>\s*' - r'<li>Plot details: <a href="%s">almost finished</a>' % ( - reverse('admin:admin_views_plot_change', args=(self.pl1.pk,)), - reverse('admin:admin_views_plotdetails_change', args=(self.pd1.pk,)), + r'<li>Plot details: <a href="%s">almost finished</a>' + % ( + reverse("admin:admin_views_plot_change", args=(self.pl1.pk,)), + reverse("admin:admin_views_plotdetails_change", args=(self.pd1.pk,)), ) ) - response = self.client.get(reverse('admin:admin_views_villain_delete', args=(self.v1.pk,))) + response = self.client.get( + reverse("admin:admin_views_villain_delete", args=(self.v1.pk,)) + ) self.assertRegex(response.content.decode(), pattern) def test_cyclic(self): @@ -2694,24 +3288,33 @@ class AdminViewDeletedObjectsTest(TestCase): listed once. """ one = '<li>Cyclic one: <a href="%s">I am recursive</a>' % ( - reverse('admin:admin_views_cyclicone_change', args=(self.cy1.pk,)), + reverse("admin:admin_views_cyclicone_change", args=(self.cy1.pk,)), ) two = '<li>Cyclic two: <a href="%s">I am recursive too</a>' % ( - reverse('admin:admin_views_cyclictwo_change', args=(self.cy2.pk,)), + reverse("admin:admin_views_cyclictwo_change", args=(self.cy2.pk,)), + ) + response = self.client.get( + reverse("admin:admin_views_cyclicone_delete", args=(self.cy1.pk,)) ) - response = self.client.get(reverse('admin:admin_views_cyclicone_delete', args=(self.cy1.pk,))) self.assertContains(response, one, 1) self.assertContains(response, two, 1) def test_perms_needed(self): self.client.logout() - delete_user = User.objects.get(username='deleteuser') - delete_user.user_permissions.add(get_perm(Plot, get_permission_codename('delete', Plot._meta))) + delete_user = User.objects.get(username="deleteuser") + delete_user.user_permissions.add( + get_perm(Plot, get_permission_codename("delete", Plot._meta)) + ) self.client.force_login(self.deleteuser) - response = self.client.get(reverse('admin:admin_views_plot_delete', args=(self.pl1.pk,))) - self.assertContains(response, "your account doesn't have permission to delete the following types of objects") + response = self.client.get( + reverse("admin:admin_views_plot_delete", args=(self.pl1.pk,)) + ) + self.assertContains( + response, + "your account doesn't have permission to delete the following types of objects", + ) self.assertContains(response, "<li>plot details</li>") def test_protected(self): @@ -2719,15 +3322,21 @@ class AdminViewDeletedObjectsTest(TestCase): a1 = Answer.objects.create(question=q, answer="Because.") a2 = Answer.objects.create(question=q, answer="Yes.") - response = self.client.get(reverse('admin:admin_views_question_delete', args=(q.pk,))) - self.assertContains(response, "would require deleting the following protected related objects") + response = self.client.get( + reverse("admin:admin_views_question_delete", args=(q.pk,)) + ) + self.assertContains( + response, "would require deleting the following protected related objects" + ) self.assertContains( response, - '<li>Answer: <a href="%s">Because.</a></li>' % reverse('admin:admin_views_answer_change', args=(a1.pk,)) + '<li>Answer: <a href="%s">Because.</a></li>' + % reverse("admin:admin_views_answer_change", args=(a1.pk,)), ) self.assertContains( response, - '<li>Answer: <a href="%s">Yes.</a></li>' % reverse('admin:admin_views_answer_change', args=(a2.pk,)) + '<li>Answer: <a href="%s">Yes.</a></li>' + % reverse("admin:admin_views_answer_change", args=(a2.pk,)), ) def test_post_delete_protected(self): @@ -2735,43 +3344,51 @@ class AdminViewDeletedObjectsTest(TestCase): A POST request to delete protected objects should display the page which says the deletion is prohibited. """ - q = Question.objects.create(question='Why?') - Answer.objects.create(question=q, answer='Because.') + q = Question.objects.create(question="Why?") + Answer.objects.create(question=q, answer="Because.") - response = self.client.post(reverse('admin:admin_views_question_delete', args=(q.pk,)), {'post': 'yes'}) + response = self.client.post( + reverse("admin:admin_views_question_delete", args=(q.pk,)), {"post": "yes"} + ) self.assertEqual(Question.objects.count(), 1) - self.assertContains(response, "would require deleting the following protected related objects") + self.assertContains( + response, "would require deleting the following protected related objects" + ) def test_restricted(self): - album = Album.objects.create(title='Amaryllis') - song = Song.objects.create(album=album, name='Unity') - response = self.client.get(reverse('admin:admin_views_album_delete', args=(album.pk,))) + album = Album.objects.create(title="Amaryllis") + song = Song.objects.create(album=album, name="Unity") + response = self.client.get( + reverse("admin:admin_views_album_delete", args=(album.pk,)) + ) self.assertContains( response, - 'would require deleting the following protected related objects', + "would require deleting the following protected related objects", ) self.assertContains( response, '<li>Song: <a href="%s">Unity</a></li>' - % reverse('admin:admin_views_song_change', args=(song.pk,)) + % reverse("admin:admin_views_song_change", args=(song.pk,)), ) def test_post_delete_restricted(self): - album = Album.objects.create(title='Amaryllis') - Song.objects.create(album=album, name='Unity') + album = Album.objects.create(title="Amaryllis") + Song.objects.create(album=album, name="Unity") response = self.client.post( - reverse('admin:admin_views_album_delete', args=(album.pk,)), - {'post': 'yes'}, + reverse("admin:admin_views_album_delete", args=(album.pk,)), + {"post": "yes"}, ) self.assertEqual(Album.objects.count(), 1) self.assertContains( response, - 'would require deleting the following protected related objects', + "would require deleting the following protected related objects", ) def test_not_registered(self): should_contain = """<li>Secret hideout: underground bunker""" - response = self.client.get(reverse('admin:admin_views_villain_delete', args=(self.v1.pk,))) + response = self.client.get( + reverse("admin:admin_views_villain_delete", args=(self.v1.pk,)) + ) self.assertContains(response, should_contain, 1) def test_multiple_fkeys_to_same_model(self): @@ -2781,11 +3398,15 @@ class AdminViewDeletedObjectsTest(TestCase): objects to delete. """ should_contain = '<li>Plot: <a href="%s">World Domination</a>' % reverse( - 'admin:admin_views_plot_change', args=(self.pl1.pk,) + "admin:admin_views_plot_change", args=(self.pl1.pk,) + ) + response = self.client.get( + reverse("admin:admin_views_villain_delete", args=(self.v1.pk,)) ) - response = self.client.get(reverse('admin:admin_views_villain_delete', args=(self.v1.pk,))) self.assertContains(response, should_contain) - response = self.client.get(reverse('admin:admin_views_villain_delete', args=(self.v2.pk,))) + response = self.client.get( + reverse("admin:admin_views_villain_delete", args=(self.v2.pk,)) + ) self.assertContains(response, should_contain) def test_multiple_fkeys_to_same_instance(self): @@ -2795,9 +3416,11 @@ class AdminViewDeletedObjectsTest(TestCase): once. """ should_contain = '<li>Plot: <a href="%s">World Peace</a></li>' % reverse( - 'admin:admin_views_plot_change', args=(self.pl2.pk,) + "admin:admin_views_plot_change", args=(self.pl2.pk,) + ) + response = self.client.get( + reverse("admin:admin_views_villain_delete", args=(self.v2.pk,)) ) - response = self.client.get(reverse('admin:admin_views_villain_delete', args=(self.v2.pk,))) self.assertContains(response, should_contain, 1) def test_inheritance(self): @@ -2807,17 +3430,21 @@ class AdminViewDeletedObjectsTest(TestCase): for deletion, as well as any relationships they have. """ should_contain = [ - '<li>Villain: <a href="%s">Bob</a>' % reverse('admin:admin_views_villain_change', args=(self.sv1.pk,)), - '<li>Super villain: <a href="%s">Bob</a>' % reverse( - 'admin:admin_views_supervillain_change', args=(self.sv1.pk,) - ), - '<li>Secret hideout: floating castle', - '<li>Super secret hideout: super floating castle!', + '<li>Villain: <a href="%s">Bob</a>' + % reverse("admin:admin_views_villain_change", args=(self.sv1.pk,)), + '<li>Super villain: <a href="%s">Bob</a>' + % reverse("admin:admin_views_supervillain_change", args=(self.sv1.pk,)), + "<li>Secret hideout: floating castle", + "<li>Super secret hideout: super floating castle!", ] - response = self.client.get(reverse('admin:admin_views_villain_delete', args=(self.sv1.pk,))) + response = self.client.get( + reverse("admin:admin_views_villain_delete", args=(self.sv1.pk,)) + ) for should in should_contain: self.assertContains(response, should, 1) - response = self.client.get(reverse('admin:admin_views_supervillain_delete', args=(self.sv1.pk,))) + response = self.client.get( + reverse("admin:admin_views_supervillain_delete", args=(self.sv1.pk,)) + ) for should in should_contain: self.assertContains(response, should, 1) @@ -2827,10 +3454,13 @@ class AdminViewDeletedObjectsTest(TestCase): those objects should be listed for deletion. """ plot = self.pl3 - tag = FunkyTag.objects.create(content_object=plot, name='hott') + tag = FunkyTag.objects.create(content_object=plot, name="hott") should_contain = '<li>Funky tag: <a href="%s">hott' % reverse( - 'admin:admin_views_funkytag_change', args=(tag.id,)) - response = self.client.get(reverse('admin:admin_views_plot_delete', args=(plot.pk,))) + "admin:admin_views_funkytag_change", args=(tag.id,) + ) + response = self.client.get( + reverse("admin:admin_views_plot_delete", args=(plot.pk,)) + ) self.assertContains(response, should_contain) def test_generic_relations_with_related_query_name(self): @@ -2839,64 +3469,89 @@ class AdminViewDeletedObjectsTest(TestCase): GenericRelation(related_query_name='...') pointing to it, those objects should be listed for deletion. """ - bookmark = Bookmark.objects.create(name='djangoproject') - tag = FunkyTag.objects.create(content_object=bookmark, name='django') - tag_url = reverse('admin:admin_views_funkytag_change', args=(tag.id,)) + bookmark = Bookmark.objects.create(name="djangoproject") + tag = FunkyTag.objects.create(content_object=bookmark, name="django") + tag_url = reverse("admin:admin_views_funkytag_change", args=(tag.id,)) should_contain = '<li>Funky tag: <a href="%s">django' % tag_url - response = self.client.get(reverse('admin:admin_views_bookmark_delete', args=(bookmark.pk,))) + response = self.client.get( + reverse("admin:admin_views_bookmark_delete", args=(bookmark.pk,)) + ) self.assertContains(response, should_contain) def test_delete_view_uses_get_deleted_objects(self): """The delete view uses ModelAdmin.get_deleted_objects().""" - book = Book.objects.create(name='Test Book') - response = self.client.get(reverse('admin2:admin_views_book_delete', args=(book.pk,))) + book = Book.objects.create(name="Test Book") + response = self.client.get( + reverse("admin2:admin_views_book_delete", args=(book.pk,)) + ) # BookAdmin.get_deleted_objects() returns custom text. - self.assertContains(response, 'a deletable object') + self.assertContains(response, "a deletable object") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class TestGenericRelations(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.v1 = Villain.objects.create(name='Adam') - cls.pl3 = Plot.objects.create(name='Corn Conspiracy', team_leader=cls.v1, contact=cls.v1) + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.v1 = Villain.objects.create(name="Adam") + cls.pl3 = Plot.objects.create( + name="Corn Conspiracy", team_leader=cls.v1, contact=cls.v1 + ) def setUp(self): self.client.force_login(self.superuser) def test_generic_content_object_in_list_display(self): - FunkyTag.objects.create(content_object=self.pl3, name='hott') - response = self.client.get(reverse('admin:admin_views_funkytag_changelist')) + FunkyTag.objects.create(content_object=self.pl3, name="hott") + response = self.client.get(reverse("admin:admin_views_funkytag_changelist")) self.assertContains(response, "%s</td>" % self.pl3) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewStringPrimaryKeyTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') cls.pk = ( "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 " r"""-_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`""" ) cls.m1 = ModelWithStringPrimaryKey.objects.create(string_pk=cls.pk) - content_type_pk = ContentType.objects.get_for_model(ModelWithStringPrimaryKey).pk + content_type_pk = ContentType.objects.get_for_model( + ModelWithStringPrimaryKey + ).pk user_pk = cls.superuser.pk - LogEntry.objects.log_action(user_pk, content_type_pk, cls.pk, cls.pk, 2, change_message='Changed something') + LogEntry.objects.log_action( + user_pk, + content_type_pk, + cls.pk, + cls.pk, + 2, + change_message="Changed something", + ) def setUp(self): self.client.force_login(self.superuser) @@ -2907,57 +3562,82 @@ class AdminViewStringPrimaryKeyTest(TestCase): key should work. Refs #12349, #18550. """ - response = self.client.get(reverse('admin:admin_views_modelwithstringprimarykey_history', args=(self.pk,))) + response = self.client.get( + reverse( + "admin:admin_views_modelwithstringprimarykey_history", args=(self.pk,) + ) + ) self.assertContains(response, escape(self.pk)) - self.assertContains(response, 'Changed something') + self.assertContains(response, "Changed something") def test_get_change_view(self): "Retrieving the object using urlencoded form of primary key should work" - response = self.client.get(reverse('admin:admin_views_modelwithstringprimarykey_change', args=(self.pk,))) + response = self.client.get( + reverse( + "admin:admin_views_modelwithstringprimarykey_change", args=(self.pk,) + ) + ) self.assertContains(response, escape(self.pk)) def test_changelist_to_changeform_link(self): "Link to the changeform of the object in changelist should use reverse() and be quoted -- #18072" - response = self.client.get(reverse('admin:admin_views_modelwithstringprimarykey_changelist')) + response = self.client.get( + reverse("admin:admin_views_modelwithstringprimarykey_changelist") + ) # this URL now comes through reverse(), thus url quoting and iri_to_uri encoding pk_final_url = escape(iri_to_uri(quote(self.pk))) change_url = reverse( - 'admin:admin_views_modelwithstringprimarykey_change', args=('__fk__',) - ).replace('__fk__', pk_final_url) - should_contain = '<th class="field-__str__"><a href="%s">%s</a></th>' % (change_url, escape(self.pk)) + "admin:admin_views_modelwithstringprimarykey_change", args=("__fk__",) + ).replace("__fk__", pk_final_url) + should_contain = '<th class="field-__str__"><a href="%s">%s</a></th>' % ( + change_url, + escape(self.pk), + ) self.assertContains(response, should_contain) def test_recentactions_link(self): "The link from the recent actions list referring to the changeform of the object should be quoted" - response = self.client.get(reverse('admin:index')) - link = reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(self.pk),)) + response = self.client.get(reverse("admin:index")) + link = reverse( + "admin:admin_views_modelwithstringprimarykey_change", args=(quote(self.pk),) + ) should_contain = """<a href="%s">%s</a>""" % (escape(link), escape(self.pk)) self.assertContains(response, should_contain) def test_deleteconfirmation_link(self): "The link from the delete confirmation page referring back to the changeform of the object should be quoted" - url = reverse('admin:admin_views_modelwithstringprimarykey_delete', args=(quote(self.pk),)) + url = reverse( + "admin:admin_views_modelwithstringprimarykey_delete", args=(quote(self.pk),) + ) response = self.client.get(url) # this URL now comes through reverse(), thus url quoting and iri_to_uri encoding change_url = reverse( - 'admin:admin_views_modelwithstringprimarykey_change', args=('__fk__',) - ).replace('__fk__', escape(iri_to_uri(quote(self.pk)))) + "admin:admin_views_modelwithstringprimarykey_change", args=("__fk__",) + ).replace("__fk__", escape(iri_to_uri(quote(self.pk)))) should_contain = '<a href="%s">%s</a>' % (change_url, escape(self.pk)) self.assertContains(response, should_contain) def test_url_conflicts_with_add(self): "A model with a primary key that ends with add or is `add` should be visible" - add_model = ModelWithStringPrimaryKey.objects.create(pk="i have something to add") + add_model = ModelWithStringPrimaryKey.objects.create( + pk="i have something to add" + ) add_model.save() response = self.client.get( - reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(add_model.pk),)) + reverse( + "admin:admin_views_modelwithstringprimarykey_change", + args=(quote(add_model.pk),), + ) ) should_contain = """<h1>Change model with string primary key</h1>""" self.assertContains(response, should_contain) add_model2 = ModelWithStringPrimaryKey.objects.create(pk="add") - add_url = reverse('admin:admin_views_modelwithstringprimarykey_add') - change_url = reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(add_model2.pk),)) + add_url = reverse("admin:admin_views_modelwithstringprimarykey_add") + change_url = reverse( + "admin:admin_views_modelwithstringprimarykey_change", + args=(quote(add_model2.pk),), + ) self.assertNotEqual(add_url, change_url) def test_url_conflicts_with_delete(self): @@ -2965,7 +3645,10 @@ class AdminViewStringPrimaryKeyTest(TestCase): delete_model = ModelWithStringPrimaryKey(pk="delete") delete_model.save() response = self.client.get( - reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(delete_model.pk),)) + reverse( + "admin:admin_views_modelwithstringprimarykey_change", + args=(quote(delete_model.pk),), + ) ) should_contain = """<h1>Change model with string primary key</h1>""" self.assertContains(response, should_contain) @@ -2975,17 +3658,23 @@ class AdminViewStringPrimaryKeyTest(TestCase): history_model = ModelWithStringPrimaryKey(pk="history") history_model.save() response = self.client.get( - reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(history_model.pk),)) + reverse( + "admin:admin_views_modelwithstringprimarykey_change", + args=(quote(history_model.pk),), + ) ) should_contain = """<h1>Change model with string primary key</h1>""" self.assertContains(response, should_contain) def test_shortcut_view_with_escaping(self): "'View on site should' work properly with char fields" - model = ModelWithStringPrimaryKey(pk='abc_123') + model = ModelWithStringPrimaryKey(pk="abc_123") model.save() response = self.client.get( - reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(model.pk),)) + reverse( + "admin:admin_views_modelwithstringprimarykey_change", + args=(quote(model.pk),), + ) ) should_contain = '/%s/" class="viewsitelink">' % model.pk self.assertContains(response, should_contain) @@ -2993,16 +3682,20 @@ class AdminViewStringPrimaryKeyTest(TestCase): def test_change_view_history_link(self): """Object history button link should work and contain the pk value quoted.""" url = reverse( - 'admin:%s_modelwithstringprimarykey_change' % ModelWithStringPrimaryKey._meta.app_label, - args=(quote(self.pk),) + "admin:%s_modelwithstringprimarykey_change" + % ModelWithStringPrimaryKey._meta.app_label, + args=(quote(self.pk),), ) response = self.client.get(url) self.assertEqual(response.status_code, 200) expected_link = reverse( - 'admin:%s_modelwithstringprimarykey_history' % ModelWithStringPrimaryKey._meta.app_label, - args=(quote(self.pk),) + "admin:%s_modelwithstringprimarykey_history" + % ModelWithStringPrimaryKey._meta.app_label, + args=(quote(self.pk),), + ) + self.assertContains( + response, '<a href="%s" class="historylink"' % escape(expected_link) ) - self.assertContains(response, '<a href="%s" class="historylink"' % escape(expected_link)) def test_redirect_on_add_view_continue_button(self): """As soon as an object is added using "Save and continue editing" @@ -3012,29 +3705,31 @@ class AdminViewStringPrimaryKeyTest(TestCase): like slash or underscore, these characters must be escaped (see #22266) """ response = self.client.post( - reverse('admin:admin_views_modelwithstringprimarykey_add'), + reverse("admin:admin_views_modelwithstringprimarykey_add"), { - 'string_pk': '123/history', + "string_pk": "123/history", "_continue": "1", # Save and continue editing - } + }, ) self.assertEqual(response.status_code, 302) # temporary redirect - self.assertIn('/123_2Fhistory/', response.headers['location']) # PK is quoted + self.assertIn("/123_2Fhistory/", response.headers["location"]) # PK is quoted -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class SecureViewTests(TestCase): """ Test behavior of a view protected by the staff_member_required decorator. """ def test_secure_view_shows_login_if_not_logged_in(self): - secure_url = reverse('secure_view') + secure_url = reverse("secure_view") response = self.client.get(secure_url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), secure_url)) + self.assertRedirects( + response, "%s?next=%s" % (reverse("admin:login"), secure_url) + ) response = self.client.get(secure_url, follow=True) - self.assertTemplateUsed(response, 'admin/login.html') + self.assertTemplateUsed(response, "admin/login.html") self.assertEqual(response.context[REDIRECT_FIELD_NAME], secure_url) def test_staff_member_required_decorator_works_with_argument(self): @@ -3042,32 +3737,53 @@ class SecureViewTests(TestCase): Staff_member_required decorator works with an argument (redirect_field_name). """ - secure_url = '/test_admin/admin/secure-view2/' + secure_url = "/test_admin/admin/secure-view2/" response = self.client.get(secure_url) - self.assertRedirects(response, '%s?myfield=%s' % (reverse('admin:login'), secure_url)) + self.assertRedirects( + response, "%s?myfield=%s" % (reverse("admin:login"), secure_url) + ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewUnicodeTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.b1 = Book.objects.create(name='Lærdommer') - cls.p1 = Promo.objects.create(name='<Promo for Lærdommer>', book=cls.b1) + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.b1 = Book.objects.create(name="Lærdommer") + cls.p1 = Promo.objects.create(name="<Promo for Lærdommer>", book=cls.b1) cls.chap1 = Chapter.objects.create( - title='Norske bostaver æøå skaper problemer', content='<p>Svært frustrerende med UnicodeDecodeErro</p>', - book=cls.b1 + title="Norske bostaver æøå skaper problemer", + content="<p>Svært frustrerende med UnicodeDecodeErro</p>", + book=cls.b1, ) cls.chap2 = Chapter.objects.create( - title='Kjærlighet', content='<p>La kjærligheten til de lidende seire.</p>', book=cls.b1) - cls.chap3 = Chapter.objects.create(title='Kjærlighet', content='<p>Noe innhold</p>', book=cls.b1) - cls.chap4 = ChapterXtra1.objects.create(chap=cls.chap1, xtra='<Xtra(1) Norske bostaver æøå skaper problemer>') - cls.chap5 = ChapterXtra1.objects.create(chap=cls.chap2, xtra='<Xtra(1) Kjærlighet>') - cls.chap6 = ChapterXtra1.objects.create(chap=cls.chap3, xtra='<Xtra(1) Kjærlighet>') - cls.chap7 = ChapterXtra2.objects.create(chap=cls.chap1, xtra='<Xtra(2) Norske bostaver æøå skaper problemer>') - cls.chap8 = ChapterXtra2.objects.create(chap=cls.chap2, xtra='<Xtra(2) Kjærlighet>') - cls.chap9 = ChapterXtra2.objects.create(chap=cls.chap3, xtra='<Xtra(2) Kjærlighet>') + title="Kjærlighet", + content="<p>La kjærligheten til de lidende seire.</p>", + book=cls.b1, + ) + cls.chap3 = Chapter.objects.create( + title="Kjærlighet", content="<p>Noe innhold</p>", book=cls.b1 + ) + cls.chap4 = ChapterXtra1.objects.create( + chap=cls.chap1, xtra="<Xtra(1) Norske bostaver æøå skaper problemer>" + ) + cls.chap5 = ChapterXtra1.objects.create( + chap=cls.chap2, xtra="<Xtra(1) Kjærlighet>" + ) + cls.chap6 = ChapterXtra1.objects.create( + chap=cls.chap3, xtra="<Xtra(1) Kjærlighet>" + ) + cls.chap7 = ChapterXtra2.objects.create( + chap=cls.chap1, xtra="<Xtra(2) Norske bostaver æøå skaper problemer>" + ) + cls.chap8 = ChapterXtra2.objects.create( + chap=cls.chap2, xtra="<Xtra(2) Kjærlighet>" + ) + cls.chap9 = ChapterXtra2.objects.create( + chap=cls.chap3, xtra="<Xtra(2) Kjærlighet>" + ) def setUp(self): self.client.force_login(self.superuser) @@ -3102,62 +3818,75 @@ class AdminViewUnicodeTest(TestCase): "chapter_set-5-content": "", } - response = self.client.post(reverse('admin:admin_views_book_change', args=(self.b1.pk,)), post_data) + response = self.client.post( + reverse("admin:admin_views_book_change", args=(self.b1.pk,)), post_data + ) self.assertEqual(response.status_code, 302) # redirect somewhere def test_unicode_delete(self): """ The delete_view handles non-ASCII characters """ - delete_dict = {'post': 'yes'} - delete_url = reverse('admin:admin_views_book_delete', args=(self.b1.pk,)) + delete_dict = {"post": "yes"} + delete_url = reverse("admin:admin_views_book_delete", args=(self.b1.pk,)) response = self.client.get(delete_url) self.assertEqual(response.status_code, 200) response = self.client.post(delete_url, delete_dict) - self.assertRedirects(response, reverse('admin:admin_views_book_changelist')) + self.assertRedirects(response, reverse("admin:admin_views_book_changelist")) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewListEditable(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') - cls.per1 = Person.objects.create(name='John Mauchly', gender=1, alive=True) - cls.per2 = Person.objects.create(name='Grace Hopper', gender=1, alive=False) - cls.per3 = Person.objects.create(name='Guido van Rossum', gender=1, alive=True) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" + ) + cls.per1 = Person.objects.create(name="John Mauchly", gender=1, alive=True) + cls.per2 = Person.objects.create(name="Grace Hopper", gender=1, alive=False) + cls.per3 = Person.objects.create(name="Guido van Rossum", gender=1, alive=True) def setUp(self): self.client.force_login(self.superuser) def test_inheritance(self): - Podcast.objects.create(name="This Week in Django", release_date=datetime.date.today()) - response = self.client.get(reverse('admin:admin_views_podcast_changelist')) + Podcast.objects.create( + name="This Week in Django", release_date=datetime.date.today() + ) + response = self.client.get(reverse("admin:admin_views_podcast_changelist")) self.assertEqual(response.status_code, 200) def test_inheritance_2(self): Vodcast.objects.create(name="This Week in Django", released=True) - response = self.client.get(reverse('admin:admin_views_vodcast_changelist')) + response = self.client.get(reverse("admin:admin_views_vodcast_changelist")) self.assertEqual(response.status_code, 200) def test_custom_pk(self): - Language.objects.create(iso='en', name='English', english_name='English') - response = self.client.get(reverse('admin:admin_views_language_changelist')) + Language.objects.create(iso="en", name="English", english_name="English") + response = self.client.get(reverse("admin:admin_views_language_changelist")) self.assertEqual(response.status_code, 200) def test_changelist_input_html(self): - response = self.client.get(reverse('admin:admin_views_person_changelist')) + response = self.client.get(reverse("admin:admin_views_person_changelist")) # 2 inputs per object(the field and the hidden id field) = 6 # 4 management hidden fields = 4 # 4 action inputs (3 regular checkboxes, 1 checkbox to select all) @@ -3177,42 +3906,35 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "3", "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", - "form-0-gender": "1", "form-0-id": str(self.per1.pk), - "form-1-gender": "2", "form-1-id": str(self.per2.pk), - "form-2-alive": "checked", "form-2-gender": "1", "form-2-id": str(self.per3.pk), - "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_person_changelist'), - data, follow=True) - self.assertEqual(len(response.context['messages']), 1) + response = self.client.post( + reverse("admin:admin_views_person_changelist"), data, follow=True + ) + self.assertEqual(len(response.context["messages"]), 1) def test_post_submission(self): data = { "form-TOTAL_FORMS": "3", "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", - "form-0-gender": "1", "form-0-id": str(self.per1.pk), - "form-1-gender": "2", "form-1-id": str(self.per2.pk), - "form-2-alive": "checked", "form-2-gender": "1", "form-2-id": str(self.per3.pk), - "_save": "Save", } - self.client.post(reverse('admin:admin_views_person_changelist'), data) + self.client.post(reverse("admin:admin_views_person_changelist"), data) self.assertIs(Person.objects.get(name="John Mauchly").alive, False) self.assertEqual(Person.objects.get(name="Grace Hopper").gender, 2) @@ -3222,18 +3944,17 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "2", "form-INITIAL_FORMS": "2", "form-MAX_NUM_FORMS": "0", - "form-0-id": str(self.per1.pk), "form-0-gender": "1", "form-0-alive": "checked", - "form-1-id": str(self.per3.pk), "form-1-gender": "1", "form-1-alive": "checked", - "_save": "Save", } - self.client.post(reverse('admin:admin_views_person_changelist') + '?gender__exact=1', data) + self.client.post( + reverse("admin:admin_views_person_changelist") + "?gender__exact=1", data + ) self.assertIs(Person.objects.get(name="John Mauchly").alive, True) @@ -3242,13 +3963,13 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "1", "form-MAX_NUM_FORMS": "0", - "form-0-id": str(self.per1.pk), "form-0-gender": "1", - "_save": "Save", } - self.client.post(reverse('admin:admin_views_person_changelist') + '?q=john', data) + self.client.post( + reverse("admin:admin_views_person_changelist") + "?q=john", data + ) self.assertIs(Person.objects.get(name="John Mauchly").alive, False) @@ -3257,73 +3978,75 @@ class AdminViewListEditable(TestCase): Non-field errors are displayed for each of the forms in the changelist's formset. """ - fd1 = FoodDelivery.objects.create(reference='123', driver='bill', restaurant='thai') - fd2 = FoodDelivery.objects.create(reference='456', driver='bill', restaurant='india') - fd3 = FoodDelivery.objects.create(reference='789', driver='bill', restaurant='pizza') + fd1 = FoodDelivery.objects.create( + reference="123", driver="bill", restaurant="thai" + ) + fd2 = FoodDelivery.objects.create( + reference="456", driver="bill", restaurant="india" + ) + fd3 = FoodDelivery.objects.create( + reference="789", driver="bill", restaurant="pizza" + ) data = { "form-TOTAL_FORMS": "3", "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", - "form-0-id": str(fd1.id), "form-0-reference": "123", "form-0-driver": "bill", "form-0-restaurant": "thai", - # Same data as above: Forbidden because of unique_together! "form-1-id": str(fd2.id), "form-1-reference": "456", "form-1-driver": "bill", "form-1-restaurant": "thai", - "form-2-id": str(fd3.id), "form-2-reference": "789", "form-2-driver": "bill", "form-2-restaurant": "pizza", - "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_fooddelivery_changelist'), data) + response = self.client.post( + reverse("admin:admin_views_fooddelivery_changelist"), data + ) self.assertContains( response, '<tr><td colspan="4"><ul class="errorlist nonfield"><li>Food delivery ' - 'with this Driver and Restaurant already exists.</li></ul></td></tr>', + "with this Driver and Restaurant already exists.</li></ul></td></tr>", 1, - html=True + html=True, ) data = { "form-TOTAL_FORMS": "3", "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", - "form-0-id": str(fd1.id), "form-0-reference": "123", "form-0-driver": "bill", "form-0-restaurant": "thai", - # Same data as above: Forbidden because of unique_together! "form-1-id": str(fd2.id), "form-1-reference": "456", "form-1-driver": "bill", "form-1-restaurant": "thai", - # Same data also. "form-2-id": str(fd3.id), "form-2-reference": "789", "form-2-driver": "bill", "form-2-restaurant": "thai", - "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_fooddelivery_changelist'), data) + response = self.client.post( + reverse("admin:admin_views_fooddelivery_changelist"), data + ) self.assertContains( response, '<tr><td colspan="4"><ul class="errorlist nonfield"><li>Food delivery ' - 'with this Driver and Restaurant already exists.</li></ul></td></tr>', + "with this Driver and Restaurant already exists.</li></ul></td></tr>", 2, - html=True + html=True, ) def test_non_form_errors(self): @@ -3332,16 +4055,16 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "1", "form-MAX_NUM_FORMS": "0", - "form-0-id": str(self.per2.pk), "form-0-alive": "1", "form-0-gender": "2", - # The form processing understands this as a list_editable "Save" # and not an action "Go". "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_person_changelist'), data) + response = self.client.post( + reverse("admin:admin_views_person_changelist"), data + ) self.assertContains(response, "Grace is not a Zombie") def test_non_form_errors_is_errorlist(self): @@ -3350,19 +4073,19 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "1", "form-MAX_NUM_FORMS": "0", - "form-0-id": str(self.per2.pk), "form-0-alive": "1", "form-0-gender": "2", - "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_person_changelist'), data) - non_form_errors = response.context['cl'].formset.non_form_errors() + response = self.client.post( + reverse("admin:admin_views_person_changelist"), data + ) + non_form_errors = response.context["cl"].formset.non_form_errors() self.assertIsInstance(non_form_errors, ErrorList) self.assertEqual( str(non_form_errors), - str(ErrorList(['Grace is not a Zombie'], error_class='nonform')), + str(ErrorList(["Grace is not a Zombie"], error_class="nonform")), ) def test_list_editable_ordering(self): @@ -3378,28 +4101,25 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "4", "form-INITIAL_FORMS": "4", "form-MAX_NUM_FORMS": "0", - "form-0-order": "14", "form-0-id": "1", "form-0-collector": "1", - "form-1-order": "13", "form-1-id": "2", "form-1-collector": "1", - "form-2-order": "1", "form-2-id": "3", "form-2-collector": "1", - "form-3-order": "0", "form-3-id": "4", "form-3-collector": "1", - # The form processing understands this as a list_editable "Save" # and not an action "Go". "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_category_changelist'), data) + response = self.client.post( + reverse("admin:admin_views_category_changelist"), data + ) # Successful post will redirect self.assertEqual(response.status_code, 302) @@ -3413,17 +4133,21 @@ class AdminViewListEditable(TestCase): """ Pagination works for list_editable items. """ - UnorderedObject.objects.create(id=1, name='Unordered object #1') - UnorderedObject.objects.create(id=2, name='Unordered object #2') - UnorderedObject.objects.create(id=3, name='Unordered object #3') - response = self.client.get(reverse('admin:admin_views_unorderedobject_changelist')) - self.assertContains(response, 'Unordered object #3') - self.assertContains(response, 'Unordered object #2') - self.assertNotContains(response, 'Unordered object #1') - response = self.client.get(reverse('admin:admin_views_unorderedobject_changelist') + '?p=2') - self.assertNotContains(response, 'Unordered object #3') - self.assertNotContains(response, 'Unordered object #2') - self.assertContains(response, 'Unordered object #1') + UnorderedObject.objects.create(id=1, name="Unordered object #1") + UnorderedObject.objects.create(id=2, name="Unordered object #2") + UnorderedObject.objects.create(id=3, name="Unordered object #3") + response = self.client.get( + reverse("admin:admin_views_unorderedobject_changelist") + ) + self.assertContains(response, "Unordered object #3") + self.assertContains(response, "Unordered object #2") + self.assertNotContains(response, "Unordered object #1") + response = self.client.get( + reverse("admin:admin_views_unorderedobject_changelist") + "?p=2" + ) + self.assertNotContains(response, "Unordered object #3") + self.assertNotContains(response, "Unordered object #2") + self.assertContains(response, "Unordered object #1") def test_list_editable_action_submit(self): # List editable changes should not be executed if the action "Go" button is @@ -3432,22 +4156,18 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "3", "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", - "form-0-gender": "1", "form-0-id": "1", - "form-1-gender": "2", "form-1-id": "2", - "form-2-alive": "checked", "form-2-gender": "1", "form-2-id": "3", - "index": "0", - "_selected_action": ['3'], - "action": ['', 'delete_selected'], + "_selected_action": ["3"], + "action": ["", "delete_selected"], } - self.client.post(reverse('admin:admin_views_person_changelist'), data) + self.client.post(reverse("admin:admin_views_person_changelist"), data) self.assertIs(Person.objects.get(name="John Mauchly").alive, True) self.assertEqual(Person.objects.get(name="Grace Hopper").gender, 1) @@ -3459,22 +4179,18 @@ class AdminViewListEditable(TestCase): "form-TOTAL_FORMS": "3", "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", - "form-0-gender": "1", "form-0-id": str(self.per1.pk), - "form-1-gender": "2", "form-1-id": str(self.per2.pk), - "form-2-alive": "checked", "form-2-gender": "1", "form-2-id": str(self.per3.pk), - "_save": "Save", - "_selected_action": ['1'], - "action": ['', 'delete_selected'], + "_selected_action": ["1"], + "action": ["", "delete_selected"], } - self.client.post(reverse('admin:admin_views_person_changelist'), data) + self.client.post(reverse("admin:admin_views_person_changelist"), data) self.assertIs(Person.objects.get(name="John Mauchly").alive, False) self.assertEqual(Person.objects.get(name="Grace Hopper").gender, 2) @@ -3483,10 +4199,12 @@ class AdminViewListEditable(TestCase): """ Fields should not be list-editable in popups. """ - response = self.client.get(reverse('admin:admin_views_person_changelist')) - self.assertNotEqual(response.context['cl'].list_editable, ()) - response = self.client.get(reverse('admin:admin_views_person_changelist') + '?%s' % IS_POPUP_VAR) - self.assertEqual(response.context['cl'].list_editable, ()) + response = self.client.get(reverse("admin:admin_views_person_changelist")) + self.assertNotEqual(response.context["cl"].list_editable, ()) + response = self.client.get( + reverse("admin:admin_views_person_changelist") + "?%s" % IS_POPUP_VAR + ) + self.assertEqual(response.context["cl"].list_editable, ()) def test_pk_hidden_fields(self): """ @@ -3494,12 +4212,14 @@ class AdminViewListEditable(TestCase): corresponding human-readable value is displayed instead. The hidden pk fields are displayed but separately (not in the table) and only once. """ - story1 = Story.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...') + story1 = Story.objects.create( + title="The adventures of Guido", content="Once upon a time in Djangoland..." + ) story2 = Story.objects.create( - title='Crouching Tiger, Hidden Python', - content='The Python was sneaking into...', + title="Crouching Tiger, Hidden Python", + content="The Python was sneaking into...", ) - response = self.client.get(reverse('admin:admin_views_story_changelist')) + response = self.client.get(reverse("admin:admin_views_story_changelist")) # Only one hidden field, in a separate place than the table. self.assertContains(response, 'id="id_form-0-id"', 1) self.assertContains(response, 'id="id_form-1-id"', 1) @@ -3509,27 +4229,27 @@ class AdminViewListEditable(TestCase): '<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id">' '<input type="hidden" name="form-1-id" value="%d" id="id_form-1-id">\n</div>' % (story2.id, story1.id), - html=True + html=True, ) self.assertContains(response, '<td class="field-id">%d</td>' % story1.id, 1) self.assertContains(response, '<td class="field-id">%d</td>' % story2.id, 1) def test_pk_hidden_fields_with_list_display_links(self): - """ Similarly as test_pk_hidden_fields, but when the hidden pk fields are - referenced in list_display_links. - Refs #12475. + """Similarly as test_pk_hidden_fields, but when the hidden pk fields are + referenced in list_display_links. + Refs #12475. """ story1 = OtherStory.objects.create( - title='The adventures of Guido', - content='Once upon a time in Djangoland...', + title="The adventures of Guido", + content="Once upon a time in Djangoland...", ) story2 = OtherStory.objects.create( - title='Crouching Tiger, Hidden Python', - content='The Python was sneaking into...', + title="Crouching Tiger, Hidden Python", + content="The Python was sneaking into...", ) - link1 = reverse('admin:admin_views_otherstory_change', args=(story1.pk,)) - link2 = reverse('admin:admin_views_otherstory_change', args=(story2.pk,)) - response = self.client.get(reverse('admin:admin_views_otherstory_changelist')) + link1 = reverse("admin:admin_views_otherstory_change", args=(story1.pk,)) + link2 = reverse("admin:admin_views_otherstory_change", args=(story2.pk,)) + response = self.client.get(reverse("admin:admin_views_otherstory_changelist")) # Only one hidden field, in a separate place than the table. self.assertContains(response, 'id="id_form-0-id"', 1) self.assertContains(response, 'id="id_form-1-id"', 1) @@ -3539,35 +4259,54 @@ class AdminViewListEditable(TestCase): '<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id">' '<input type="hidden" name="form-1-id" value="%d" id="id_form-1-id">\n</div>' % (story2.id, story1.id), - html=True + html=True, + ) + self.assertContains( + response, + '<th class="field-id"><a href="%s">%d</a></th>' % (link1, story1.id), + 1, + ) + self.assertContains( + response, + '<th class="field-id"><a href="%s">%d</a></th>' % (link2, story2.id), + 1, ) - self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link1, story1.id), 1) - self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link2, story2.id), 1) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminSearchTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.joepublicuser = User.objects.create_user(username='joepublic', password='secret') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.joepublicuser = User.objects.create_user( + username="joepublic", password="secret" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') - cls.per1 = Person.objects.create(name='John Mauchly', gender=1, alive=True) - cls.per2 = Person.objects.create(name='Grace Hopper', gender=1, alive=False) - cls.per3 = Person.objects.create(name='Guido van Rossum', gender=1, alive=True) - Person.objects.create(name='John Doe', gender=1) + cls.per1 = Person.objects.create(name="John Mauchly", gender=1, alive=True) + cls.per2 = Person.objects.create(name="Grace Hopper", gender=1, alive=False) + cls.per3 = Person.objects.create(name="Guido van Rossum", gender=1, alive=True) + Person.objects.create(name="John Doe", gender=1) Person.objects.create(name='John O"Hara', gender=1) Person.objects.create(name="John O'Hara", gender=1) @@ -3576,17 +4315,19 @@ class AdminSearchTest(TestCase): cls.t3 = Recommender.objects.create() cls.t4 = Recommendation.objects.create(the_recommender=cls.t3) - cls.tt1 = TitleTranslation.objects.create(title=cls.t1, text='Bar') - cls.tt2 = TitleTranslation.objects.create(title=cls.t2, text='Foo') - cls.tt3 = TitleTranslation.objects.create(title=cls.t3, text='Few') - cls.tt4 = TitleTranslation.objects.create(title=cls.t4, text='Bas') + cls.tt1 = TitleTranslation.objects.create(title=cls.t1, text="Bar") + cls.tt2 = TitleTranslation.objects.create(title=cls.t2, text="Foo") + cls.tt3 = TitleTranslation.objects.create(title=cls.t3, text="Few") + cls.tt4 = TitleTranslation.objects.create(title=cls.t4, text="Bas") def setUp(self): self.client.force_login(self.superuser) def test_search_on_sibling_models(self): "A search that mentions sibling models" - response = self.client.get(reverse('admin:admin_views_recommendation_changelist') + '?q=bar') + response = self.client.get( + reverse("admin:admin_views_recommendation_changelist") + "?q=bar" + ) # confirm the search returned 1 object self.assertContains(response, "\n1 recommendation\n") @@ -3595,26 +4336,40 @@ class AdminSearchTest(TestCase): The to_field GET parameter is preserved when a search is performed. Refs #10918. """ - response = self.client.get(reverse('admin:auth_user_changelist') + '?q=joe&%s=id' % TO_FIELD_VAR) + response = self.client.get( + reverse("admin:auth_user_changelist") + "?q=joe&%s=id" % TO_FIELD_VAR + ) self.assertContains(response, "\n1 user\n") - self.assertContains(response, '<input type="hidden" name="%s" value="id">' % TO_FIELD_VAR, html=True) + self.assertContains( + response, + '<input type="hidden" name="%s" value="id">' % TO_FIELD_VAR, + html=True, + ) def test_exact_matches(self): - response = self.client.get(reverse('admin:admin_views_recommendation_changelist') + '?q=bar') + response = self.client.get( + reverse("admin:admin_views_recommendation_changelist") + "?q=bar" + ) # confirm the search returned one object self.assertContains(response, "\n1 recommendation\n") - response = self.client.get(reverse('admin:admin_views_recommendation_changelist') + '?q=ba') + response = self.client.get( + reverse("admin:admin_views_recommendation_changelist") + "?q=ba" + ) # confirm the search returned zero objects self.assertContains(response, "\n0 recommendations\n") def test_beginning_matches(self): - response = self.client.get(reverse('admin:admin_views_person_changelist') + '?q=Gui') + response = self.client.get( + reverse("admin:admin_views_person_changelist") + "?q=Gui" + ) # confirm the search returned one object self.assertContains(response, "\n1 person\n") self.assertContains(response, "Guido") - response = self.client.get(reverse('admin:admin_views_person_changelist') + '?q=uido') + response = self.client.get( + reverse("admin:admin_views_person_changelist") + "?q=uido" + ) # confirm the search returned zero objects self.assertContains(response, "\n0 persons\n") self.assertNotContains(response, "Guido") @@ -3623,12 +4378,16 @@ class AdminSearchTest(TestCase): PluggableSearchPerson.objects.create(name="Bob", age=10) PluggableSearchPerson.objects.create(name="Amy", age=20) - response = self.client.get(reverse('admin:admin_views_pluggablesearchperson_changelist') + '?q=Bob') + response = self.client.get( + reverse("admin:admin_views_pluggablesearchperson_changelist") + "?q=Bob" + ) # confirm the search returned one object self.assertContains(response, "\n1 pluggable search person\n") self.assertContains(response, "Bob") - response = self.client.get(reverse('admin:admin_views_pluggablesearchperson_changelist') + '?q=20') + response = self.client.get( + reverse("admin:admin_views_pluggablesearchperson_changelist") + "?q=20" + ) # confirm the search returned one object self.assertContains(response, "\n1 pluggable search person\n") self.assertContains(response, "Amy") @@ -3641,11 +4400,13 @@ class AdminSearchTest(TestCase): # + 1 for filtered result + 1 for filtered count # + 1 for total count with self.assertNumQueries(5): - response = self.client.get(reverse('admin:admin_views_person_changelist') + '?q=Gui') + response = self.client.get( + reverse("admin:admin_views_person_changelist") + "?q=Gui" + ) self.assertContains( response, """<span class="small quiet">1 result (<a href="?">6 total</a>)</span>""", - html=True + html=True, ) def test_no_total_count(self): @@ -3656,42 +4417,45 @@ class AdminSearchTest(TestCase): # 1 query for session + 1 for fetching user # + 1 for filtered result + 1 for filtered count with self.assertNumQueries(4): - response = self.client.get(reverse('admin:admin_views_recommendation_changelist') + '?q=bar') + response = self.client.get( + reverse("admin:admin_views_recommendation_changelist") + "?q=bar" + ) self.assertContains( response, """<span class="small quiet">1 result (<a href="?">Show all</a>)</span>""", - html=True + html=True, ) - self.assertTrue(response.context['cl'].show_admin_actions) + self.assertTrue(response.context["cl"].show_admin_actions) def test_search_with_spaces(self): - url = reverse('admin:admin_views_person_changelist') + '?q=%s' + url = reverse("admin:admin_views_person_changelist") + "?q=%s" tests = [ ('"John Doe"', 1), ("'John Doe'", 1), - ('John Doe', 0), + ("John Doe", 0), ('"John Doe" John', 1), ("'John Doe' John", 1), ("John Doe John", 0), ('"John Do"', 1), ("'John Do'", 1), - ("'John O\'Hara'", 0), + ("'John O'Hara'", 0), ("'John O\\'Hara'", 1), - ('"John O\"Hara"', 0), + ('"John O"Hara"', 0), ('"John O\\"Hara"', 1), ] for search, hits in tests: with self.subTest(search=search): response = self.client.get(url % search) - self.assertContains(response, '\n%s person' % hits) + self.assertContains(response, "\n%s person" % hits) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminInheritedInlinesTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -3706,7 +4470,7 @@ class AdminInheritedInlinesTest(TestCase): name_re = re.compile(b'name="(.*?)"') # test the add case - response = self.client.get(reverse('admin:admin_views_persona_add')) + response = self.client.get(reverse("admin:admin_views_persona_add")) names = name_re.findall(response.content) # make sure we have no duplicate HTML names self.assertEqual(len(names), len(set(names))) @@ -3725,7 +4489,7 @@ class AdminInheritedInlinesTest(TestCase): "accounts-2-0-username": bar_user, } - response = self.client.post(reverse('admin:admin_views_persona_add'), post_data) + response = self.client.post(reverse("admin:admin_views_persona_add"), post_data) self.assertEqual(response.status_code, 302) # redirect somewhere self.assertEqual(Persona.objects.count(), 1) self.assertEqual(FooAccount.objects.count(), 1) @@ -3740,31 +4504,31 @@ class AdminInheritedInlinesTest(TestCase): # test the edit case - response = self.client.get(reverse('admin:admin_views_persona_change', args=(persona_id,))) + response = self.client.get( + reverse("admin:admin_views_persona_change", args=(persona_id,)) + ) names = name_re.findall(response.content) # make sure we have no duplicate HTML names self.assertEqual(len(names), len(set(names))) post_data = { "name": "Test Name", - "accounts-TOTAL_FORMS": "2", "accounts-INITIAL_FORMS": "1", "accounts-MAX_NUM_FORMS": "0", - "accounts-0-username": "%s-1" % foo_user, "accounts-0-account_ptr": str(foo_id), "accounts-0-persona": str(persona_id), - "accounts-2-TOTAL_FORMS": "2", "accounts-2-INITIAL_FORMS": "1", "accounts-2-MAX_NUM_FORMS": "0", - "accounts-2-0-username": "%s-1" % bar_user, "accounts-2-0-account_ptr": str(bar_id), "accounts-2-0-persona": str(persona_id), } - response = self.client.post(reverse('admin:admin_views_persona_change', args=(persona_id,)), post_data) + response = self.client.post( + reverse("admin:admin_views_persona_change", args=(persona_id,)), post_data + ) self.assertEqual(response.status_code, 302) self.assertEqual(Persona.objects.count(), 1) self.assertEqual(FooAccount.objects.count(), 1) @@ -3774,12 +4538,13 @@ class AdminInheritedInlinesTest(TestCase): self.assertEqual(Persona.objects.all()[0].accounts.count(), 2) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class TestCustomChangeList(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -3790,21 +4555,22 @@ class TestCustomChangeList(TestCase): """ # Insert some data post_data = {"name": "First Gadget"} - response = self.client.post(reverse('admin:admin_views_gadget_add'), post_data) + response = self.client.post(reverse("admin:admin_views_gadget_add"), post_data) self.assertEqual(response.status_code, 302) # redirect somewhere # Hit the page once to get messages out of the queue message list - response = self.client.get(reverse('admin:admin_views_gadget_changelist')) + response = self.client.get(reverse("admin:admin_views_gadget_changelist")) # Data is still not visible on the page - response = self.client.get(reverse('admin:admin_views_gadget_changelist')) - self.assertNotContains(response, 'First Gadget') + response = self.client.get(reverse("admin:admin_views_gadget_changelist")) + self.assertNotContains(response, "First Gadget") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class TestInlineNotEditable(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -3813,73 +4579,74 @@ class TestInlineNotEditable(TestCase): """ InlineModelAdmin broken? """ - response = self.client.get(reverse('admin:admin_views_parent_add')) + response = self.client.get(reverse("admin:admin_views_parent_add")) self.assertEqual(response.status_code, 200) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminCustomQuerysetTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) cls.pks = [EmptyModel.objects.create().id for i in range(3)] def setUp(self): self.client.force_login(self.superuser) self.super_login = { - REDIRECT_FIELD_NAME: reverse('admin:index'), - 'username': 'super', - 'password': 'secret', + REDIRECT_FIELD_NAME: reverse("admin:index"), + "username": "super", + "password": "secret", } def test_changelist_view(self): - response = self.client.get(reverse('admin:admin_views_emptymodel_changelist')) + response = self.client.get(reverse("admin:admin_views_emptymodel_changelist")) for i in self.pks: if i > 1: - self.assertContains(response, 'Primary key = %s' % i) + self.assertContains(response, "Primary key = %s" % i) else: - self.assertNotContains(response, 'Primary key = %s' % i) + self.assertNotContains(response, "Primary key = %s" % i) def test_changelist_view_count_queries(self): # create 2 Person objects - Person.objects.create(name='person1', gender=1) - Person.objects.create(name='person2', gender=2) - changelist_url = reverse('admin:admin_views_person_changelist') + Person.objects.create(name="person1", gender=1) + Person.objects.create(name="person2", gender=2) + changelist_url = reverse("admin:admin_views_person_changelist") # 5 queries are expected: 1 for the session, 1 for the user, # 2 for the counts and 1 for the objects on the page with self.assertNumQueries(5): resp = self.client.get(changelist_url) - self.assertEqual(resp.context['selection_note'], '0 of 2 selected') - self.assertEqual(resp.context['selection_note_all'], 'All 2 selected') + self.assertEqual(resp.context["selection_note"], "0 of 2 selected") + self.assertEqual(resp.context["selection_note_all"], "All 2 selected") with self.assertNumQueries(5): - extra = {'q': 'not_in_name'} + extra = {"q": "not_in_name"} resp = self.client.get(changelist_url, extra) - self.assertEqual(resp.context['selection_note'], '0 of 0 selected') - self.assertEqual(resp.context['selection_note_all'], 'All 0 selected') + self.assertEqual(resp.context["selection_note"], "0 of 0 selected") + self.assertEqual(resp.context["selection_note_all"], "All 0 selected") with self.assertNumQueries(5): - extra = {'q': 'person'} + extra = {"q": "person"} resp = self.client.get(changelist_url, extra) - self.assertEqual(resp.context['selection_note'], '0 of 2 selected') - self.assertEqual(resp.context['selection_note_all'], 'All 2 selected') + self.assertEqual(resp.context["selection_note"], "0 of 2 selected") + self.assertEqual(resp.context["selection_note_all"], "All 2 selected") with self.assertNumQueries(5): - extra = {'gender__exact': '1'} + extra = {"gender__exact": "1"} resp = self.client.get(changelist_url, extra) - self.assertEqual(resp.context['selection_note'], '0 of 1 selected') - self.assertEqual(resp.context['selection_note_all'], '1 selected') + self.assertEqual(resp.context["selection_note"], "0 of 1 selected") + self.assertEqual(resp.context["selection_note_all"], "1 selected") def test_change_view(self): for i in self.pks: - url = reverse('admin:admin_views_emptymodel_change', args=(i,)) + url = reverse("admin:admin_views_emptymodel_change", args=(i,)) response = self.client.get(url, follow=True) if i > 1: self.assertEqual(response.status_code, 200) else: - self.assertRedirects(response, reverse('admin:index')) + self.assertRedirects(response, reverse("admin:index")) self.assertEqual( - [m.message for m in response.context['messages']], - ['empty model with ID “1” doesn’t exist. Perhaps it was deleted?'] + [m.message for m in response.context["messages"]], + ["empty model with ID “1” doesn’t exist. Perhaps it was deleted?"], ) def test_add_model_modeladmin_defer_qs(self): @@ -3892,7 +4659,9 @@ class AdminCustomQuerysetTest(TestCase): "author": "Candidate, Best", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_coverletter_add'), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_coverletter_add"), post_data, follow=True + ) self.assertEqual(response.status_code, 200) self.assertEqual(CoverLetter.objects.count(), 1) # Message should contain non-ugly model verbose name @@ -3900,8 +4669,9 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The cover letter “<a href="%s">' - 'Candidate, Best</a>” was added successfully.</li>' % - reverse('admin:admin_views_coverletter_change', args=(pk,)), html=True + "Candidate, Best</a>” was added successfully.</li>" + % reverse("admin:admin_views_coverletter_change", args=(pk,)), + html=True, ) # model has no __str__ method @@ -3911,7 +4681,9 @@ class AdminCustomQuerysetTest(TestCase): "content": "What's this SMS thing?", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_shortmessage_add'), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_shortmessage_add"), post_data, follow=True + ) self.assertEqual(response.status_code, 200) self.assertEqual(ShortMessage.objects.count(), 1) # Message should contain non-ugly model verbose name @@ -3919,8 +4691,9 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The short message “<a href="%s">' - '%s</a>” was added successfully.</li>' % - (reverse('admin:admin_views_shortmessage_change', args=(sm.pk,)), sm), html=True + "%s</a>” was added successfully.</li>" + % (reverse("admin:admin_views_shortmessage_change", args=(sm.pk,)), sm), + html=True, ) def test_add_model_modeladmin_only_qs(self): @@ -3933,7 +4706,9 @@ class AdminCustomQuerysetTest(TestCase): "title": "Urgent telegram", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_telegram_add'), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_telegram_add"), post_data, follow=True + ) self.assertEqual(response.status_code, 200) self.assertEqual(Telegram.objects.count(), 1) # Message should contain non-ugly model verbose name @@ -3941,8 +4716,9 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The telegram “<a href="%s">' - 'Urgent telegram</a>” was added successfully.</li>' % - reverse('admin:admin_views_telegram_change', args=(pk,)), html=True + "Urgent telegram</a>” was added successfully.</li>" + % reverse("admin:admin_views_telegram_change", args=(pk,)), + html=True, ) # model has no __str__ method @@ -3952,7 +4728,9 @@ class AdminCustomQuerysetTest(TestCase): "title": "My Modified Paper Title", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_paper_add'), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_paper_add"), post_data, follow=True + ) self.assertEqual(response.status_code, 200) self.assertEqual(Paper.objects.count(), 1) # Message should contain non-ugly model verbose name @@ -3960,8 +4738,9 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The paper “<a href="%s">' - '%s</a>” was added successfully.</li>' % - (reverse('admin:admin_views_paper_change', args=(p.pk,)), p), html=True + "%s</a>” was added successfully.</li>" + % (reverse("admin:admin_views_paper_change", args=(p.pk,)), p), + html=True, ) def test_edit_model_modeladmin_defer_qs(self): @@ -3970,14 +4749,16 @@ class AdminCustomQuerysetTest(TestCase): # model has __str__ method cl = CoverLetter.objects.create(author="John Doe") self.assertEqual(CoverLetter.objects.count(), 1) - response = self.client.get(reverse('admin:admin_views_coverletter_change', args=(cl.pk,))) + response = self.client.get( + reverse("admin:admin_views_coverletter_change", args=(cl.pk,)) + ) self.assertEqual(response.status_code, 200) # Emulate model instance edit via the admin post_data = { "author": "John Doe II", "_save": "Save", } - url = reverse('admin:admin_views_coverletter_change', args=(cl.pk,)) + url = reverse("admin:admin_views_coverletter_change", args=(cl.pk,)) response = self.client.post(url, post_data, follow=True) self.assertEqual(response.status_code, 200) self.assertEqual(CoverLetter.objects.count(), 1) @@ -3986,21 +4767,24 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The cover letter “<a href="%s">' - 'John Doe II</a>” was changed successfully.</li>' % - reverse('admin:admin_views_coverletter_change', args=(cl.pk,)), html=True + "John Doe II</a>” was changed successfully.</li>" + % reverse("admin:admin_views_coverletter_change", args=(cl.pk,)), + html=True, ) # model has no __str__ method sm = ShortMessage.objects.create(content="This is expensive") self.assertEqual(ShortMessage.objects.count(), 1) - response = self.client.get(reverse('admin:admin_views_shortmessage_change', args=(sm.pk,))) + response = self.client.get( + reverse("admin:admin_views_shortmessage_change", args=(sm.pk,)) + ) self.assertEqual(response.status_code, 200) # Emulate model instance edit via the admin post_data = { "content": "Too expensive", "_save": "Save", } - url = reverse('admin:admin_views_shortmessage_change', args=(sm.pk,)) + url = reverse("admin:admin_views_shortmessage_change", args=(sm.pk,)) response = self.client.post(url, post_data, follow=True) self.assertEqual(response.status_code, 200) self.assertEqual(ShortMessage.objects.count(), 1) @@ -4009,8 +4793,9 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The short message “<a href="%s">' - '%s</a>” was changed successfully.</li>' % - (reverse('admin:admin_views_shortmessage_change', args=(sm.pk,)), sm), html=True + "%s</a>” was changed successfully.</li>" + % (reverse("admin:admin_views_shortmessage_change", args=(sm.pk,)), sm), + html=True, ) def test_edit_model_modeladmin_only_qs(self): @@ -4019,14 +4804,20 @@ class AdminCustomQuerysetTest(TestCase): # model has __str__ method t = Telegram.objects.create(title="First Telegram") self.assertEqual(Telegram.objects.count(), 1) - response = self.client.get(reverse('admin:admin_views_telegram_change', args=(t.pk,))) + response = self.client.get( + reverse("admin:admin_views_telegram_change", args=(t.pk,)) + ) self.assertEqual(response.status_code, 200) # Emulate model instance edit via the admin post_data = { "title": "Telegram without typo", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_telegram_change', args=(t.pk,)), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_telegram_change", args=(t.pk,)), + post_data, + follow=True, + ) self.assertEqual(response.status_code, 200) self.assertEqual(Telegram.objects.count(), 1) # Message should contain non-ugly model verbose name. The instance @@ -4034,21 +4825,28 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The telegram “<a href="%s">' - 'Telegram without typo</a>” was changed successfully.</li>' % - reverse('admin:admin_views_telegram_change', args=(t.pk,)), html=True + "Telegram without typo</a>” was changed successfully.</li>" + % reverse("admin:admin_views_telegram_change", args=(t.pk,)), + html=True, ) # model has no __str__ method p = Paper.objects.create(title="My Paper Title") self.assertEqual(Paper.objects.count(), 1) - response = self.client.get(reverse('admin:admin_views_paper_change', args=(p.pk,))) + response = self.client.get( + reverse("admin:admin_views_paper_change", args=(p.pk,)) + ) self.assertEqual(response.status_code, 200) # Emulate model instance edit via the admin post_data = { "title": "My Modified Paper Title", "_save": "Save", } - response = self.client.post(reverse('admin:admin_views_paper_change', args=(p.pk,)), post_data, follow=True) + response = self.client.post( + reverse("admin:admin_views_paper_change", args=(p.pk,)), + post_data, + follow=True, + ) self.assertEqual(response.status_code, 200) self.assertEqual(Paper.objects.count(), 1) # Message should contain non-ugly model verbose name. The ugly(!) @@ -4056,41 +4854,51 @@ class AdminCustomQuerysetTest(TestCase): self.assertContains( response, '<li class="success">The paper “<a href="%s">' - '%s</a>” was changed successfully.</li>' % - (reverse('admin:admin_views_paper_change', args=(p.pk,)), p), html=True + "%s</a>” was changed successfully.</li>" + % (reverse("admin:admin_views_paper_change", args=(p.pk,)), p), + html=True, ) def test_history_view_custom_qs(self): """ Custom querysets are considered for the admin history view. """ - self.client.post(reverse('admin:login'), self.super_login) + self.client.post(reverse("admin:login"), self.super_login) FilteredManager.objects.create(pk=1) FilteredManager.objects.create(pk=2) - response = self.client.get(reverse('admin:admin_views_filteredmanager_changelist')) + response = self.client.get( + reverse("admin:admin_views_filteredmanager_changelist") + ) self.assertContains(response, "PK=1") self.assertContains(response, "PK=2") self.assertEqual( - self.client.get(reverse('admin:admin_views_filteredmanager_history', args=(1,))).status_code, 200 + self.client.get( + reverse("admin:admin_views_filteredmanager_history", args=(1,)) + ).status_code, + 200, ) self.assertEqual( - self.client.get(reverse('admin:admin_views_filteredmanager_history', args=(2,))).status_code, 200 + self.client.get( + reverse("admin:admin_views_filteredmanager_history", args=(2,)) + ).status_code, + 200, ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminInlineFileUploadTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) file1 = tempfile.NamedTemporaryFile(suffix=".file1") - file1.write(b'a' * (2 ** 21)) + file1.write(b"a" * (2**21)) filename = file1.name file1.close() - cls.gallery = Gallery.objects.create(name='Test Gallery') + cls.gallery = Gallery.objects.create(name="Test Gallery") cls.picture = Picture.objects.create( - name='Test Picture', + name="Test Picture", image=filename, gallery=cls.gallery, ) @@ -4100,9 +4908,9 @@ class AdminInlineFileUploadTest(TestCase): def test_form_has_multipart_enctype(self): response = self.client.get( - reverse('admin:admin_views_gallery_change', args=(self.gallery.id,)) + reverse("admin:admin_views_gallery_change", args=(self.gallery.id,)) ) - self.assertIs(response.context['has_file_field'], True) + self.assertIs(response.context["has_file_field"], True) self.assertContains(response, MULTIPART_ENCTYPE) def test_inline_file_upload_edit_validation_error_post(self): @@ -4124,23 +4932,24 @@ class AdminInlineFileUploadTest(TestCase): "pictures-1-image": "", } response = self.client.post( - reverse('admin:admin_views_gallery_change', args=(self.gallery.id,)), post_data + reverse("admin:admin_views_gallery_change", args=(self.gallery.id,)), + post_data, ) self.assertContains(response, b"Currently") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminInlineTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.collector = Collector.objects.create(pk=1, name='John Fowles') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.collector = Collector.objects.create(pk=1, name="John Fowles") def setUp(self): self.post_data = { "name": "Test Name", - "widget_set-TOTAL_FORMS": "3", "widget_set-INITIAL_FORMS": "0", "widget_set-MAX_NUM_FORMS": "0", @@ -4153,7 +4962,6 @@ class AdminInlineTests(TestCase): "widget_set-2-id": "", "widget_set-2-owner": "1", "widget_set-2-name": "", - "doohickey_set-TOTAL_FORMS": "3", "doohickey_set-INITIAL_FORMS": "0", "doohickey_set-MAX_NUM_FORMS": "0", @@ -4166,7 +4974,6 @@ class AdminInlineTests(TestCase): "doohickey_set-2-owner": "1", "doohickey_set-2-code": "", "doohickey_set-2-name": "", - "grommet_set-TOTAL_FORMS": "3", "grommet_set-INITIAL_FORMS": "0", "grommet_set-MAX_NUM_FORMS": "0", @@ -4179,7 +4986,6 @@ class AdminInlineTests(TestCase): "grommet_set-2-code": "", "grommet_set-2-owner": "1", "grommet_set-2-name": "", - "whatsit_set-TOTAL_FORMS": "3", "whatsit_set-INITIAL_FORMS": "0", "whatsit_set-MAX_NUM_FORMS": "0", @@ -4192,7 +4998,6 @@ class AdminInlineTests(TestCase): "whatsit_set-2-owner": "1", "whatsit_set-2-index": "", "whatsit_set-2-name": "", - "fancydoodad_set-TOTAL_FORMS": "3", "fancydoodad_set-INITIAL_FORMS": "0", "fancydoodad_set-MAX_NUM_FORMS": "0", @@ -4208,7 +5013,6 @@ class AdminInlineTests(TestCase): "fancydoodad_set-2-owner": "1", "fancydoodad_set-2-name": "", "fancydoodad_set-2-expensive": "on", - "category_set-TOTAL_FORMS": "3", "category_set-INITIAL_FORMS": "0", "category_set-MAX_NUM_FORMS": "0", @@ -4228,8 +5032,10 @@ class AdminInlineTests(TestCase): def test_simple_inline(self): "A simple model can be saved as inlines" # First add a new inline - self.post_data['widget_set-0-name'] = "Widget 1" - collector_url = reverse('admin:admin_views_collector_change', args=(self.collector.pk,)) + self.post_data["widget_set-0-name"] = "Widget 1" + collector_url = reverse( + "admin:admin_views_collector_change", args=(self.collector.pk,) + ) response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Widget.objects.count(), 1) @@ -4241,22 +5047,22 @@ class AdminInlineTests(TestCase): self.assertContains(response, 'name="widget_set-0-id"') # No file or image fields, no enctype on the forms - self.assertIs(response.context['has_file_field'], False) + self.assertIs(response.context["has_file_field"], False) self.assertNotContains(response, MULTIPART_ENCTYPE) # Now resave that inline - self.post_data['widget_set-INITIAL_FORMS'] = "1" - self.post_data['widget_set-0-id'] = str(widget_id) - self.post_data['widget_set-0-name'] = "Widget 1" + self.post_data["widget_set-INITIAL_FORMS"] = "1" + self.post_data["widget_set-0-id"] = str(widget_id) + self.post_data["widget_set-0-name"] = "Widget 1" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Widget.objects.count(), 1) self.assertEqual(Widget.objects.all()[0].name, "Widget 1") # Now modify that inline - self.post_data['widget_set-INITIAL_FORMS'] = "1" - self.post_data['widget_set-0-id'] = str(widget_id) - self.post_data['widget_set-0-name'] = "Widget 1 Updated" + self.post_data["widget_set-INITIAL_FORMS"] = "1" + self.post_data["widget_set-0-id"] = str(widget_id) + self.post_data["widget_set-0-name"] = "Widget 1 Updated" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Widget.objects.count(), 1) @@ -4265,8 +5071,10 @@ class AdminInlineTests(TestCase): def test_explicit_autofield_inline(self): "A model with an explicit autofield primary key can be saved as inlines. Regression for #8093" # First add a new inline - self.post_data['grommet_set-0-name'] = "Grommet 1" - collector_url = reverse('admin:admin_views_collector_change', args=(self.collector.pk,)) + self.post_data["grommet_set-0-name"] = "Grommet 1" + collector_url = reverse( + "admin:admin_views_collector_change", args=(self.collector.pk,) + ) response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Grommet.objects.count(), 1) @@ -4277,18 +5085,18 @@ class AdminInlineTests(TestCase): self.assertContains(response, 'name="grommet_set-0-code"') # Now resave that inline - self.post_data['grommet_set-INITIAL_FORMS'] = "1" - self.post_data['grommet_set-0-code'] = str(Grommet.objects.all()[0].code) - self.post_data['grommet_set-0-name'] = "Grommet 1" + self.post_data["grommet_set-INITIAL_FORMS"] = "1" + self.post_data["grommet_set-0-code"] = str(Grommet.objects.all()[0].code) + self.post_data["grommet_set-0-name"] = "Grommet 1" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Grommet.objects.count(), 1) self.assertEqual(Grommet.objects.all()[0].name, "Grommet 1") # Now modify that inline - self.post_data['grommet_set-INITIAL_FORMS'] = "1" - self.post_data['grommet_set-0-code'] = str(Grommet.objects.all()[0].code) - self.post_data['grommet_set-0-name'] = "Grommet 1 Updated" + self.post_data["grommet_set-INITIAL_FORMS"] = "1" + self.post_data["grommet_set-0-code"] = str(Grommet.objects.all()[0].code) + self.post_data["grommet_set-0-name"] = "Grommet 1 Updated" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Grommet.objects.count(), 1) @@ -4297,9 +5105,11 @@ class AdminInlineTests(TestCase): def test_char_pk_inline(self): "A model with a character PK can be saved as inlines. Regression for #10992" # First add a new inline - self.post_data['doohickey_set-0-code'] = "DH1" - self.post_data['doohickey_set-0-name'] = "Doohickey 1" - collector_url = reverse('admin:admin_views_collector_change', args=(self.collector.pk,)) + self.post_data["doohickey_set-0-code"] = "DH1" + self.post_data["doohickey_set-0-name"] = "Doohickey 1" + collector_url = reverse( + "admin:admin_views_collector_change", args=(self.collector.pk,) + ) response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(DooHickey.objects.count(), 1) @@ -4310,18 +5120,18 @@ class AdminInlineTests(TestCase): self.assertContains(response, 'name="doohickey_set-0-code"') # Now resave that inline - self.post_data['doohickey_set-INITIAL_FORMS'] = "1" - self.post_data['doohickey_set-0-code'] = "DH1" - self.post_data['doohickey_set-0-name'] = "Doohickey 1" + self.post_data["doohickey_set-INITIAL_FORMS"] = "1" + self.post_data["doohickey_set-0-code"] = "DH1" + self.post_data["doohickey_set-0-name"] = "Doohickey 1" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(DooHickey.objects.count(), 1) self.assertEqual(DooHickey.objects.all()[0].name, "Doohickey 1") # Now modify that inline - self.post_data['doohickey_set-INITIAL_FORMS'] = "1" - self.post_data['doohickey_set-0-code'] = "DH1" - self.post_data['doohickey_set-0-name'] = "Doohickey 1 Updated" + self.post_data["doohickey_set-INITIAL_FORMS"] = "1" + self.post_data["doohickey_set-0-code"] = "DH1" + self.post_data["doohickey_set-0-name"] = "Doohickey 1 Updated" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(DooHickey.objects.count(), 1) @@ -4330,9 +5140,11 @@ class AdminInlineTests(TestCase): def test_integer_pk_inline(self): "A model with an integer PK can be saved as inlines. Regression for #10992" # First add a new inline - self.post_data['whatsit_set-0-index'] = "42" - self.post_data['whatsit_set-0-name'] = "Whatsit 1" - collector_url = reverse('admin:admin_views_collector_change', args=(self.collector.pk,)) + self.post_data["whatsit_set-0-index"] = "42" + self.post_data["whatsit_set-0-name"] = "Whatsit 1" + collector_url = reverse( + "admin:admin_views_collector_change", args=(self.collector.pk,) + ) response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Whatsit.objects.count(), 1) @@ -4343,18 +5155,18 @@ class AdminInlineTests(TestCase): self.assertContains(response, 'name="whatsit_set-0-index"') # Now resave that inline - self.post_data['whatsit_set-INITIAL_FORMS'] = "1" - self.post_data['whatsit_set-0-index'] = "42" - self.post_data['whatsit_set-0-name'] = "Whatsit 1" + self.post_data["whatsit_set-INITIAL_FORMS"] = "1" + self.post_data["whatsit_set-0-index"] = "42" + self.post_data["whatsit_set-0-name"] = "Whatsit 1" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Whatsit.objects.count(), 1) self.assertEqual(Whatsit.objects.all()[0].name, "Whatsit 1") # Now modify that inline - self.post_data['whatsit_set-INITIAL_FORMS'] = "1" - self.post_data['whatsit_set-0-index'] = "42" - self.post_data['whatsit_set-0-name'] = "Whatsit 1 Updated" + self.post_data["whatsit_set-INITIAL_FORMS"] = "1" + self.post_data["whatsit_set-0-index"] = "42" + self.post_data["whatsit_set-0-name"] = "Whatsit 1 Updated" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(Whatsit.objects.count(), 1) @@ -4363,8 +5175,10 @@ class AdminInlineTests(TestCase): def test_inherited_inline(self): "An inherited model can be saved as inlines. Regression for #11042" # First add a new inline - self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1" - collector_url = reverse('admin:admin_views_collector_change', args=(self.collector.pk,)) + self.post_data["fancydoodad_set-0-name"] = "Fancy Doodad 1" + collector_url = reverse( + "admin:admin_views_collector_change", args=(self.collector.pk,) + ) response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(FancyDoodad.objects.count(), 1) @@ -4376,18 +5190,18 @@ class AdminInlineTests(TestCase): self.assertContains(response, 'name="fancydoodad_set-0-doodad_ptr"') # Now resave that inline - self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1" - self.post_data['fancydoodad_set-0-doodad_ptr'] = str(doodad_pk) - self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1" + self.post_data["fancydoodad_set-INITIAL_FORMS"] = "1" + self.post_data["fancydoodad_set-0-doodad_ptr"] = str(doodad_pk) + self.post_data["fancydoodad_set-0-name"] = "Fancy Doodad 1" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(FancyDoodad.objects.count(), 1) self.assertEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1") # Now modify that inline - self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1" - self.post_data['fancydoodad_set-0-doodad_ptr'] = str(doodad_pk) - self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1 Updated" + self.post_data["fancydoodad_set-INITIAL_FORMS"] = "1" + self.post_data["fancydoodad_set-0-doodad_ptr"] = str(doodad_pk) + self.post_data["fancydoodad_set-0-name"] = "Fancy Doodad 1 Updated" response = self.client.post(collector_url, self.post_data) self.assertEqual(response.status_code, 302) self.assertEqual(FancyDoodad.objects.count(), 1) @@ -4404,42 +5218,38 @@ class AdminInlineTests(TestCase): Category.objects.create(id=4, order=0, collector=self.collector) # NB: The order values must be changed so that the items are reordered. - self.post_data.update({ - "name": "Frederick Clegg", - - "category_set-TOTAL_FORMS": "7", - "category_set-INITIAL_FORMS": "4", - "category_set-MAX_NUM_FORMS": "0", - - "category_set-0-order": "14", - "category_set-0-id": "1", - "category_set-0-collector": "1", - - "category_set-1-order": "13", - "category_set-1-id": "2", - "category_set-1-collector": "1", - - "category_set-2-order": "1", - "category_set-2-id": "3", - "category_set-2-collector": "1", - - "category_set-3-order": "0", - "category_set-3-id": "4", - "category_set-3-collector": "1", - - "category_set-4-order": "", - "category_set-4-id": "", - "category_set-4-collector": "1", - - "category_set-5-order": "", - "category_set-5-id": "", - "category_set-5-collector": "1", - - "category_set-6-order": "", - "category_set-6-id": "", - "category_set-6-collector": "1", - }) - collector_url = reverse('admin:admin_views_collector_change', args=(self.collector.pk,)) + self.post_data.update( + { + "name": "Frederick Clegg", + "category_set-TOTAL_FORMS": "7", + "category_set-INITIAL_FORMS": "4", + "category_set-MAX_NUM_FORMS": "0", + "category_set-0-order": "14", + "category_set-0-id": "1", + "category_set-0-collector": "1", + "category_set-1-order": "13", + "category_set-1-id": "2", + "category_set-1-collector": "1", + "category_set-2-order": "1", + "category_set-2-id": "3", + "category_set-2-collector": "1", + "category_set-3-order": "0", + "category_set-3-id": "4", + "category_set-3-collector": "1", + "category_set-4-order": "", + "category_set-4-id": "", + "category_set-4-collector": "1", + "category_set-5-order": "", + "category_set-5-id": "", + "category_set-5-collector": "1", + "category_set-6-order": "", + "category_set-6-id": "", + "category_set-6-collector": "1", + } + ) + collector_url = reverse( + "admin:admin_views_collector_change", args=(self.collector.pk,) + ) response = self.client.post(collector_url, self.post_data) # Successful post will redirect self.assertEqual(response.status_code, 302) @@ -4452,105 +5262,124 @@ class AdminInlineTests(TestCase): self.assertEqual(Category.objects.get(id=4).order, 0) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class NeverCacheTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = Section.objects.create(name="Test section") def setUp(self): self.client.force_login(self.superuser) def test_admin_index(self): "Check the never-cache status of the main index" - response = self.client.get(reverse('admin:index')) + response = self.client.get(reverse("admin:index")) self.assertEqual(get_max_age(response), 0) def test_app_index(self): "Check the never-cache status of an application index" - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertEqual(get_max_age(response), 0) def test_model_index(self): "Check the never-cache status of a model index" - response = self.client.get(reverse('admin:admin_views_fabric_changelist')) + response = self.client.get(reverse("admin:admin_views_fabric_changelist")) self.assertEqual(get_max_age(response), 0) def test_model_add(self): "Check the never-cache status of a model add page" - response = self.client.get(reverse('admin:admin_views_fabric_add')) + response = self.client.get(reverse("admin:admin_views_fabric_add")) self.assertEqual(get_max_age(response), 0) def test_model_view(self): "Check the never-cache status of a model edit page" - response = self.client.get(reverse('admin:admin_views_section_change', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_section_change", args=(self.s1.pk,)) + ) self.assertEqual(get_max_age(response), 0) def test_model_history(self): "Check the never-cache status of a model history page" - response = self.client.get(reverse('admin:admin_views_section_history', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_section_history", args=(self.s1.pk,)) + ) self.assertEqual(get_max_age(response), 0) def test_model_delete(self): "Check the never-cache status of a model delete page" - response = self.client.get(reverse('admin:admin_views_section_delete', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_section_delete", args=(self.s1.pk,)) + ) self.assertEqual(get_max_age(response), 0) def test_login(self): "Check the never-cache status of login views" self.client.logout() - response = self.client.get(reverse('admin:index')) + response = self.client.get(reverse("admin:index")) self.assertEqual(get_max_age(response), 0) def test_logout(self): "Check the never-cache status of logout view" - response = self.client.get(reverse('admin:logout')) + response = self.client.get(reverse("admin:logout")) self.assertEqual(get_max_age(response), 0) def test_password_change(self): "Check the never-cache status of the password change view" self.client.logout() - response = self.client.get(reverse('admin:password_change')) + response = self.client.get(reverse("admin:password_change")) self.assertIsNone(get_max_age(response)) def test_password_change_done(self): "Check the never-cache status of the password change done view" - response = self.client.get(reverse('admin:password_change_done')) + response = self.client.get(reverse("admin:password_change_done")) self.assertIsNone(get_max_age(response)) def test_JS_i18n(self): "Check the never-cache status of the JavaScript i18n view" - response = self.client.get(reverse('admin:jsi18n')) + response = self.client.get(reverse("admin:jsi18n")) self.assertIsNone(get_max_age(response)) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class PrePopulatedTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" + ) def setUp(self): self.client.force_login(self.superuser) def test_prepopulated_on(self): - response = self.client.get(reverse('admin:admin_views_prepopulatedpost_add')) + response = self.client.get(reverse("admin:admin_views_prepopulatedpost_add")) self.assertContains(response, ""id": "#id_slug"") - self.assertContains(response, ""dependency_ids": ["#id_title"]") - self.assertContains(response, ""id": "#id_prepopulatedsubpost_set-0-subslug"") + self.assertContains( + response, ""dependency_ids": ["#id_title"]" + ) + self.assertContains( + response, + ""id": "#id_prepopulatedsubpost_set-0-subslug"", + ) def test_prepopulated_off(self): - response = self.client.get(reverse('admin:admin_views_prepopulatedpost_change', args=(self.p1.pk,))) + response = self.client.get( + reverse("admin:admin_views_prepopulatedpost_change", args=(self.p1.pk,)) + ) self.assertContains(response, "A Long Title") self.assertNotContains(response, ""id": "#id_slug"") - self.assertNotContains(response, ""dependency_ids": ["#id_title"]") + self.assertNotContains( + response, ""dependency_ids": ["#id_title"]" + ) self.assertNotContains( response, - ""id": "#id_prepopulatedsubpost_set-0-subslug"" + ""id": "#id_prepopulatedsubpost_set-0-subslug"", ) @override_settings(USE_THOUSAND_SEPARATOR=True) @@ -4559,7 +5388,9 @@ class PrePopulatedTest(TestCase): Regression test for #15938: if USE_THOUSAND_SEPARATOR is set, make sure that maxLength (in the JavaScript) is rendered without separators. """ - response = self.client.get(reverse('admin:admin_views_prepopulatedpostlargeslug_add')) + response = self.client.get( + reverse("admin:admin_views_prepopulatedpostlargeslug_add") + ) self.assertContains(response, ""maxLength": 1000") # instead of 1,000 def test_view_only_add_form(self): @@ -4568,38 +5399,44 @@ class PrePopulatedTest(TestCase): which is present in the add view, even if the ModelAdmin.has_change_permission() returns False. """ - response = self.client.get(reverse('admin7:admin_views_prepopulatedpost_add')) - self.assertContains(response, 'data-prepopulated-fields=') - self.assertContains(response, '"id": "#id_slug"') + response = self.client.get(reverse("admin7:admin_views_prepopulatedpost_add")) + self.assertContains(response, "data-prepopulated-fields=") + self.assertContains(response, ""id": "#id_slug"") def test_view_only_change_form(self): """ PrePopulatedPostReadOnlyAdmin.prepopulated_fields includes 'slug'. That doesn't break a view-only change view. """ - response = self.client.get(reverse('admin7:admin_views_prepopulatedpost_change', args=(self.p1.pk,))) + response = self.client.get( + reverse("admin7:admin_views_prepopulatedpost_change", args=(self.p1.pk,)) + ) self.assertContains(response, 'data-prepopulated-fields="[]"') self.assertContains(response, '<div class="readonly">%s</div>' % self.p1.slug) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class SeleniumTests(AdminSeleniumTestCase): - available_apps = ['admin_views'] + AdminSeleniumTestCase.available_apps + available_apps = ["admin_views"] + AdminSeleniumTestCase.available_apps def setUp(self): - self.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - self.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') + self.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + self.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" + ) def test_login_button_centered(self): from selenium.webdriver.common.by import By - self.selenium.get(self.live_server_url + reverse('admin:login')) - button = self.selenium.find_element(By.CSS_SELECTOR, '.submit-row input') - offset_left = button.get_property('offsetLeft') - offset_right = ( - button.get_property('offsetParent').get_property('offsetWidth') - - (offset_left + button.get_property('offsetWidth')) - ) + + self.selenium.get(self.live_server_url + reverse("admin:login")) + button = self.selenium.find_element(By.CSS_SELECTOR, ".submit-row input") + offset_left = button.get_property("offsetLeft") + offset_right = button.get_property("offsetParent").get_property( + "offsetWidth" + ) - (offset_left + button.get_property("offsetWidth")) # Use assertAlmostEqual to avoid pixel rounding errors. self.assertAlmostEqual(offset_left, offset_right, delta=3) @@ -4610,164 +5447,221 @@ class SeleniumTests(AdminSeleniumTestCase): Refs #13068, #9264, #9983, #9784. """ from selenium.webdriver.common.by import By - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - self.selenium.get(self.live_server_url + reverse('admin:admin_views_mainprepopulated_add')) - self.wait_for('.select2') + + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + self.selenium.get( + self.live_server_url + reverse("admin:admin_views_mainprepopulated_add") + ) + self.wait_for(".select2") # Main form ---------------------------------------------------------- - self.selenium.find_element(By.ID, 'id_pubdate').send_keys('2012-02-18') - self.select_option('#id_status', 'option two') - self.selenium.find_element(By.ID, 'id_name').send_keys(' the mAin nÀMë and it\'s awεšomeıııİ') - slug1 = self.selenium.find_element(By.ID, 'id_slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_slug2').get_attribute('value') - slug3 = self.selenium.find_element(By.ID, 'id_slug3').get_attribute('value') - self.assertEqual(slug1, 'the-main-name-and-its-awesomeiiii-2012-02-18') - self.assertEqual(slug2, 'option-two-the-main-name-and-its-awesomeiiii') - self.assertEqual(slug3, 'the-main-n\xe0m\xeb-and-its-aw\u03b5\u0161ome\u0131\u0131\u0131i') + self.selenium.find_element(By.ID, "id_pubdate").send_keys("2012-02-18") + self.select_option("#id_status", "option two") + self.selenium.find_element(By.ID, "id_name").send_keys( + " the mAin nÀMë and it's awεšomeıııİ" + ) + slug1 = self.selenium.find_element(By.ID, "id_slug1").get_attribute("value") + slug2 = self.selenium.find_element(By.ID, "id_slug2").get_attribute("value") + slug3 = self.selenium.find_element(By.ID, "id_slug3").get_attribute("value") + self.assertEqual(slug1, "the-main-name-and-its-awesomeiiii-2012-02-18") + self.assertEqual(slug2, "option-two-the-main-name-and-its-awesomeiiii") + self.assertEqual( + slug3, "the-main-n\xe0m\xeb-and-its-aw\u03b5\u0161ome\u0131\u0131\u0131i" + ) # Stacked inlines with fieldsets ------------------------------------- # Initial inline - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-0-pubdate').send_keys('2011-12-17') - self.select_option('#id_relatedprepopulated_set-0-status', 'option one') - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-0-name').send_keys( - ' here is a sŤāÇkeð inline ! ' - ) - slug1 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-0-slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-0-slug2').get_attribute('value') - self.assertEqual(slug1, 'here-is-a-stacked-inline-2011-12-17') - self.assertEqual(slug2, 'option-one-here-is-a-stacked-inline') - initial_select2_inputs = self.selenium.find_elements(By.CLASS_NAME, 'select2-selection') + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-0-pubdate" + ).send_keys("2011-12-17") + self.select_option("#id_relatedprepopulated_set-0-status", "option one") + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-0-name" + ).send_keys(" here is a sŤāÇkeð inline ! ") + slug1 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-0-slug1" + ).get_attribute("value") + slug2 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-0-slug2" + ).get_attribute("value") + self.assertEqual(slug1, "here-is-a-stacked-inline-2011-12-17") + self.assertEqual(slug2, "option-one-here-is-a-stacked-inline") + initial_select2_inputs = self.selenium.find_elements( + By.CLASS_NAME, "select2-selection" + ) # Inline formsets have empty/invisible forms. # Only the 4 visible select2 inputs are initialized. num_initial_select2_inputs = len(initial_select2_inputs) self.assertEqual(num_initial_select2_inputs, 4) # Add an inline - self.selenium.find_elements(By.LINK_TEXT, 'Add another Related prepopulated')[0].click() + self.selenium.find_elements(By.LINK_TEXT, "Add another Related prepopulated")[ + 0 + ].click() self.assertEqual( - len(self.selenium.find_elements(By.CLASS_NAME, 'select2-selection')), - num_initial_select2_inputs + 2 - ) - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-1-pubdate').send_keys('1999-01-25') - self.select_option('#id_relatedprepopulated_set-1-status', 'option two') - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-1-name').send_keys( - ' now you haVe anöther sŤāÇkeð inline with a very ... ' - 'loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog text... ' - ) - slug1 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-1-slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-1-slug2').get_attribute('value') + len(self.selenium.find_elements(By.CLASS_NAME, "select2-selection")), + num_initial_select2_inputs + 2, + ) + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-1-pubdate" + ).send_keys("1999-01-25") + self.select_option("#id_relatedprepopulated_set-1-status", "option two") + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-1-name" + ).send_keys( + " now you haVe anöther sŤāÇkeð inline with a very ... " + "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog text... " + ) + slug1 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-1-slug1" + ).get_attribute("value") + slug2 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-1-slug2" + ).get_attribute("value") # 50 characters maximum for slug1 field - self.assertEqual(slug1, 'now-you-have-another-stacked-inline-with-a-very-lo') + self.assertEqual(slug1, "now-you-have-another-stacked-inline-with-a-very-lo") # 60 characters maximum for slug2 field - self.assertEqual(slug2, 'option-two-now-you-have-another-stacked-inline-with-a-very-l') + self.assertEqual( + slug2, "option-two-now-you-have-another-stacked-inline-with-a-very-l" + ) # Tabular inlines ---------------------------------------------------- # Initial inline - element = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-0-status') - self.selenium.execute_script('window.scrollTo(0, %s);' % element.location['y']) - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-0-pubdate').send_keys('1234-12-07') - self.select_option('#id_relatedprepopulated_set-2-0-status', 'option two') - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-0-name').send_keys( - 'And now, with a tÃbűlaŘ inline !!!' - ) - slug1 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-0-slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-0-slug2').get_attribute('value') - self.assertEqual(slug1, 'and-now-with-a-tabular-inline-1234-12-07') - self.assertEqual(slug2, 'option-two-and-now-with-a-tabular-inline') + element = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-0-status" + ) + self.selenium.execute_script("window.scrollTo(0, %s);" % element.location["y"]) + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-0-pubdate" + ).send_keys("1234-12-07") + self.select_option("#id_relatedprepopulated_set-2-0-status", "option two") + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-0-name" + ).send_keys("And now, with a tÃbűlaŘ inline !!!") + slug1 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-0-slug1" + ).get_attribute("value") + slug2 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-0-slug2" + ).get_attribute("value") + self.assertEqual(slug1, "and-now-with-a-tabular-inline-1234-12-07") + self.assertEqual(slug2, "option-two-and-now-with-a-tabular-inline") # Add an inline # Button may be outside the browser frame. - element = self.selenium.find_elements(By.LINK_TEXT, 'Add another Related prepopulated')[1] - self.selenium.execute_script('window.scrollTo(0, %s);' % element.location['y']) + element = self.selenium.find_elements( + By.LINK_TEXT, "Add another Related prepopulated" + )[1] + self.selenium.execute_script("window.scrollTo(0, %s);" % element.location["y"]) element.click() self.assertEqual( - len(self.selenium.find_elements(By.CLASS_NAME, 'select2-selection')), - num_initial_select2_inputs + 4 - ) - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-1-pubdate').send_keys('1981-08-22') - self.select_option('#id_relatedprepopulated_set-2-1-status', 'option one') - self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-1-name').send_keys( - r'tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters' - ) - slug1 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-1-slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_relatedprepopulated_set-2-1-slug2').get_attribute('value') - self.assertEqual(slug1, 'tabular-inline-with-ignored-characters-1981-08-22') - self.assertEqual(slug2, 'option-one-tabular-inline-with-ignored-characters') + len(self.selenium.find_elements(By.CLASS_NAME, "select2-selection")), + num_initial_select2_inputs + 4, + ) + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-1-pubdate" + ).send_keys("1981-08-22") + self.select_option("#id_relatedprepopulated_set-2-1-status", "option one") + self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-1-name" + ).send_keys(r'tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters') + slug1 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-1-slug1" + ).get_attribute("value") + slug2 = self.selenium.find_element( + By.ID, "id_relatedprepopulated_set-2-1-slug2" + ).get_attribute("value") + self.assertEqual(slug1, "tabular-inline-with-ignored-characters-1981-08-22") + self.assertEqual(slug2, "option-one-tabular-inline-with-ignored-characters") # Add an inline without an initial inline. # The button is outside of the browser frame. self.selenium.execute_script("window.scrollTo(0, document.body.scrollHeight);") - self.selenium.find_elements(By.LINK_TEXT, 'Add another Related prepopulated')[2].click() + self.selenium.find_elements(By.LINK_TEXT, "Add another Related prepopulated")[ + 2 + ].click() self.assertEqual( - len(self.selenium.find_elements(By.CLASS_NAME, 'select2-selection')), - num_initial_select2_inputs + 6 + len(self.selenium.find_elements(By.CLASS_NAME, "select2-selection")), + num_initial_select2_inputs + 6, ) # Stacked Inlines without fieldsets ---------------------------------- # Initial inline. - row_id = 'id_relatedprepopulated_set-4-0-' - self.selenium.find_element(By.ID, f'{row_id}pubdate').send_keys('2011-12-12') - self.select_option(f'#{row_id}status', 'option one') - self.selenium.find_element(By.ID, f'{row_id}name').send_keys(' sŤāÇkeð inline ! ') - slug1 = self.selenium.find_element(By.ID, f'{row_id}slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, f'{row_id}slug2').get_attribute('value') - self.assertEqual(slug1, 'stacked-inline-2011-12-12') - self.assertEqual(slug2, 'option-one') + row_id = "id_relatedprepopulated_set-4-0-" + self.selenium.find_element(By.ID, f"{row_id}pubdate").send_keys("2011-12-12") + self.select_option(f"#{row_id}status", "option one") + self.selenium.find_element(By.ID, f"{row_id}name").send_keys( + " sŤāÇkeð inline ! " + ) + slug1 = self.selenium.find_element(By.ID, f"{row_id}slug1").get_attribute( + "value" + ) + slug2 = self.selenium.find_element(By.ID, f"{row_id}slug2").get_attribute( + "value" + ) + self.assertEqual(slug1, "stacked-inline-2011-12-12") + self.assertEqual(slug2, "option-one") # Add inline. self.selenium.find_elements( By.LINK_TEXT, - 'Add another Related prepopulated', + "Add another Related prepopulated", )[3].click() - row_id = 'id_relatedprepopulated_set-4-1-' - self.selenium.find_element(By.ID, f'{row_id}pubdate').send_keys('1999-01-20') - self.select_option(f'#{row_id}status', 'option two') - self.selenium.find_element(By.ID, f'{row_id}name').send_keys( - ' now you haVe anöther sŤāÇkeð inline with a very loooong ' + row_id = "id_relatedprepopulated_set-4-1-" + self.selenium.find_element(By.ID, f"{row_id}pubdate").send_keys("1999-01-20") + self.select_option(f"#{row_id}status", "option two") + self.selenium.find_element(By.ID, f"{row_id}name").send_keys( + " now you haVe anöther sŤāÇkeð inline with a very loooong " + ) + slug1 = self.selenium.find_element(By.ID, f"{row_id}slug1").get_attribute( + "value" ) - slug1 = self.selenium.find_element(By.ID, f'{row_id}slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, f'{row_id}slug2').get_attribute('value') - self.assertEqual(slug1, 'now-you-have-another-stacked-inline-with-a-very-lo') - self.assertEqual(slug2, 'option-two') + slug2 = self.selenium.find_element(By.ID, f"{row_id}slug2").get_attribute( + "value" + ) + self.assertEqual(slug1, "now-you-have-another-stacked-inline-with-a-very-lo") + self.assertEqual(slug2, "option-two") # Save and check that everything is properly stored in the database with self.wait_page_loaded(): self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.assertEqual(MainPrepopulated.objects.all().count(), 1) MainPrepopulated.objects.get( - name=' the mAin nÀMë and it\'s awεšomeıııİ', - pubdate='2012-02-18', - status='option two', - slug1='the-main-name-and-its-awesomeiiii-2012-02-18', - slug2='option-two-the-main-name-and-its-awesomeiiii', - slug3='the-main-nàmë-and-its-awεšomeıııi', + name=" the mAin nÀMë and it's awεšomeıııİ", + pubdate="2012-02-18", + status="option two", + slug1="the-main-name-and-its-awesomeiiii-2012-02-18", + slug2="option-two-the-main-name-and-its-awesomeiiii", + slug3="the-main-nàmë-and-its-awεšomeıııi", ) self.assertEqual(RelatedPrepopulated.objects.all().count(), 6) RelatedPrepopulated.objects.get( - name=' here is a sŤāÇkeð inline ! ', - pubdate='2011-12-17', - status='option one', - slug1='here-is-a-stacked-inline-2011-12-17', - slug2='option-one-here-is-a-stacked-inline', + name=" here is a sŤāÇkeð inline ! ", + pubdate="2011-12-17", + status="option one", + slug1="here-is-a-stacked-inline-2011-12-17", + slug2="option-one-here-is-a-stacked-inline", ) RelatedPrepopulated.objects.get( # 75 characters in name field - name=' now you haVe anöther sŤāÇkeð inline with a very ... loooooooooooooooooo', - pubdate='1999-01-25', - status='option two', - slug1='now-you-have-another-stacked-inline-with-a-very-lo', - slug2='option-two-now-you-have-another-stacked-inline-with-a-very-l', + name=" now you haVe anöther sŤāÇkeð inline with a very ... loooooooooooooooooo", + pubdate="1999-01-25", + status="option two", + slug1="now-you-have-another-stacked-inline-with-a-very-lo", + slug2="option-two-now-you-have-another-stacked-inline-with-a-very-l", ) RelatedPrepopulated.objects.get( - name='And now, with a tÃbűlaŘ inline !!!', - pubdate='1234-12-07', - status='option two', - slug1='and-now-with-a-tabular-inline-1234-12-07', - slug2='option-two-and-now-with-a-tabular-inline', + name="And now, with a tÃbűlaŘ inline !!!", + pubdate="1234-12-07", + status="option two", + slug1="and-now-with-a-tabular-inline-1234-12-07", + slug2="option-two-and-now-with-a-tabular-inline", ) RelatedPrepopulated.objects.get( name=r'tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters', - pubdate='1981-08-22', - status='option one', - slug1='tabular-inline-with-ignored-characters-1981-08-22', - slug2='option-one-tabular-inline-with-ignored-characters', + pubdate="1981-08-22", + status="option one", + slug1="tabular-inline-with-ignored-characters-1981-08-22", + slug2="option-one-tabular-inline-with-ignored-characters", ) def test_populate_existing_object(self): @@ -4779,37 +5673,41 @@ class SeleniumTests(AdminSeleniumTestCase): # Slugs are empty to start with. item = MainPrepopulated.objects.create( - name=' this is the mAin nÀMë', - pubdate='2012-02-18', - status='option two', - slug1='', - slug2='', + name=" this is the mAin nÀMë", + pubdate="2012-02-18", + status="option two", + slug1="", + slug2="", + ) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") ) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - object_url = self.live_server_url + reverse('admin:admin_views_mainprepopulated_change', args=(item.id,)) + object_url = self.live_server_url + reverse( + "admin:admin_views_mainprepopulated_change", args=(item.id,) + ) self.selenium.get(object_url) - self.selenium.find_element(By.ID, 'id_name').send_keys(' the best') + self.selenium.find_element(By.ID, "id_name").send_keys(" the best") # The slugs got prepopulated since they were originally empty - slug1 = self.selenium.find_element(By.ID, 'id_slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_slug2').get_attribute('value') - self.assertEqual(slug1, 'this-is-the-main-name-the-best-2012-02-18') - self.assertEqual(slug2, 'option-two-this-is-the-main-name-the-best') + slug1 = self.selenium.find_element(By.ID, "id_slug1").get_attribute("value") + slug2 = self.selenium.find_element(By.ID, "id_slug2").get_attribute("value") + self.assertEqual(slug1, "this-is-the-main-name-the-best-2012-02-18") + self.assertEqual(slug2, "option-two-this-is-the-main-name-the-best") # Save the object with self.wait_page_loaded(): self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.get(object_url) - self.selenium.find_element(By.ID, 'id_name').send_keys(' hello') + self.selenium.find_element(By.ID, "id_name").send_keys(" hello") # The slugs got prepopulated didn't change since they were originally not empty - slug1 = self.selenium.find_element(By.ID, 'id_slug1').get_attribute('value') - slug2 = self.selenium.find_element(By.ID, 'id_slug2').get_attribute('value') - self.assertEqual(slug1, 'this-is-the-main-name-the-best-2012-02-18') - self.assertEqual(slug2, 'option-two-this-is-the-main-name-the-best') + slug1 = self.selenium.find_element(By.ID, "id_slug1").get_attribute("value") + slug2 = self.selenium.find_element(By.ID, "id_slug2").get_attribute("value") + self.assertEqual(slug1, "this-is-the-main-name-the-best-2012-02-18") + self.assertEqual(slug2, "option-two-this-is-the-main-name-the-best") def test_collapsible_fieldset(self): """ @@ -4817,32 +5715,39 @@ class SeleniumTests(AdminSeleniumTestCase): show/hide the appropriate field section. """ from selenium.webdriver.common.by import By - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - self.selenium.get(self.live_server_url + reverse('admin:admin_views_article_add')) - self.assertFalse(self.selenium.find_element(By.ID, 'id_title').is_displayed()) - self.selenium.find_elements(By.LINK_TEXT, 'Show')[0].click() - self.assertTrue(self.selenium.find_element(By.ID, 'id_title').is_displayed()) - self.assertEqual(self.selenium.find_element(By.ID, 'fieldsetcollapser0').text, "Hide") + + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + self.selenium.get( + self.live_server_url + reverse("admin:admin_views_article_add") + ) + self.assertFalse(self.selenium.find_element(By.ID, "id_title").is_displayed()) + self.selenium.find_elements(By.LINK_TEXT, "Show")[0].click() + self.assertTrue(self.selenium.find_element(By.ID, "id_title").is_displayed()) + self.assertEqual( + self.selenium.find_element(By.ID, "fieldsetcollapser0").text, "Hide" + ) def test_selectbox_height_collapsible_fieldset(self): from selenium.webdriver.common.by import By self.admin_login( - username='super', - password='secret', - login_url=reverse('admin7:index'), + username="super", + password="secret", + login_url=reverse("admin7:index"), ) - url = self.live_server_url + reverse('admin7:admin_views_pizza_add') + url = self.live_server_url + reverse("admin7:admin_views_pizza_add") self.selenium.get(url) - self.selenium.find_elements(By.LINK_TEXT, 'Show')[0].click() - filter_box = self.selenium.find_element(By.ID, 'id_toppings_filter') - from_box = self.selenium.find_element(By.ID, 'id_toppings_from') - to_box = self.selenium.find_element(By.ID, 'id_toppings_to') + self.selenium.find_elements(By.LINK_TEXT, "Show")[0].click() + filter_box = self.selenium.find_element(By.ID, "id_toppings_filter") + from_box = self.selenium.find_element(By.ID, "id_toppings_from") + to_box = self.selenium.find_element(By.ID, "id_toppings_to") self.assertEqual( - to_box.get_property('offsetHeight'), + to_box.get_property("offsetHeight"), ( - filter_box.get_property('offsetHeight') + - from_box.get_property('offsetHeight') + filter_box.get_property("offsetHeight") + + from_box.get_property("offsetHeight") ), ) @@ -4850,20 +5755,20 @@ class SeleniumTests(AdminSeleniumTestCase): from selenium.webdriver.common.by import By self.admin_login( - username='super', - password='secret', - login_url=reverse('admin7:index'), + username="super", + password="secret", + login_url=reverse("admin7:index"), ) - url = self.live_server_url + reverse('admin7:admin_views_question_add') + url = self.live_server_url + reverse("admin7:admin_views_question_add") self.selenium.get(url) - filter_box = self.selenium.find_element(By.ID, 'id_related_questions_filter') - from_box = self.selenium.find_element(By.ID, 'id_related_questions_from') - to_box = self.selenium.find_element(By.ID, 'id_related_questions_to') + filter_box = self.selenium.find_element(By.ID, "id_related_questions_filter") + from_box = self.selenium.find_element(By.ID, "id_related_questions_from") + to_box = self.selenium.find_element(By.ID, "id_related_questions_to") self.assertEqual( - to_box.get_property('offsetHeight'), + to_box.get_property("offsetHeight"), ( - filter_box.get_property('offsetHeight') + - from_box.get_property('offsetHeight') + filter_box.get_property("offsetHeight") + + from_box.get_property("offsetHeight") ), ) @@ -4872,35 +5777,44 @@ class SeleniumTests(AdminSeleniumTestCase): from selenium.webdriver.common.by import By # First form field has a single widget - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) with self.wait_page_loaded(): - self.selenium.get(self.live_server_url + reverse('admin:admin_views_picture_add')) + self.selenium.get( + self.live_server_url + reverse("admin:admin_views_picture_add") + ) self.assertEqual( self.selenium.switch_to.active_element, - self.selenium.find_element(By.ID, 'id_name') + self.selenium.find_element(By.ID, "id_name"), ) # First form field has a MultiWidget with self.wait_page_loaded(): - self.selenium.get(self.live_server_url + reverse('admin:admin_views_reservation_add')) + self.selenium.get( + self.live_server_url + reverse("admin:admin_views_reservation_add") + ) self.assertEqual( self.selenium.switch_to.active_element, - self.selenium.find_element(By.ID, 'id_start_date_0') + self.selenium.find_element(By.ID, "id_start_date_0"), ) def test_cancel_delete_confirmation(self): "Cancelling the deletion of an object takes the user back one page." from selenium.webdriver.common.by import By + pizza = Pizza.objects.create(name="Double Cheese") - url = reverse('admin:admin_views_pizza_change', args=(pizza.id,)) + url = reverse("admin:admin_views_pizza_change", args=(pizza.id,)) full_url = self.live_server_url + url - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) self.selenium.get(full_url) - self.selenium.find_element(By.CLASS_NAME, 'deletelink').click() + self.selenium.find_element(By.CLASS_NAME, "deletelink").click() # Click 'cancel' on the delete page. - self.selenium.find_element(By.CLASS_NAME, 'cancel-link').click() + self.selenium.find_element(By.CLASS_NAME, "cancel-link").click() # Wait until we're back on the change page. - self.wait_for_text('#content h1', 'Change pizza') + self.wait_for_text("#content h1", "Change pizza") self.assertEqual(self.selenium.current_url, full_url) self.assertEqual(Pizza.objects.count(), 1) @@ -4910,19 +5824,22 @@ class SeleniumTests(AdminSeleniumTestCase): one page. """ from selenium.webdriver.common.by import By + pizza = Pizza.objects.create(name="Double Cheese") topping1 = Topping.objects.create(name="Cheddar") topping2 = Topping.objects.create(name="Mozzarella") pizza.toppings.add(topping1, topping2) - url = reverse('admin:admin_views_pizza_change', args=(pizza.id,)) + url = reverse("admin:admin_views_pizza_change", args=(pizza.id,)) full_url = self.live_server_url + url - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) self.selenium.get(full_url) - self.selenium.find_element(By.CLASS_NAME, 'deletelink').click() + self.selenium.find_element(By.CLASS_NAME, "deletelink").click() # Click 'cancel' on the delete page. - self.selenium.find_element(By.CLASS_NAME, 'cancel-link').click() + self.selenium.find_element(By.CLASS_NAME, "cancel-link").click() # Wait until we're back on the change page. - self.wait_for_text('#content h1', 'Change pizza') + self.wait_for_text("#content h1", "Change pizza") self.assertEqual(self.selenium.current_url, full_url) self.assertEqual(Pizza.objects.count(), 1) self.assertEqual(Topping.objects.count(), 2) @@ -4933,104 +5850,141 @@ class SeleniumTests(AdminSeleniumTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select - s1 = Section.objects.create(name='Test section') + + s1 = Section.objects.create(name="Test section") Article.objects.create( - title='foo', - content='<p>Middle content</p>', + title="foo", + content="<p>Middle content</p>", date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=s1, ) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - self.selenium.get(self.live_server_url + reverse('admin:admin_views_article_changelist')) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + self.selenium.get( + self.live_server_url + reverse("admin:admin_views_article_changelist") + ) # Change popup - self.selenium.find_element(By.ID, 'change_id_form-0-section').click() + self.selenium.find_element(By.ID, "change_id_form-0-section").click() self.wait_for_and_switch_to_popup() - self.wait_for_text('#content h1', 'Change section') - name_input = self.selenium.find_element(By.ID, 'id_name') + self.wait_for_text("#content h1", "Change section") + name_input = self.selenium.find_element(By.ID, "id_name") name_input.clear() - name_input.send_keys('<i>edited section</i>') + name_input.send_keys("<i>edited section</i>") self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(self.selenium.window_handles[0]) # Hide sidebar. - toggle_button = self.selenium.find_element(By.CSS_SELECTOR, '#toggle-nav-sidebar') + toggle_button = self.selenium.find_element( + By.CSS_SELECTOR, "#toggle-nav-sidebar" + ) toggle_button.click() - select = Select(self.selenium.find_element(By.ID, 'id_form-0-section')) - self.assertEqual(select.first_selected_option.text, '<i>edited section</i>') + select = Select(self.selenium.find_element(By.ID, "id_form-0-section")) + self.assertEqual(select.first_selected_option.text, "<i>edited section</i>") # Rendered select2 input. - select2_display = self.selenium.find_element(By.CLASS_NAME, 'select2-selection__rendered') + select2_display = self.selenium.find_element( + By.CLASS_NAME, "select2-selection__rendered" + ) # Clear button (×\n) is included in text. - self.assertEqual(select2_display.text, '×\n<i>edited section</i>') + self.assertEqual(select2_display.text, "×\n<i>edited section</i>") # Add popup - self.selenium.find_element(By.ID, 'add_id_form-0-section').click() + self.selenium.find_element(By.ID, "add_id_form-0-section").click() self.wait_for_and_switch_to_popup() - self.wait_for_text('#content h1', 'Add section') - self.selenium.find_element(By.ID, 'id_name').send_keys('new section') + self.wait_for_text("#content h1", "Add section") + self.selenium.find_element(By.ID, "id_name").send_keys("new section") self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(self.selenium.window_handles[0]) - select = Select(self.selenium.find_element(By.ID, 'id_form-0-section')) - self.assertEqual(select.first_selected_option.text, 'new section') - select2_display = self.selenium.find_element(By.CLASS_NAME, 'select2-selection__rendered') + select = Select(self.selenium.find_element(By.ID, "id_form-0-section")) + self.assertEqual(select.first_selected_option.text, "new section") + select2_display = self.selenium.find_element( + By.CLASS_NAME, "select2-selection__rendered" + ) # Clear button (×\n) is included in text. - self.assertEqual(select2_display.text, '×\nnew section') + self.assertEqual(select2_display.text, "×\nnew section") def test_inline_uuid_pk_edit_with_popup(self): from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select - parent = ParentWithUUIDPK.objects.create(title='test') + + parent = ParentWithUUIDPK.objects.create(title="test") related_with_parent = RelatedWithUUIDPKModel.objects.create(parent=parent) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - change_url = reverse('admin:admin_views_relatedwithuuidpkmodel_change', args=(related_with_parent.id,)) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + change_url = reverse( + "admin:admin_views_relatedwithuuidpkmodel_change", + args=(related_with_parent.id,), + ) self.selenium.get(self.live_server_url + change_url) - self.selenium.find_element(By.ID, 'change_id_parent').click() + self.selenium.find_element(By.ID, "change_id_parent").click() self.wait_for_and_switch_to_popup() self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(self.selenium.window_handles[0]) - select = Select(self.selenium.find_element(By.ID, 'id_parent')) + select = Select(self.selenium.find_element(By.ID, "id_parent")) self.assertEqual(select.first_selected_option.text, str(parent.id)) - self.assertEqual(select.first_selected_option.get_attribute('value'), str(parent.id)) + self.assertEqual( + select.first_selected_option.get_attribute("value"), str(parent.id) + ) def test_inline_uuid_pk_add_with_popup(self): from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - self.selenium.get(self.live_server_url + reverse('admin:admin_views_relatedwithuuidpkmodel_add')) - self.selenium.find_element(By.ID, 'add_id_parent').click() + + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + self.selenium.get( + self.live_server_url + + reverse("admin:admin_views_relatedwithuuidpkmodel_add") + ) + self.selenium.find_element(By.ID, "add_id_parent").click() self.wait_for_and_switch_to_popup() - self.selenium.find_element(By.ID, 'id_title').send_keys('test') + self.selenium.find_element(By.ID, "id_title").send_keys("test") self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(self.selenium.window_handles[0]) - select = Select(self.selenium.find_element(By.ID, 'id_parent')) + select = Select(self.selenium.find_element(By.ID, "id_parent")) uuid_id = str(ParentWithUUIDPK.objects.first().id) self.assertEqual(select.first_selected_option.text, uuid_id) - self.assertEqual(select.first_selected_option.get_attribute('value'), uuid_id) + self.assertEqual(select.first_selected_option.get_attribute("value"), uuid_id) def test_inline_uuid_pk_delete_with_popup(self): from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select - parent = ParentWithUUIDPK.objects.create(title='test') + + parent = ParentWithUUIDPK.objects.create(title="test") related_with_parent = RelatedWithUUIDPKModel.objects.create(parent=parent) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - change_url = reverse('admin:admin_views_relatedwithuuidpkmodel_change', args=(related_with_parent.id,)) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + change_url = reverse( + "admin:admin_views_relatedwithuuidpkmodel_change", + args=(related_with_parent.id,), + ) self.selenium.get(self.live_server_url + change_url) - self.selenium.find_element(By.ID, 'delete_id_parent').click() + self.selenium.find_element(By.ID, "delete_id_parent").click() self.wait_for_and_switch_to_popup() self.selenium.find_element(By.XPATH, '//input[@value="Yes, I’m sure"]').click() self.selenium.switch_to.window(self.selenium.window_handles[0]) - select = Select(self.selenium.find_element(By.ID, 'id_parent')) + select = Select(self.selenium.find_element(By.ID, "id_parent")) self.assertEqual(ParentWithUUIDPK.objects.count(), 0) - self.assertEqual(select.first_selected_option.text, '---------') - self.assertEqual(select.first_selected_option.get_attribute('value'), '') + self.assertEqual(select.first_selected_option.text, "---------") + self.assertEqual(select.first_selected_option.get_attribute("value"), "") def test_inline_with_popup_cancel_delete(self): """Clicking ""No, take me back" on a delete popup closes the window.""" from selenium.webdriver.common.by import By - parent = ParentWithUUIDPK.objects.create(title='test') + + parent = ParentWithUUIDPK.objects.create(title="test") related_with_parent = RelatedWithUUIDPKModel.objects.create(parent=parent) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - change_url = reverse('admin:admin_views_relatedwithuuidpkmodel_change', args=(related_with_parent.id,)) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + change_url = reverse( + "admin:admin_views_relatedwithuuidpkmodel_change", + args=(related_with_parent.id,), + ) self.selenium.get(self.live_server_url + change_url) - self.selenium.find_element(By.ID, 'delete_id_parent').click() + self.selenium.find_element(By.ID, "delete_id_parent").click() self.wait_for_and_switch_to_popup() self.selenium.find_element(By.XPATH, '//a[text()="No, take me back"]').click() self.selenium.switch_to.window(self.selenium.window_handles[0]) @@ -5038,19 +5992,27 @@ class SeleniumTests(AdminSeleniumTestCase): def test_list_editable_raw_id_fields(self): from selenium.webdriver.common.by import By - parent = ParentWithUUIDPK.objects.create(title='test') - parent2 = ParentWithUUIDPK.objects.create(title='test2') + + parent = ParentWithUUIDPK.objects.create(title="test") + parent2 = ParentWithUUIDPK.objects.create(title="test2") RelatedWithUUIDPKModel.objects.create(parent=parent) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - change_url = reverse('admin:admin_views_relatedwithuuidpkmodel_changelist', current_app=site2.name) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + change_url = reverse( + "admin:admin_views_relatedwithuuidpkmodel_changelist", + current_app=site2.name, + ) self.selenium.get(self.live_server_url + change_url) - self.selenium.find_element(By.ID, 'lookup_id_form-0-parent').click() + self.selenium.find_element(By.ID, "lookup_id_form-0-parent").click() self.wait_for_and_switch_to_popup() # Select "parent2" in the popup. self.selenium.find_element(By.LINK_TEXT, str(parent2.pk)).click() self.selenium.switch_to.window(self.selenium.window_handles[0]) # The newly selected pk should appear in the raw id input. - value = self.selenium.find_element(By.ID, 'id_form-0-parent').get_attribute('value') + value = self.selenium.find_element(By.ID, "id_form-0-parent").get_attribute( + "value" + ) self.assertEqual(value, str(parent2.pk)) def test_input_element_font(self): @@ -5059,27 +6021,31 @@ class SeleniumTests(AdminSeleniumTestCase): adds additional CSS to handle this. """ from selenium.webdriver.common.by import By - self.selenium.get(self.live_server_url + reverse('admin:login')) - element = self.selenium.find_element(By.ID, 'id_username') + + self.selenium.get(self.live_server_url + reverse("admin:login")) + element = self.selenium.find_element(By.ID, "id_username") # Some browsers quotes the fonts, some don't. fonts = [ font.strip().strip('"') - for font in element.value_of_css_property('font-family').split(',') + for font in element.value_of_css_property("font-family").split(",") ] self.assertEqual( fonts, - ['Roboto', 'Lucida Grande', 'Verdana', 'Arial', 'sans-serif'], + ["Roboto", "Lucida Grande", "Verdana", "Arial", "sans-serif"], ) def test_search_input_filtered_page(self): from selenium.webdriver.common.by import By - Person.objects.create(name='Guido van Rossum', gender=1, alive=True) - Person.objects.create(name='Grace Hopper', gender=1, alive=False) - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - person_url = reverse('admin:admin_views_person_changelist') + '?q=Gui' + + Person.objects.create(name="Guido van Rossum", gender=1, alive=True) + Person.objects.create(name="Grace Hopper", gender=1, alive=False) + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + person_url = reverse("admin:admin_views_person_changelist") + "?q=Gui" self.selenium.get(self.live_server_url + person_url) self.assertGreater( - self.selenium.find_element(By.ID, 'searchbar').rect['width'], + self.selenium.find_element(By.ID, "searchbar").rect["width"], 50, ) @@ -5089,63 +6055,75 @@ class SeleniumTests(AdminSeleniumTestCase): """ from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - add_url = reverse('admin:admin_views_box_add', current_app=site.name) + + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + add_url = reverse("admin:admin_views_box_add", current_app=site.name) self.selenium.get(self.live_server_url + add_url) base_window = self.selenium.current_window_handle - self.selenium.find_element(By.ID, 'add_id_next_box').click() + self.selenium.find_element(By.ID, "add_id_next_box").click() self.wait_for_and_switch_to_popup() popup_window_test = self.selenium.current_window_handle - self.selenium.find_element(By.ID, 'id_title').send_keys('test') - self.selenium.find_element(By.ID, 'add_id_next_box').click() + self.selenium.find_element(By.ID, "id_title").send_keys("test") + self.selenium.find_element(By.ID, "add_id_next_box").click() self.wait_for_and_switch_to_popup(num_windows=3) popup_window_test2 = self.selenium.current_window_handle - self.selenium.find_element(By.ID, 'id_title').send_keys('test2') - self.selenium.find_element(By.ID, 'add_id_next_box').click() + self.selenium.find_element(By.ID, "id_title").send_keys("test2") + self.selenium.find_element(By.ID, "add_id_next_box").click() self.wait_for_and_switch_to_popup(num_windows=4) - self.selenium.find_element(By.ID, 'id_title').send_keys('test3') + self.selenium.find_element(By.ID, "id_title").send_keys("test3") self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(popup_window_test2) - select = Select(self.selenium.find_element(By.ID, 'id_next_box')) + select = Select(self.selenium.find_element(By.ID, "id_next_box")) next_box_id = str(Box.objects.get(title="test3").id) - self.assertEqual(select.first_selected_option.get_attribute('value'), next_box_id) + self.assertEqual( + select.first_selected_option.get_attribute("value"), next_box_id + ) self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(popup_window_test) - select = Select(self.selenium.find_element(By.ID, 'id_next_box')) + select = Select(self.selenium.find_element(By.ID, "id_next_box")) next_box_id = str(Box.objects.get(title="test2").id) - self.assertEqual(select.first_selected_option.get_attribute('value'), next_box_id) + self.assertEqual( + select.first_selected_option.get_attribute("value"), next_box_id + ) self.selenium.find_element(By.XPATH, '//input[@value="Save"]').click() self.selenium.switch_to.window(base_window) - select = Select(self.selenium.find_element(By.ID, 'id_next_box')) + select = Select(self.selenium.find_element(By.ID, "id_next_box")) next_box_id = str(Box.objects.get(title="test").id) - self.assertEqual(select.first_selected_option.get_attribute('value'), next_box_id) + self.assertEqual( + select.first_selected_option.get_attribute("value"), next_box_id + ) def test_related_popup_incorrect_close(self): """ Cleanup child popups when closing a parent popup. """ from selenium.webdriver.common.by import By - self.admin_login(username='super', password='secret', login_url=reverse('admin:index')) - add_url = reverse('admin:admin_views_box_add', current_app=site.name) + + self.admin_login( + username="super", password="secret", login_url=reverse("admin:index") + ) + add_url = reverse("admin:admin_views_box_add", current_app=site.name) self.selenium.get(self.live_server_url + add_url) - self.selenium.find_element(By.ID, 'add_id_next_box').click() + self.selenium.find_element(By.ID, "add_id_next_box").click() self.wait_for_and_switch_to_popup() test_window = self.selenium.current_window_handle - self.selenium.find_element(By.ID, 'id_title').send_keys('test') - self.selenium.find_element(By.ID, 'add_id_next_box').click() + self.selenium.find_element(By.ID, "id_title").send_keys("test") + self.selenium.find_element(By.ID, "add_id_next_box").click() self.wait_for_and_switch_to_popup(num_windows=3) test2_window = self.selenium.current_window_handle - self.selenium.find_element(By.ID, 'id_title').send_keys('test2') - self.selenium.find_element(By.ID, 'add_id_next_box').click() + self.selenium.find_element(By.ID, "id_title").send_keys("test2") + self.selenium.find_element(By.ID, "add_id_next_box").click() self.wait_for_and_switch_to_popup(num_windows=4) self.assertEqual(len(self.selenium.window_handles), 4) @@ -5164,12 +6142,12 @@ class SeleniumTests(AdminSeleniumTestCase): from selenium.webdriver.common.by import By self.admin_login( - username='super', - password='secret', - login_url=reverse('admin:index'), + username="super", + password="secret", + login_url=reverse("admin:index"), ) - self.selenium.get(self.live_server_url + reverse('admin:admin_views_story_add')) - field_title = self.selenium.find_element(By.CLASS_NAME, 'field-title') + self.selenium.get(self.live_server_url + reverse("admin:admin_views_story_add")) + field_title = self.selenium.find_element(By.CLASS_NAME, "field-title") current_size = self.selenium.get_window_size() try: self.selenium.set_window_size(1024, 768) @@ -5177,21 +6155,22 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.set_window_size(767, 575) self.assertIs(field_title.is_displayed(), False) finally: - self.selenium.set_window_size(current_size['width'], current_size['height']) + self.selenium.set_window_size(current_size["width"], current_size["height"]) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class ReadonlyTest(AdminFieldExtractionMixin, TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_readonly_get(self): - response = self.client.get(reverse('admin:admin_views_post_add')) + response = self.client.get(reverse("admin:admin_views_post_add")) self.assertNotContains(response, 'name="posted"') # 3 fields + 2 submit buttons + 5 inline management form fields, + 2 # hidden fields for inlines + 1 field for the inline + 2 empty form @@ -5203,11 +6182,18 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): self.assertContains(response, "foo") # Multiline text in a readonly field gets <br> tags - self.assertContains(response, 'Multiline<br>test<br>string') - self.assertContains(response, '<div class="readonly">Multiline<br>html<br>content</div>', html=True) - self.assertContains(response, 'InlineMultiline<br>test<br>string') + self.assertContains(response, "Multiline<br>test<br>string") + self.assertContains( + response, + '<div class="readonly">Multiline<br>html<br>content</div>', + html=True, + ) + self.assertContains(response, "InlineMultiline<br>test<br>string") - self.assertContains(response, formats.localize(datetime.date.today() - datetime.timedelta(days=7))) + self.assertContains( + response, + formats.localize(datetime.date.today() - datetime.timedelta(days=7)), + ) self.assertContains(response, '<div class="form-row field-coolness">') self.assertContains(response, '<div class="form-row field-awesomeness_level">') @@ -5218,37 +6204,45 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): self.assertContains( response, '<div class="help">Some help text for the title (with Unicode ŠĐĆŽćžšđ)</div>', - html=True + html=True, ) self.assertContains( response, '<div class="help">Some help text for the content (with Unicode ŠĐĆŽćžšđ)</div>', - html=True + html=True, ) self.assertContains( response, '<div class="help">Some help text for the date (with Unicode ŠĐĆŽćžšđ)</div>', - html=True + html=True, ) - p = Post.objects.create(title="I worked on readonly_fields", content="Its good stuff") - response = self.client.get(reverse('admin:admin_views_post_change', args=(p.pk,))) + p = Post.objects.create( + title="I worked on readonly_fields", content="Its good stuff" + ) + response = self.client.get( + reverse("admin:admin_views_post_change", args=(p.pk,)) + ) self.assertContains(response, "%d amount of cool" % p.pk) def test_readonly_text_field(self): p = Post.objects.create( - title="Readonly test", content="test", - readonly_content='test\r\n\r\ntest\r\n\r\ntest\r\n\r\ntest', + title="Readonly test", + content="test", + readonly_content="test\r\n\r\ntest\r\n\r\ntest\r\n\r\ntest", ) Link.objects.create( - url="http://www.djangoproject.com", post=p, + url="http://www.djangoproject.com", + post=p, readonly_link_content="test\r\nlink", ) - response = self.client.get(reverse('admin:admin_views_post_change', args=(p.pk,))) + response = self.client.get( + reverse("admin:admin_views_post_change", args=(p.pk,)) + ) # Checking readonly field. - self.assertContains(response, 'test<br><br>test<br><br>test<br><br>test') + self.assertContains(response, "test<br><br>test<br><br>test<br><br>test") # Checking readonly field in inline. - self.assertContains(response, 'test<br>link') + self.assertContains(response, "test<br>link") def test_readonly_post(self): data = { @@ -5258,27 +6252,29 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): "link_set-INITIAL_FORMS": "0", "link_set-MAX_NUM_FORMS": "0", } - response = self.client.post(reverse('admin:admin_views_post_add'), data) + response = self.client.post(reverse("admin:admin_views_post_add"), data) self.assertEqual(response.status_code, 302) self.assertEqual(Post.objects.count(), 1) p = Post.objects.get() self.assertEqual(p.posted, datetime.date.today()) data["posted"] = "10-8-1990" # some date that's not today - response = self.client.post(reverse('admin:admin_views_post_add'), data) + response = self.client.post(reverse("admin:admin_views_post_add"), data) self.assertEqual(response.status_code, 302) self.assertEqual(Post.objects.count(), 2) - p = Post.objects.order_by('-id')[0] + p = Post.objects.order_by("-id")[0] self.assertEqual(p.posted, datetime.date.today()) def test_readonly_manytomany(self): "Regression test for #13004" - response = self.client.get(reverse('admin:admin_views_pizza_add')) + response = self.client.get(reverse("admin:admin_views_pizza_add")) self.assertEqual(response.status_code, 200) def test_user_password_change_limited_queryset(self): su = User.objects.filter(is_superuser=True)[0] - response = self.client.get(reverse('admin2:auth_user_password_change', args=(su.pk,))) + response = self.client.get( + reverse("admin2:auth_user_password_change", args=(su.pk,)) + ) self.assertEqual(response.status_code, 404) def test_change_form_renders_correct_null_choice_value(self): @@ -5286,8 +6282,12 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): Regression test for #17911. """ choice = Choice.objects.create(choice=None) - response = self.client.get(reverse('admin:admin_views_choice_change', args=(choice.pk,))) - self.assertContains(response, '<div class="readonly">No opinion</div>', html=True) + response = self.client.get( + reverse("admin:admin_views_choice_change", args=(choice.pk,)) + ) + self.assertContains( + response, '<div class="readonly">No opinion</div>', html=True + ) def _test_readonly_foreignkey_links(self, admin_site): """ @@ -5295,21 +6295,23 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): registered in admin. """ chapter = Chapter.objects.create( - title='Chapter 1', - content='content', - book=Book.objects.create(name='Book 1'), + title="Chapter 1", + content="content", + book=Book.objects.create(name="Book 1"), ) - language = Language.objects.create(iso='_40', name='Test') + language = Language.objects.create(iso="_40", name="Test") obj = ReadOnlyRelatedField.objects.create( chapter=chapter, language=language, user=self.superuser, ) response = self.client.get( - reverse(f'{admin_site}:admin_views_readonlyrelatedfield_change', args=(obj.pk,)), + reverse( + f"{admin_site}:admin_views_readonlyrelatedfield_change", args=(obj.pk,) + ), ) # Related ForeignKey object registered in admin. - user_url = reverse(f'{admin_site}:auth_user_change', args=(self.superuser.pk,)) + user_url = reverse(f"{admin_site}:auth_user_change", args=(self.superuser.pk,)) self.assertContains( response, '<div class="readonly"><a href="%s">super</a></div>' % user_url, @@ -5317,7 +6319,7 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): ) # Related ForeignKey with the string primary key registered in admin. language_url = reverse( - f'{admin_site}:admin_views_language_change', + f"{admin_site}:admin_views_language_change", args=(quote(language.pk),), ) self.assertContains( @@ -5326,53 +6328,61 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): html=True, ) # Related ForeignKey object not registered in admin. - self.assertContains(response, '<div class="readonly">Chapter 1</div>', html=True) + self.assertContains( + response, '<div class="readonly">Chapter 1</div>', html=True + ) def test_readonly_foreignkey_links_default_admin_site(self): - self._test_readonly_foreignkey_links('admin') + self._test_readonly_foreignkey_links("admin") def test_readonly_foreignkey_links_custom_admin_site(self): - self._test_readonly_foreignkey_links('namespaced_admin') + self._test_readonly_foreignkey_links("namespaced_admin") def test_readonly_manytomany_backwards_ref(self): """ Regression test for #16433 - backwards references for related objects broke if the related field is read-only due to the help_text attribute """ - topping = Topping.objects.create(name='Salami') - pizza = Pizza.objects.create(name='Americano') + topping = Topping.objects.create(name="Salami") + pizza = Pizza.objects.create(name="Americano") pizza.toppings.add(topping) - response = self.client.get(reverse('admin:admin_views_topping_add')) + response = self.client.get(reverse("admin:admin_views_topping_add")) self.assertEqual(response.status_code, 200) def test_readonly_manytomany_forwards_ref(self): - topping = Topping.objects.create(name='Salami') - pizza = Pizza.objects.create(name='Americano') + topping = Topping.objects.create(name="Salami") + pizza = Pizza.objects.create(name="Americano") pizza.toppings.add(topping) - response = self.client.get(reverse('admin:admin_views_pizza_change', args=(pizza.pk,))) - self.assertContains(response, '<label>Toppings:</label>', html=True) + response = self.client.get( + reverse("admin:admin_views_pizza_change", args=(pizza.pk,)) + ) + self.assertContains(response, "<label>Toppings:</label>", html=True) self.assertContains(response, '<div class="readonly">Salami</div>', html=True) def test_readonly_onetoone_backwards_ref(self): """ Can reference a reverse OneToOneField in ModelAdmin.readonly_fields. """ - v1 = Villain.objects.create(name='Adam') - pl = Plot.objects.create(name='Test Plot', team_leader=v1, contact=v1) - pd = PlotDetails.objects.create(details='Brand New Plot', plot=pl) + v1 = Villain.objects.create(name="Adam") + pl = Plot.objects.create(name="Test Plot", team_leader=v1, contact=v1) + pd = PlotDetails.objects.create(details="Brand New Plot", plot=pl) - response = self.client.get(reverse('admin:admin_views_plotproxy_change', args=(pl.pk,))) - field = self.get_admin_readonly_field(response, 'plotdetails') - pd_url = reverse('admin:admin_views_plotdetails_change', args=(pd.pk,)) + response = self.client.get( + reverse("admin:admin_views_plotproxy_change", args=(pl.pk,)) + ) + field = self.get_admin_readonly_field(response, "plotdetails") + pd_url = reverse("admin:admin_views_plotdetails_change", args=(pd.pk,)) self.assertEqual(field.contents(), '<a href="%s">Brand New Plot</a>' % pd_url) # The reverse relation also works if the OneToOneField is null. pd.plot = None pd.save() - response = self.client.get(reverse('admin:admin_views_plotproxy_change', args=(pl.pk,))) - field = self.get_admin_readonly_field(response, 'plotdetails') - self.assertEqual(field.contents(), '-') # default empty value + response = self.client.get( + reverse("admin:admin_views_plotproxy_change", args=(pl.pk,)) + ) + field = self.get_admin_readonly_field(response, "plotdetails") + self.assertEqual(field.contents(), "-") # default empty value def test_readonly_field_overrides(self): """ @@ -5380,34 +6390,47 @@ class ReadonlyTest(AdminFieldExtractionMixin, TestCase): AdminReadonlyField """ p = FieldOverridePost.objects.create(title="Test Post", content="Test Content") - response = self.client.get(reverse('admin:admin_views_fieldoverridepost_change', args=(p.pk,))) - self.assertContains(response, '<div class="help">Overridden help text for the date</div>') - self.assertContains(response, '<label for="id_public">Overridden public label:</label>', html=True) - self.assertNotContains(response, 'Some help text for the date (with Unicode ŠĐĆŽćžšđ)') + response = self.client.get( + reverse("admin:admin_views_fieldoverridepost_change", args=(p.pk,)) + ) + self.assertContains( + response, '<div class="help">Overridden help text for the date</div>' + ) + self.assertContains( + response, + '<label for="id_public">Overridden public label:</label>', + html=True, + ) + self.assertNotContains( + response, "Some help text for the date (with Unicode ŠĐĆŽćžšđ)" + ) def test_correct_autoescaping(self): """ Make sure that non-field readonly elements are properly autoescaped (#24461) """ - section = Section.objects.create(name='<a>evil</a>') - response = self.client.get(reverse('admin:admin_views_section_change', args=(section.pk,))) + section = Section.objects.create(name="<a>evil</a>") + response = self.client.get( + reverse("admin:admin_views_section_change", args=(section.pk,)) + ) self.assertNotContains(response, "<a>evil</a>", status_code=200) self.assertContains(response, "<a>evil</a>", status_code=200) def test_label_suffix_translated(self): - pizza = Pizza.objects.create(name='Americano') - url = reverse('admin:admin_views_pizza_change', args=(pizza.pk,)) - with self.settings(LANGUAGE_CODE='fr'): + pizza = Pizza.objects.create(name="Americano") + url = reverse("admin:admin_views_pizza_change", args=(pizza.pk,)) + with self.settings(LANGUAGE_CODE="fr"): response = self.client.get(url) - self.assertContains(response, '<label>Toppings\u00A0:</label>', html=True) + self.assertContains(response, "<label>Toppings\u00A0:</label>", html=True) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class LimitChoicesToInAdminTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -5415,25 +6438,26 @@ class LimitChoicesToInAdminTest(TestCase): def test_limit_choices_to_as_callable(self): """Test for ticket 2445 changes to admin.""" threepwood = Character.objects.create( - username='threepwood', + username="threepwood", last_action=datetime.datetime.today() + datetime.timedelta(days=1), ) marley = Character.objects.create( - username='marley', + username="marley", last_action=datetime.datetime.today() - datetime.timedelta(days=1), ) - response = self.client.get(reverse('admin:admin_views_stumpjoke_add')) + response = self.client.get(reverse("admin:admin_views_stumpjoke_add")) # The allowed option should appear twice; the limited option should not appear. self.assertContains(response, threepwood.username, count=2) self.assertNotContains(response, marley.username) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class RawIdFieldsTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -5441,20 +6465,18 @@ class RawIdFieldsTest(TestCase): def test_limit_choices_to(self): """Regression test for 14880""" actor = Actor.objects.create(name="Palin", age=27) - Inquisition.objects.create(expected=True, - leader=actor, - country="England") - Inquisition.objects.create(expected=False, - leader=actor, - country="Spain") - response = self.client.get(reverse('admin:admin_views_sketch_add')) + Inquisition.objects.create(expected=True, leader=actor, country="England") + Inquisition.objects.create(expected=False, leader=actor, country="Spain") + response = self.client.get(reverse("admin:admin_views_sketch_add")) # Find the link - m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_inquisition"', response.content) + m = re.search( + rb'<a href="([^"]*)"[^>]* id="lookup_id_inquisition"', response.content + ) self.assertTrue(m) # Got a match - popup_url = m[1].decode().replace('&', '&') + popup_url = m[1].decode().replace("&", "&") # Handle relative links - popup_url = urljoin(response.request['PATH_INFO'], popup_url) + popup_url = urljoin(response.request["PATH_INFO"], popup_url) # Get the popup and verify the correct objects show up in the resulting # page. This step also tests integers, strings and booleans in the # lookup query string; in model we define inquisition field to have a @@ -5470,14 +6492,16 @@ class RawIdFieldsTest(TestCase): """Regression test for 20182""" Actor.objects.create(name="Palin", age=27) Actor.objects.create(name="Kilbraken", age=50, title="Judge") - response = self.client.get(reverse('admin:admin_views_sketch_add')) + response = self.client.get(reverse("admin:admin_views_sketch_add")) # Find the link - m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_defendant0"', response.content) + m = re.search( + rb'<a href="([^"]*)"[^>]* id="lookup_id_defendant0"', response.content + ) self.assertTrue(m) # Got a match - popup_url = m[1].decode().replace('&', '&') + popup_url = m[1].decode().replace("&", "&") # Handle relative links - popup_url = urljoin(response.request['PATH_INFO'], popup_url) + popup_url = urljoin(response.request["PATH_INFO"], popup_url) # Get the popup and verify the correct objects show up in the resulting # page. This step tests field__isnull=0 gets parsed correctly from the # lookup query string; in model we define defendant0 field to have a @@ -5490,14 +6514,16 @@ class RawIdFieldsTest(TestCase): """Regression test for 20182""" Actor.objects.create(name="Palin", age=27) Actor.objects.create(name="Kilbraken", age=50, title="Judge") - response = self.client.get(reverse('admin:admin_views_sketch_add')) + response = self.client.get(reverse("admin:admin_views_sketch_add")) # Find the link - m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_defendant1"', response.content) + m = re.search( + rb'<a href="([^"]*)"[^>]* id="lookup_id_defendant1"', response.content + ) self.assertTrue(m) # Got a match - popup_url = m[1].decode().replace('&', '&') + popup_url = m[1].decode().replace("&", "&") # Handle relative links - popup_url = urljoin(response.request['PATH_INFO'], popup_url) + popup_url = urljoin(response.request["PATH_INFO"], popup_url) # Get the popup and verify the correct objects show up in the resulting # page. This step tests field__isnull=1 gets parsed correctly from the # lookup query string; in model we define defendant1 field to have a @@ -5513,11 +6539,11 @@ class RawIdFieldsTest(TestCase): """ actor = Actor.objects.create(name="Palin", age=27) Inquisition.objects.create(expected=True, leader=actor, country="England") - response = self.client.get(reverse('admin:admin_views_inquisition_changelist')) - self.assertContains(response, 'list-display-sketch') + response = self.client.get(reverse("admin:admin_views_inquisition_changelist")) + self.assertContains(response, "list-display-sketch") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class UserAdminTest(TestCase): """ Tests user CRUD functionality. @@ -5525,50 +6551,72 @@ class UserAdminTest(TestCase): @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.adduser = User.objects.create_user(username='adduser', password='secret', is_staff=True) - cls.changeuser = User.objects.create_user(username='changeuser', password='secret', is_staff=True) - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.adduser = User.objects.create_user( + username="adduser", password="secret", is_staff=True + ) + cls.changeuser = User.objects.create_user( + username="changeuser", password="secret", is_staff=True + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') - cls.per1 = Person.objects.create(name='John Mauchly', gender=1, alive=True) - cls.per2 = Person.objects.create(name='Grace Hopper', gender=1, alive=False) - cls.per3 = Person.objects.create(name='Guido van Rossum', gender=1, alive=True) + cls.per1 = Person.objects.create(name="John Mauchly", gender=1, alive=True) + cls.per2 = Person.objects.create(name="Grace Hopper", gender=1, alive=False) + cls.per3 = Person.objects.create(name="Guido van Rossum", gender=1, alive=True) def setUp(self): self.client.force_login(self.superuser) def test_save_button(self): user_count = User.objects.count() - response = self.client.post(reverse('admin:auth_user_add'), { - 'username': 'newuser', - 'password1': 'newpassword', - 'password2': 'newpassword', - }) - new_user = User.objects.get(username='newuser') - self.assertRedirects(response, reverse('admin:auth_user_change', args=(new_user.pk,))) + response = self.client.post( + reverse("admin:auth_user_add"), + { + "username": "newuser", + "password1": "newpassword", + "password2": "newpassword", + }, + ) + new_user = User.objects.get(username="newuser") + self.assertRedirects( + response, reverse("admin:auth_user_change", args=(new_user.pk,)) + ) self.assertEqual(User.objects.count(), user_count + 1) self.assertTrue(new_user.has_usable_password()) def test_save_continue_editing_button(self): user_count = User.objects.count() - response = self.client.post(reverse('admin:auth_user_add'), { - 'username': 'newuser', - 'password1': 'newpassword', - 'password2': 'newpassword', - '_continue': '1', - }) - new_user = User.objects.get(username='newuser') - new_user_url = reverse('admin:auth_user_change', args=(new_user.pk,)) + response = self.client.post( + reverse("admin:auth_user_add"), + { + "username": "newuser", + "password1": "newpassword", + "password2": "newpassword", + "_continue": "1", + }, + ) + new_user = User.objects.get(username="newuser") + new_user_url = reverse("admin:auth_user_change", args=(new_user.pk,)) self.assertRedirects(response, new_user_url, fetch_redirect_response=False) self.assertEqual(User.objects.count(), user_count + 1) self.assertTrue(new_user.has_usable_password()) @@ -5576,90 +6624,122 @@ class UserAdminTest(TestCase): self.assertContains( response, '<li class="success">The user “<a href="%s">' - '%s</a>” was added successfully. You may edit it again below.</li>' + "%s</a>” was added successfully. You may edit it again below.</li>" % (new_user_url, new_user), html=True, ) def test_password_mismatch(self): - response = self.client.post(reverse('admin:auth_user_add'), { - 'username': 'newuser', - 'password1': 'newpassword', - 'password2': 'mismatch', - }) + response = self.client.post( + reverse("admin:auth_user_add"), + { + "username": "newuser", + "password1": "newpassword", + "password2": "mismatch", + }, + ) self.assertEqual(response.status_code, 200) - self.assertFormError(response, 'adminform', 'password', []) - self.assertFormError(response, 'adminform', 'password2', ['The two password fields didn’t match.']) + self.assertFormError(response, "adminform", "password", []) + self.assertFormError( + response, + "adminform", + "password2", + ["The two password fields didn’t match."], + ) def test_user_fk_add_popup(self): """User addition through a FK popup should return the appropriate JavaScript response.""" - response = self.client.get(reverse('admin:admin_views_album_add')) - self.assertContains(response, reverse('admin:auth_user_add')) - self.assertContains(response, 'class="related-widget-wrapper-link add-related" id="add_id_owner"') - response = self.client.get(reverse('admin:auth_user_add') + '?%s=1' % IS_POPUP_VAR) + response = self.client.get(reverse("admin:admin_views_album_add")) + self.assertContains(response, reverse("admin:auth_user_add")) + self.assertContains( + response, + 'class="related-widget-wrapper-link add-related" id="add_id_owner"', + ) + response = self.client.get( + reverse("admin:auth_user_add") + "?%s=1" % IS_POPUP_VAR + ) self.assertNotContains(response, 'name="_continue"') self.assertNotContains(response, 'name="_addanother"') data = { - 'username': 'newuser', - 'password1': 'newpassword', - 'password2': 'newpassword', - IS_POPUP_VAR: '1', - '_save': '1', + "username": "newuser", + "password1": "newpassword", + "password2": "newpassword", + IS_POPUP_VAR: "1", + "_save": "1", } - response = self.client.post(reverse('admin:auth_user_add') + '?%s=1' % IS_POPUP_VAR, data, follow=True) - self.assertContains(response, '"obj": "newuser"') + response = self.client.post( + reverse("admin:auth_user_add") + "?%s=1" % IS_POPUP_VAR, data, follow=True + ) + self.assertContains(response, ""obj": "newuser"") def test_user_fk_change_popup(self): """User change through a FK popup should return the appropriate JavaScript response.""" - response = self.client.get(reverse('admin:admin_views_album_add')) - self.assertContains(response, reverse('admin:auth_user_change', args=('__fk__',))) - self.assertContains(response, 'class="related-widget-wrapper-link change-related" id="change_id_owner"') - user = User.objects.get(username='changeuser') - url = reverse('admin:auth_user_change', args=(user.pk,)) + '?%s=1' % IS_POPUP_VAR + response = self.client.get(reverse("admin:admin_views_album_add")) + self.assertContains( + response, reverse("admin:auth_user_change", args=("__fk__",)) + ) + self.assertContains( + response, + 'class="related-widget-wrapper-link change-related" id="change_id_owner"', + ) + user = User.objects.get(username="changeuser") + url = ( + reverse("admin:auth_user_change", args=(user.pk,)) + "?%s=1" % IS_POPUP_VAR + ) response = self.client.get(url) self.assertNotContains(response, 'name="_continue"') self.assertNotContains(response, 'name="_addanother"') data = { - 'username': 'newuser', - 'password1': 'newpassword', - 'password2': 'newpassword', - 'last_login_0': '2007-05-30', - 'last_login_1': '13:20:10', - 'date_joined_0': '2007-05-30', - 'date_joined_1': '13:20:10', - IS_POPUP_VAR: '1', - '_save': '1', + "username": "newuser", + "password1": "newpassword", + "password2": "newpassword", + "last_login_0": "2007-05-30", + "last_login_1": "13:20:10", + "date_joined_0": "2007-05-30", + "date_joined_1": "13:20:10", + IS_POPUP_VAR: "1", + "_save": "1", } response = self.client.post(url, data, follow=True) - self.assertContains(response, '"obj": "newuser"') - self.assertContains(response, '"action": "change"') + self.assertContains(response, ""obj": "newuser"") + self.assertContains(response, ""action": "change"") def test_user_fk_delete_popup(self): """User deletion through a FK popup should return the appropriate JavaScript response.""" - response = self.client.get(reverse('admin:admin_views_album_add')) - self.assertContains(response, reverse('admin:auth_user_delete', args=('__fk__',))) - self.assertContains(response, 'class="related-widget-wrapper-link change-related" id="change_id_owner"') - user = User.objects.get(username='changeuser') - url = reverse('admin:auth_user_delete', args=(user.pk,)) + '?%s=1' % IS_POPUP_VAR + response = self.client.get(reverse("admin:admin_views_album_add")) + self.assertContains( + response, reverse("admin:auth_user_delete", args=("__fk__",)) + ) + self.assertContains( + response, + 'class="related-widget-wrapper-link change-related" id="change_id_owner"', + ) + user = User.objects.get(username="changeuser") + url = ( + reverse("admin:auth_user_delete", args=(user.pk,)) + "?%s=1" % IS_POPUP_VAR + ) response = self.client.get(url) self.assertEqual(response.status_code, 200) data = { - 'post': 'yes', - IS_POPUP_VAR: '1', + "post": "yes", + IS_POPUP_VAR: "1", } response = self.client.post(url, data, follow=True) - self.assertContains(response, '"action": "delete"') + self.assertContains(response, ""action": "delete"") def test_save_add_another_button(self): user_count = User.objects.count() - response = self.client.post(reverse('admin:auth_user_add'), { - 'username': 'newuser', - 'password1': 'newpassword', - 'password2': 'newpassword', - '_addanother': '1', - }) - new_user = User.objects.order_by('-id')[0] - self.assertRedirects(response, reverse('admin:auth_user_add')) + response = self.client.post( + reverse("admin:auth_user_add"), + { + "username": "newuser", + "password1": "newpassword", + "password2": "newpassword", + "_addanother": "1", + }, + ) + new_user = User.objects.order_by("-id")[0] + self.assertRedirects(response, reverse("admin:auth_user_add")) self.assertEqual(User.objects.count(), user_count + 1) self.assertTrue(new_user.has_usable_password()) @@ -5670,17 +6750,19 @@ class UserAdminTest(TestCase): ContentType.objects.clear_cache() with self.assertNumQueries(10): - response = self.client.get(reverse('admin:auth_user_change', args=(u.pk,))) + response = self.client.get(reverse("admin:auth_user_change", args=(u.pk,))) self.assertEqual(response.status_code, 200) def test_form_url_present_in_context(self): u = User.objects.all()[0] - response = self.client.get(reverse('admin3:auth_user_password_change', args=(u.pk,))) + response = self.client.get( + reverse("admin3:auth_user_password_change", args=(u.pk,)) + ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['form_url'], 'pony') + self.assertEqual(response.context["form_url"], "pony") -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class GroupAdminTest(TestCase): """ Tests group CRUD functionality. @@ -5688,19 +6770,24 @@ class GroupAdminTest(TestCase): @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_save_button(self): group_count = Group.objects.count() - response = self.client.post(reverse('admin:auth_group_add'), { - 'name': 'newgroup', - }) + response = self.client.post( + reverse("admin:auth_group_add"), + { + "name": "newgroup", + }, + ) - Group.objects.order_by('-id')[0] - self.assertRedirects(response, reverse('admin:auth_group_changelist')) + Group.objects.order_by("-id")[0] + self.assertRedirects(response, reverse("admin:auth_group_changelist")) self.assertEqual(Group.objects.count(), group_count + 1) def test_group_permission_performance(self): @@ -5710,27 +6797,36 @@ class GroupAdminTest(TestCase): ContentType.objects.clear_cache() with self.assertNumQueries(8): - response = self.client.get(reverse('admin:auth_group_change', args=(g.pk,))) + response = self.client.get(reverse("admin:auth_group_change", args=(g.pk,))) self.assertEqual(response.status_code, 200) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class CSSTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.s1 = Section.objects.create(name='Test section') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.s1 = Section.objects.create(name="Test section") cls.a1 = Article.objects.create( - content='<p>Middle content</p>', date=datetime.datetime(2008, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Middle content</p>", + date=datetime.datetime(2008, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a2 = Article.objects.create( - content='<p>Oldest content</p>', date=datetime.datetime(2000, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Oldest content</p>", + date=datetime.datetime(2000, 3, 18, 11, 54, 58), + section=cls.s1, ) cls.a3 = Article.objects.create( - content='<p>Newest content</p>', date=datetime.datetime(2009, 3, 18, 11, 54, 58), section=cls.s1 + content="<p>Newest content</p>", + date=datetime.datetime(2009, 3, 18, 11, 54, 58), + section=cls.s1, + ) + cls.p1 = PrePopulatedPost.objects.create( + title="A Long Title", published=True, slug="a-long-title" ) - cls.p1 = PrePopulatedPost.objects.create(title='A Long Title', published=True, slug='a-long-title') def setUp(self): self.client.force_login(self.superuser) @@ -5739,7 +6835,7 @@ class CSSTest(TestCase): """ Fields have a CSS class name with a 'field-' prefix. """ - response = self.client.get(reverse('admin:admin_views_post_add')) + response = self.client.get(reverse("admin:admin_views_post_add")) # The main form self.assertContains(response, 'class="form-row field-title"') @@ -5760,13 +6856,13 @@ class CSSTest(TestCase): pages (#17050). """ # General index page - response = self.client.get(reverse('admin:index')) + response = self.client.get(reverse("admin:index")) self.assertContains(response, '<div class="app-admin_views module') self.assertContains(response, '<tr class="model-actor">') self.assertContains(response, '<tr class="model-album">') # App index page - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertContains(response, '<div class="app-admin_views module') self.assertContains(response, '<tr class="model-actor">') self.assertContains(response, '<tr class="model-album">') @@ -5775,14 +6871,14 @@ class CSSTest(TestCase): """ Ensure app and model tag are correctly read by change_form template """ - response = self.client.get(reverse('admin:admin_views_section_add')) + response = self.client.get(reverse("admin:admin_views_section_add")) self.assertContains(response, '<body class=" app-admin_views model-section ') def test_app_model_in_list_body_class(self): """ Ensure app and model tag are correctly read by change_list template """ - response = self.client.get(reverse('admin:admin_views_section_changelist')) + response = self.client.get(reverse("admin:admin_views_section_changelist")) self.assertContains(response, '<body class=" app-admin_views model-section ') def test_app_model_in_delete_confirmation_body_class(self): @@ -5790,14 +6886,16 @@ class CSSTest(TestCase): Ensure app and model tag are correctly read by delete_confirmation template """ - response = self.client.get(reverse('admin:admin_views_section_delete', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_section_delete", args=(self.s1.pk,)) + ) self.assertContains(response, '<body class=" app-admin_views model-section ') def test_app_model_in_app_index_body_class(self): """ Ensure app and model tag are correctly read by app_index template """ - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertContains(response, '<body class=" dashboard app-admin_views') def test_app_model_in_delete_selected_confirmation_body_class(self): @@ -5807,10 +6905,12 @@ class CSSTest(TestCase): """ action_data = { ACTION_CHECKBOX_NAME: [self.s1.pk], - 'action': 'delete_selected', - 'index': 0, + "action": "delete_selected", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_section_changelist'), action_data) + response = self.client.post( + reverse("admin:admin_views_section_changelist"), action_data + ) self.assertContains(response, '<body class=" app-admin_views model-section ') def test_changelist_field_classes(self): @@ -5819,7 +6919,7 @@ class CSSTest(TestCase): Refs #11195. """ Podcast.objects.create(name="Django Dose", release_date=datetime.date.today()) - response = self.client.get(reverse('admin:admin_views_podcast_changelist')) + response = self.client.get(reverse("admin:admin_views_podcast_changelist")) self.assertContains(response, '<th class="field-name">') self.assertContains(response, '<td class="field-release_date nowrap">') self.assertContains(response, '<td class="action-checkbox">') @@ -5832,86 +6932,113 @@ except ImportError: @unittest.skipUnless(docutils, "no docutils installed.") -@override_settings(ROOT_URLCONF='admin_views.urls') -@modify_settings(INSTALLED_APPS={'append': ['django.contrib.admindocs', 'django.contrib.flatpages']}) +@override_settings(ROOT_URLCONF="admin_views.urls") +@modify_settings( + INSTALLED_APPS={"append": ["django.contrib.admindocs", "django.contrib.flatpages"]} +) class AdminDocsTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_tags(self): - response = self.client.get(reverse('django-admindocs-tags')) + response = self.client.get(reverse("django-admindocs-tags")) # The builtin tag group exists self.assertContains(response, "<h2>Built-in tags</h2>", count=2, html=True) # A builtin tag exists in both the index and detail - self.assertContains(response, '<h3 id="built_in-autoescape">autoescape</h3>', html=True) - self.assertContains(response, '<li><a href="#built_in-autoescape">autoescape</a></li>', html=True) + self.assertContains( + response, '<h3 id="built_in-autoescape">autoescape</h3>', html=True + ) + self.assertContains( + response, + '<li><a href="#built_in-autoescape">autoescape</a></li>', + html=True, + ) # An app tag exists in both the index and detail - self.assertContains(response, '<h3 id="flatpages-get_flatpages">get_flatpages</h3>', html=True) - self.assertContains(response, '<li><a href="#flatpages-get_flatpages">get_flatpages</a></li>', html=True) + self.assertContains( + response, '<h3 id="flatpages-get_flatpages">get_flatpages</h3>', html=True + ) + self.assertContains( + response, + '<li><a href="#flatpages-get_flatpages">get_flatpages</a></li>', + html=True, + ) # The admin list tag group exists self.assertContains(response, "<h2>admin_list</h2>", count=2, html=True) # An admin list tag exists in both the index and detail - self.assertContains(response, '<h3 id="admin_list-admin_actions">admin_actions</h3>', html=True) - self.assertContains(response, '<li><a href="#admin_list-admin_actions">admin_actions</a></li>', html=True) + self.assertContains( + response, '<h3 id="admin_list-admin_actions">admin_actions</h3>', html=True + ) + self.assertContains( + response, + '<li><a href="#admin_list-admin_actions">admin_actions</a></li>', + html=True, + ) def test_filters(self): - response = self.client.get(reverse('django-admindocs-filters')) + response = self.client.get(reverse("django-admindocs-filters")) # The builtin filter group exists self.assertContains(response, "<h2>Built-in filters</h2>", count=2, html=True) # A builtin filter exists in both the index and detail self.assertContains(response, '<h3 id="built_in-add">add</h3>', html=True) - self.assertContains(response, '<li><a href="#built_in-add">add</a></li>', html=True) + self.assertContains( + response, '<li><a href="#built_in-add">add</a></li>', html=True + ) @override_settings( - ROOT_URLCONF='admin_views.urls', - TEMPLATES=[{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }], + ROOT_URLCONF="admin_views.urls", + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ], ) class ValidXHTMLTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_lang_name_present(self): with translation.override(None): - response = self.client.get(reverse('admin:app_list', args=('admin_views',))) + response = self.client.get(reverse("admin:app_list", args=("admin_views",))) self.assertNotContains(response, ' lang=""') self.assertNotContains(response, ' xml:lang=""') -@override_settings(ROOT_URLCONF='admin_views.urls', USE_THOUSAND_SEPARATOR=True) +@override_settings(ROOT_URLCONF="admin_views.urls", USE_THOUSAND_SEPARATOR=True) class DateHierarchyTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -5927,24 +7054,27 @@ class DateHierarchyTests(TestCase): def assert_contains_month_link(self, response, date): self.assertContains( - response, '?release_date__month=%d&release_date__year=%d"' % ( - date.month, date.year)) + response, + '?release_date__month=%d&release_date__year=%d"' + % (date.month, date.year), + ) def assert_contains_day_link(self, response, date): self.assertContains( - response, '?release_date__day=%d&' - 'release_date__month=%d&release_date__year=%d"' % ( - date.day, date.month, date.year)) + response, + "?release_date__day=%d&" + 'release_date__month=%d&release_date__year=%d"' + % (date.day, date.month, date.year), + ) def test_empty(self): """ No date hierarchy links display with empty changelist. """ - response = self.client.get( - reverse('admin:admin_views_podcast_changelist')) - self.assertNotContains(response, 'release_date__year=') - self.assertNotContains(response, 'release_date__month=') - self.assertNotContains(response, 'release_date__day=') + response = self.client.get(reverse("admin:admin_views_podcast_changelist")) + self.assertNotContains(response, "release_date__year=") + self.assertNotContains(response, "release_date__month=") + self.assertNotContains(response, "release_date__day=") def test_single(self): """ @@ -5952,7 +7082,7 @@ class DateHierarchyTests(TestCase): """ DATE = datetime.date(2000, 6, 30) Podcast.objects.create(release_date=DATE) - url = reverse('admin:admin_views_podcast_changelist') + url = reverse("admin:admin_views_podcast_changelist") response = self.client.get(url) self.assert_contains_day_link(response, DATE) self.assert_non_localized_year(response, 2000) @@ -5961,12 +7091,14 @@ class DateHierarchyTests(TestCase): """ day-level links appear for changelist within single month. """ - DATES = (datetime.date(2000, 6, 30), - datetime.date(2000, 6, 15), - datetime.date(2000, 6, 3)) + DATES = ( + datetime.date(2000, 6, 30), + datetime.date(2000, 6, 15), + datetime.date(2000, 6, 3), + ) for date in DATES: Podcast.objects.create(release_date=date) - url = reverse('admin:admin_views_podcast_changelist') + url = reverse("admin:admin_views_podcast_changelist") response = self.client.get(url) for date in DATES: self.assert_contains_day_link(response, date) @@ -5976,15 +7108,17 @@ class DateHierarchyTests(TestCase): """ month-level links appear for changelist within single year. """ - DATES = (datetime.date(2000, 1, 30), - datetime.date(2000, 3, 15), - datetime.date(2000, 5, 3)) + DATES = ( + datetime.date(2000, 1, 30), + datetime.date(2000, 3, 15), + datetime.date(2000, 5, 3), + ) for date in DATES: Podcast.objects.create(release_date=date) - url = reverse('admin:admin_views_podcast_changelist') + url = reverse("admin:admin_views_podcast_changelist") response = self.client.get(url) # no day-level links - self.assertNotContains(response, 'release_date__day=') + self.assertNotContains(response, "release_date__day=") for date in DATES: self.assert_contains_month_link(response, date) self.assert_non_localized_year(response, 2000) @@ -5993,33 +7127,37 @@ class DateHierarchyTests(TestCase): """ year-level links appear for year-spanning changelist. """ - DATES = (datetime.date(2001, 1, 30), - datetime.date(2003, 3, 15), - datetime.date(2005, 5, 3)) + DATES = ( + datetime.date(2001, 1, 30), + datetime.date(2003, 3, 15), + datetime.date(2005, 5, 3), + ) for date in DATES: Podcast.objects.create(release_date=date) - response = self.client.get( - reverse('admin:admin_views_podcast_changelist')) + response = self.client.get(reverse("admin:admin_views_podcast_changelist")) # no day/month-level links - self.assertNotContains(response, 'release_date__day=') - self.assertNotContains(response, 'release_date__month=') + self.assertNotContains(response, "release_date__day=") + self.assertNotContains(response, "release_date__month=") for date in DATES: self.assert_contains_year_link(response, date) # and make sure GET parameters still behave correctly for date in DATES: - url = '%s?release_date__year=%d' % ( - reverse('admin:admin_views_podcast_changelist'), - date.year) + url = "%s?release_date__year=%d" % ( + reverse("admin:admin_views_podcast_changelist"), + date.year, + ) response = self.client.get(url) self.assert_contains_month_link(response, date) self.assert_non_localized_year(response, 2000) self.assert_non_localized_year(response, 2003) self.assert_non_localized_year(response, 2005) - url = '%s?release_date__year=%d&release_date__month=%d' % ( - reverse('admin:admin_views_podcast_changelist'), - date.year, date.month) + url = "%s?release_date__year=%d&release_date__month=%d" % ( + reverse("admin:admin_views_podcast_changelist"), + date.year, + date.month, + ) response = self.client.get(url) self.assert_contains_day_link(response, date) self.assert_non_localized_year(response, 2000) @@ -6038,7 +7176,7 @@ class DateHierarchyTests(TestCase): for i in range(answer_count): question.answer_set.create() - response = self.client.get(reverse('admin:admin_views_answer_changelist')) + response = self.client.get(reverse("admin:admin_views_answer_changelist")) for date, answer_count in questions_data: link = '?question__posted__year=%d"' % date.year if answer_count > 0: @@ -6047,7 +7185,7 @@ class DateHierarchyTests(TestCase): self.assertNotContains(response, link) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminCustomSaveRelatedTests(TestCase): """ One can easily customize the way related objects are saved. @@ -6056,104 +7194,120 @@ class AdminCustomSaveRelatedTests(TestCase): @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_should_be_able_to_edit_related_objects_on_add_view(self): post = { - 'child_set-TOTAL_FORMS': '3', - 'child_set-INITIAL_FORMS': '0', - 'name': 'Josh Stone', - 'child_set-0-name': 'Paul', - 'child_set-1-name': 'Catherine', + "child_set-TOTAL_FORMS": "3", + "child_set-INITIAL_FORMS": "0", + "name": "Josh Stone", + "child_set-0-name": "Paul", + "child_set-1-name": "Catherine", } - self.client.post(reverse('admin:admin_views_parent_add'), post) + self.client.post(reverse("admin:admin_views_parent_add"), post) self.assertEqual(1, Parent.objects.count()) self.assertEqual(2, Child.objects.count()) - children_names = list(Child.objects.order_by('name').values_list('name', flat=True)) + children_names = list( + Child.objects.order_by("name").values_list("name", flat=True) + ) - self.assertEqual('Josh Stone', Parent.objects.latest('id').name) - self.assertEqual(['Catherine Stone', 'Paul Stone'], children_names) + self.assertEqual("Josh Stone", Parent.objects.latest("id").name) + self.assertEqual(["Catherine Stone", "Paul Stone"], children_names) def test_should_be_able_to_edit_related_objects_on_change_view(self): - parent = Parent.objects.create(name='Josh Stone') - paul = Child.objects.create(parent=parent, name='Paul') - catherine = Child.objects.create(parent=parent, name='Catherine') + parent = Parent.objects.create(name="Josh Stone") + paul = Child.objects.create(parent=parent, name="Paul") + catherine = Child.objects.create(parent=parent, name="Catherine") post = { - 'child_set-TOTAL_FORMS': '5', - 'child_set-INITIAL_FORMS': '2', - 'name': 'Josh Stone', - 'child_set-0-name': 'Paul', - 'child_set-0-id': paul.id, - 'child_set-1-name': 'Catherine', - 'child_set-1-id': catherine.id, + "child_set-TOTAL_FORMS": "5", + "child_set-INITIAL_FORMS": "2", + "name": "Josh Stone", + "child_set-0-name": "Paul", + "child_set-0-id": paul.id, + "child_set-1-name": "Catherine", + "child_set-1-id": catherine.id, } - self.client.post(reverse('admin:admin_views_parent_change', args=(parent.id,)), post) + self.client.post( + reverse("admin:admin_views_parent_change", args=(parent.id,)), post + ) - children_names = list(Child.objects.order_by('name').values_list('name', flat=True)) + children_names = list( + Child.objects.order_by("name").values_list("name", flat=True) + ) - self.assertEqual('Josh Stone', Parent.objects.latest('id').name) - self.assertEqual(['Catherine Stone', 'Paul Stone'], children_names) + self.assertEqual("Josh Stone", Parent.objects.latest("id").name) + self.assertEqual(["Catherine Stone", "Paul Stone"], children_names) def test_should_be_able_to_edit_related_objects_on_changelist_view(self): - parent = Parent.objects.create(name='Josh Rock') - Child.objects.create(parent=parent, name='Paul') - Child.objects.create(parent=parent, name='Catherine') + parent = Parent.objects.create(name="Josh Rock") + Child.objects.create(parent=parent, name="Paul") + Child.objects.create(parent=parent, name="Catherine") post = { - 'form-TOTAL_FORMS': '1', - 'form-INITIAL_FORMS': '1', - 'form-MAX_NUM_FORMS': '0', - 'form-0-id': parent.id, - 'form-0-name': 'Josh Stone', - '_save': 'Save' + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "1", + "form-MAX_NUM_FORMS": "0", + "form-0-id": parent.id, + "form-0-name": "Josh Stone", + "_save": "Save", } - self.client.post(reverse('admin:admin_views_parent_changelist'), post) - children_names = list(Child.objects.order_by('name').values_list('name', flat=True)) + self.client.post(reverse("admin:admin_views_parent_changelist"), post) + children_names = list( + Child.objects.order_by("name").values_list("name", flat=True) + ) - self.assertEqual('Josh Stone', Parent.objects.latest('id').name) - self.assertEqual(['Catherine Stone', 'Paul Stone'], children_names) + self.assertEqual("Josh Stone", Parent.objects.latest("id").name) + self.assertEqual(["Catherine Stone", "Paul Stone"], children_names) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewLogoutTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def test_logout(self): self.client.force_login(self.superuser) - response = self.client.get(reverse('admin:logout')) + response = self.client.get(reverse("admin:logout")) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'registration/logged_out.html') - self.assertEqual(response.request['PATH_INFO'], reverse('admin:logout')) - self.assertFalse(response.context['has_permission']) - self.assertNotContains(response, 'user-tools') # user-tools div shouldn't visible. + self.assertTemplateUsed(response, "registration/logged_out.html") + self.assertEqual(response.request["PATH_INFO"], reverse("admin:logout")) + self.assertFalse(response.context["has_permission"]) + self.assertNotContains( + response, "user-tools" + ) # user-tools div shouldn't visible. def test_client_logout_url_can_be_used_to_login(self): - response = self.client.get(reverse('admin:logout')) - self.assertEqual(response.status_code, 302) # we should be redirected to the login page. + response = self.client.get(reverse("admin:logout")) + self.assertEqual( + response.status_code, 302 + ) # we should be redirected to the login page. # follow the redirect and test results. - response = self.client.get(reverse('admin:logout'), follow=True) + response = self.client.get(reverse("admin:logout"), follow=True) self.assertContains( response, - '<input type="hidden" name="next" value="%s">' % reverse('admin:index'), + '<input type="hidden" name="next" value="%s">' % reverse("admin:index"), ) - self.assertTemplateUsed(response, 'admin/login.html') - self.assertEqual(response.request['PATH_INFO'], reverse('admin:login')) + self.assertTemplateUsed(response, "admin/login.html") + self.assertEqual(response.request["PATH_INFO"], reverse("admin:login")) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminUserMessageTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) @@ -6165,59 +7319,69 @@ class AdminUserMessageTest(TestCase): """ action_data = { ACTION_CHECKBOX_NAME: [1], - 'action': 'message_%s' % level, - 'index': 0, + "action": "message_%s" % level, + "index": 0, } - response = self.client.post(reverse('admin:admin_views_usermessenger_changelist'), - action_data, follow=True) - self.assertContains(response, - '<li class="%s">Test %s</li>' % (level, level), - html=True) + response = self.client.post( + reverse("admin:admin_views_usermessenger_changelist"), + action_data, + follow=True, + ) + self.assertContains( + response, '<li class="%s">Test %s</li>' % (level, level), html=True + ) @override_settings(MESSAGE_LEVEL=10) # Set to DEBUG for this request def test_message_debug(self): - self.send_message('debug') + self.send_message("debug") def test_message_info(self): - self.send_message('info') + self.send_message("info") def test_message_success(self): - self.send_message('success') + self.send_message("success") def test_message_warning(self): - self.send_message('warning') + self.send_message("warning") def test_message_error(self): - self.send_message('error') + self.send_message("error") def test_message_extra_tags(self): action_data = { ACTION_CHECKBOX_NAME: [1], - 'action': 'message_extra_tags', - 'index': 0, + "action": "message_extra_tags", + "index": 0, } - response = self.client.post(reverse('admin:admin_views_usermessenger_changelist'), - action_data, follow=True) - self.assertContains(response, - '<li class="extra_tag info">Test tags</li>', - html=True) + response = self.client.post( + reverse("admin:admin_views_usermessenger_changelist"), + action_data, + follow=True, + ) + self.assertContains( + response, '<li class="extra_tag info">Test tags</li>', html=True + ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminKeepChangeListFiltersTests(TestCase): admin_site = site @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - cls.joepublicuser = User.objects.create_user(username='joepublic', password='secret') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + cls.joepublicuser = User.objects.create_user( + username="joepublic", password="secret" + ) def setUp(self): self.client.force_login(self.superuser) - def assertURLEqual(self, url1, url2, msg_prefix=''): + def assertURLEqual(self, url1, url2, msg_prefix=""): """ Assert that two URLs are equal despite the ordering of their querystring. Refs #22360. @@ -6231,101 +7395,117 @@ class AdminKeepChangeListFiltersTests(TestCase): parsed_qs2 = dict(parse_qsl(parsed_url2.query)) for parsed_qs in [parsed_qs1, parsed_qs2]: - if '_changelist_filters' in parsed_qs: - changelist_filters = parsed_qs['_changelist_filters'] + if "_changelist_filters" in parsed_qs: + changelist_filters = parsed_qs["_changelist_filters"] parsed_filters = dict(parse_qsl(changelist_filters)) - parsed_qs['_changelist_filters'] = parsed_filters + parsed_qs["_changelist_filters"] = parsed_filters self.assertEqual(path1, path2) self.assertEqual(parsed_qs1, parsed_qs2) def test_assert_url_equal(self): # Test equality. - change_user_url = reverse('admin:auth_user_change', args=(self.joepublicuser.pk,)) + change_user_url = reverse( + "admin:auth_user_change", args=(self.joepublicuser.pk,) + ) self.assertURLEqual( - 'http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'.format( + "http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0".format( change_user_url ), - 'http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'.format( + "http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0".format( change_user_url - ) + ), ) # Test inequality. with self.assertRaises(AssertionError): self.assertURLEqual( - 'http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'.format( + "http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0".format( change_user_url ), - 'http://testserver{}?_changelist_filters=is_staff__exact%3D1%26is_superuser__exact%3D1'.format( + "http://testserver{}?_changelist_filters=is_staff__exact%3D1%26is_superuser__exact%3D1".format( change_user_url - ) + ), ) # Ignore scheme and host. self.assertURLEqual( - 'http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'.format( + "http://testserver{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0".format( + change_user_url + ), + "{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0".format( change_user_url ), - '{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'.format(change_user_url) ) # Ignore ordering of querystring. self.assertURLEqual( - '{}?is_staff__exact=0&is_superuser__exact=0'.format(reverse('admin:auth_user_changelist')), - '{}?is_superuser__exact=0&is_staff__exact=0'.format(reverse('admin:auth_user_changelist')) + "{}?is_staff__exact=0&is_superuser__exact=0".format( + reverse("admin:auth_user_changelist") + ), + "{}?is_superuser__exact=0&is_staff__exact=0".format( + reverse("admin:auth_user_changelist") + ), ) # Ignore ordering of _changelist_filters. self.assertURLEqual( - '{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'.format(change_user_url), - '{}?_changelist_filters=is_superuser__exact%3D0%26is_staff__exact%3D0'.format(change_user_url) + "{}?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0".format( + change_user_url + ), + "{}?_changelist_filters=is_superuser__exact%3D0%26is_staff__exact%3D0".format( + change_user_url + ), ) def get_changelist_filters(self): return { - 'is_superuser__exact': 0, - 'is_staff__exact': 0, + "is_superuser__exact": 0, + "is_staff__exact": 0, } def get_changelist_filters_querystring(self): return urlencode(self.get_changelist_filters()) def get_preserved_filters_querystring(self): - return urlencode({ - '_changelist_filters': self.get_changelist_filters_querystring() - }) + return urlencode( + {"_changelist_filters": self.get_changelist_filters_querystring()} + ) def get_sample_user_id(self): return self.joepublicuser.pk def get_changelist_url(self): - return '%s?%s' % ( - reverse('admin:auth_user_changelist', - current_app=self.admin_site.name), + return "%s?%s" % ( + reverse("admin:auth_user_changelist", current_app=self.admin_site.name), self.get_changelist_filters_querystring(), ) def get_add_url(self, add_preserved_filters=True): - url = reverse('admin:auth_user_add', current_app=self.admin_site.name) + url = reverse("admin:auth_user_add", current_app=self.admin_site.name) if add_preserved_filters: - url = '%s?%s' % (url, self.get_preserved_filters_querystring()) + url = "%s?%s" % (url, self.get_preserved_filters_querystring()) return url def get_change_url(self, user_id=None, add_preserved_filters=True): if user_id is None: user_id = self.get_sample_user_id() - url = reverse('admin:auth_user_change', args=(user_id,), current_app=self.admin_site.name) + url = reverse( + "admin:auth_user_change", args=(user_id,), current_app=self.admin_site.name + ) if add_preserved_filters: - url = '%s?%s' % (url, self.get_preserved_filters_querystring()) + url = "%s?%s" % (url, self.get_preserved_filters_querystring()) return url def get_history_url(self, user_id=None): if user_id is None: user_id = self.get_sample_user_id() return "%s?%s" % ( - reverse('admin:auth_user_history', args=(user_id,), - current_app=self.admin_site.name), + reverse( + "admin:auth_user_history", + args=(user_id,), + current_app=self.admin_site.name, + ), self.get_preserved_filters_querystring(), ) @@ -6333,8 +7513,11 @@ class AdminKeepChangeListFiltersTests(TestCase): if user_id is None: user_id = self.get_sample_user_id() return "%s?%s" % ( - reverse('admin:auth_user_delete', args=(user_id,), - current_app=self.admin_site.name), + reverse( + "admin:auth_user_delete", + args=(user_id,), + current_app=self.admin_site.name, + ), self.get_preserved_filters_querystring(), ) @@ -6345,7 +7528,7 @@ class AdminKeepChangeListFiltersTests(TestCase): # Check the `change_view` link has the correct querystring. detail_link = re.search( '<a href="(.*?)">{}</a>'.format(self.joepublicuser.username), - response.content.decode() + response.content.decode(), ) self.assertURLEqual(detail_link[1], self.get_change_url()) @@ -6357,49 +7540,49 @@ class AdminKeepChangeListFiltersTests(TestCase): # Check the form action. form_action = re.search( '<form action="(.*?)" method="post" id="user_form" novalidate>', - response.content.decode() + response.content.decode(), + ) + self.assertURLEqual( + form_action[1], "?%s" % self.get_preserved_filters_querystring() ) - self.assertURLEqual(form_action[1], '?%s' % self.get_preserved_filters_querystring()) # Check the history link. history_link = re.search( - '<a href="(.*?)" class="historylink">History</a>', - response.content.decode() + '<a href="(.*?)" class="historylink">History</a>', response.content.decode() ) self.assertURLEqual(history_link[1], self.get_history_url()) # Check the delete link. delete_link = re.search( - '<a href="(.*?)" class="deletelink">Delete</a>', - response.content.decode() + '<a href="(.*?)" class="deletelink">Delete</a>', response.content.decode() ) self.assertURLEqual(delete_link[1], self.get_delete_url()) # Test redirect on "Save". post_data = { - 'username': 'joepublic', - 'last_login_0': '2007-05-30', - 'last_login_1': '13:20:10', - 'date_joined_0': '2007-05-30', - 'date_joined_1': '13:20:10', + "username": "joepublic", + "last_login_0": "2007-05-30", + "last_login_1": "13:20:10", + "date_joined_0": "2007-05-30", + "date_joined_1": "13:20:10", } - post_data['_save'] = 1 + post_data["_save"] = 1 response = self.client.post(self.get_change_url(), data=post_data) self.assertRedirects(response, self.get_changelist_url()) - post_data.pop('_save') + post_data.pop("_save") # Test redirect on "Save and continue". - post_data['_continue'] = 1 + post_data["_continue"] = 1 response = self.client.post(self.get_change_url(), data=post_data) self.assertRedirects(response, self.get_change_url()) - post_data.pop('_continue') + post_data.pop("_continue") # Test redirect on "Save and add new". - post_data['_addanother'] = 1 + post_data["_addanother"] = 1 response = self.client.post(self.get_change_url(), data=post_data) self.assertRedirects(response, self.get_add_url()) - post_data.pop('_addanother') + post_data.pop("_addanother") def test_change_view_without_preserved_filters(self): response = self.client.get(self.get_change_url(add_preserved_filters=False)) @@ -6414,35 +7597,41 @@ class AdminKeepChangeListFiltersTests(TestCase): # Check the form action. form_action = re.search( '<form action="(.*?)" method="post" id="user_form" novalidate>', - response.content.decode() + response.content.decode(), + ) + self.assertURLEqual( + form_action[1], "?%s" % self.get_preserved_filters_querystring() ) - self.assertURLEqual(form_action[1], '?%s' % self.get_preserved_filters_querystring()) post_data = { - 'username': 'dummy', - 'password1': 'test', - 'password2': 'test', + "username": "dummy", + "password1": "test", + "password2": "test", } # Test redirect on "Save". - post_data['_save'] = 1 + post_data["_save"] = 1 response = self.client.post(self.get_add_url(), data=post_data) - self.assertRedirects(response, self.get_change_url(User.objects.get(username='dummy').pk)) - post_data.pop('_save') + self.assertRedirects( + response, self.get_change_url(User.objects.get(username="dummy").pk) + ) + post_data.pop("_save") # Test redirect on "Save and continue". - post_data['username'] = 'dummy2' - post_data['_continue'] = 1 + post_data["username"] = "dummy2" + post_data["_continue"] = 1 response = self.client.post(self.get_add_url(), data=post_data) - self.assertRedirects(response, self.get_change_url(User.objects.get(username='dummy2').pk)) - post_data.pop('_continue') + self.assertRedirects( + response, self.get_change_url(User.objects.get(username="dummy2").pk) + ) + post_data.pop("_continue") # Test redirect on "Save and add new". - post_data['username'] = 'dummy3' - post_data['_addanother'] = 1 + post_data["username"] = "dummy3" + post_data["_addanother"] = 1 response = self.client.post(self.get_add_url(), data=post_data) self.assertRedirects(response, self.get_add_url()) - post_data.pop('_addanother') + post_data.pop("_addanother") def test_add_view_without_preserved_filters(self): response = self.client.get(self.get_add_url(add_preserved_filters=False)) @@ -6451,18 +7640,20 @@ class AdminKeepChangeListFiltersTests(TestCase): def test_delete_view(self): # Test redirect on "Delete". - response = self.client.post(self.get_delete_url(), {'post': 'yes'}) + response = self.client.post(self.get_delete_url(), {"post": "yes"}) self.assertRedirects(response, self.get_changelist_url()) def test_url_prefix(self): context = { - 'preserved_filters': self.get_preserved_filters_querystring(), - 'opts': User._meta, + "preserved_filters": self.get_preserved_filters_querystring(), + "opts": User._meta, } - prefixes = ('', '/prefix/', '/後台/') + prefixes = ("", "/prefix/", "/後台/") for prefix in prefixes: with self.subTest(prefix=prefix), override_script_prefix(prefix): - url = reverse('admin:auth_user_changelist', current_app=self.admin_site.name) + url = reverse( + "admin:auth_user_changelist", current_app=self.admin_site.name + ) self.assertURLEqual( self.get_changelist_url(), add_preserved_filters(context, url), @@ -6473,40 +7664,44 @@ class NamespacedAdminKeepChangeListFiltersTests(AdminKeepChangeListFiltersTests) admin_site = site2 -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class TestLabelVisibility(TestCase): - """ #11277 -Labels of hidden fields in admin were not hidden. """ + """#11277 -Labels of hidden fields in admin were not hidden.""" @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_all_fields_visible(self): - response = self.client.get(reverse('admin:admin_views_emptymodelvisible_add')) + response = self.client.get(reverse("admin:admin_views_emptymodelvisible_add")) self.assert_fieldline_visible(response) - self.assert_field_visible(response, 'first') - self.assert_field_visible(response, 'second') + self.assert_field_visible(response, "first") + self.assert_field_visible(response, "second") def test_all_fields_hidden(self): - response = self.client.get(reverse('admin:admin_views_emptymodelhidden_add')) + response = self.client.get(reverse("admin:admin_views_emptymodelhidden_add")) self.assert_fieldline_hidden(response) - self.assert_field_hidden(response, 'first') - self.assert_field_hidden(response, 'second') + self.assert_field_hidden(response, "first") + self.assert_field_hidden(response, "second") def test_mixin(self): - response = self.client.get(reverse('admin:admin_views_emptymodelmixin_add')) + response = self.client.get(reverse("admin:admin_views_emptymodelmixin_add")) self.assert_fieldline_visible(response) - self.assert_field_hidden(response, 'first') - self.assert_field_visible(response, 'second') + self.assert_field_hidden(response, "first") + self.assert_field_visible(response, "second") def assert_field_visible(self, response, field_name): self.assertContains(response, '<div class="fieldBox field-%s">' % field_name) def assert_field_hidden(self, response, field_name): - self.assertContains(response, '<div class="fieldBox field-%s hidden">' % field_name) + self.assertContains( + response, '<div class="fieldBox field-%s hidden">' % field_name + ) def assert_fieldline_visible(self, response): self.assertContains(response, '<div class="form-row field-first field-second">') @@ -6515,28 +7710,31 @@ class TestLabelVisibility(TestCase): self.assertContains(response, '<div class="form-row hidden') -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminViewOnSiteTests(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - - cls.s1 = State.objects.create(name='New York') - cls.s2 = State.objects.create(name='Illinois') - cls.s3 = State.objects.create(name='California') - cls.c1 = City.objects.create(state=cls.s1, name='New York') - cls.c2 = City.objects.create(state=cls.s2, name='Chicago') - cls.c3 = City.objects.create(state=cls.s3, name='San Francisco') - cls.r1 = Restaurant.objects.create(city=cls.c1, name='Italian Pizza') - cls.r2 = Restaurant.objects.create(city=cls.c1, name='Boulevard') - cls.r3 = Restaurant.objects.create(city=cls.c2, name='Chinese Dinner') - cls.r4 = Restaurant.objects.create(city=cls.c2, name='Angels') - cls.r5 = Restaurant.objects.create(city=cls.c2, name='Take Away') - cls.r6 = Restaurant.objects.create(city=cls.c3, name='The Unknown Restaurant') - cls.w1 = Worker.objects.create(work_at=cls.r1, name='Mario', surname='Rossi') - cls.w2 = Worker.objects.create(work_at=cls.r1, name='Antonio', surname='Bianchi') - cls.w3 = Worker.objects.create(work_at=cls.r1, name='John', surname='Doe') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + + cls.s1 = State.objects.create(name="New York") + cls.s2 = State.objects.create(name="Illinois") + cls.s3 = State.objects.create(name="California") + cls.c1 = City.objects.create(state=cls.s1, name="New York") + cls.c2 = City.objects.create(state=cls.s2, name="Chicago") + cls.c3 = City.objects.create(state=cls.s3, name="San Francisco") + cls.r1 = Restaurant.objects.create(city=cls.c1, name="Italian Pizza") + cls.r2 = Restaurant.objects.create(city=cls.c1, name="Boulevard") + cls.r3 = Restaurant.objects.create(city=cls.c2, name="Chinese Dinner") + cls.r4 = Restaurant.objects.create(city=cls.c2, name="Angels") + cls.r5 = Restaurant.objects.create(city=cls.c2, name="Take Away") + cls.r6 = Restaurant.objects.create(city=cls.c3, name="The Unknown Restaurant") + cls.w1 = Worker.objects.create(work_at=cls.r1, name="Mario", surname="Rossi") + cls.w2 = Worker.objects.create( + work_at=cls.r1, name="Antonio", surname="Bianchi" + ) + cls.w3 = Worker.objects.create(work_at=cls.r1, name="John", surname="Doe") def setUp(self): self.client.force_login(self.superuser) @@ -6553,26 +7751,37 @@ class AdminViewOnSiteTests(TestCase): # not included on the parent form, and the family_name of the parent # does not match that of the child post_data = { - 'family_name': 'Test1', - 'dependentchild_set-TOTAL_FORMS': '1', - 'dependentchild_set-INITIAL_FORMS': '0', - 'dependentchild_set-MAX_NUM_FORMS': '1', - 'dependentchild_set-0-id': '', - 'dependentchild_set-0-parent': '', - 'dependentchild_set-0-family_name': 'Test2', + "family_name": "Test1", + "dependentchild_set-TOTAL_FORMS": "1", + "dependentchild_set-INITIAL_FORMS": "0", + "dependentchild_set-MAX_NUM_FORMS": "1", + "dependentchild_set-0-id": "", + "dependentchild_set-0-parent": "", + "dependentchild_set-0-family_name": "Test2", } - response = self.client.post(reverse('admin:admin_views_parentwithdependentchildren_add'), post_data) - self.assertFormError(response, 'adminform', 'some_required_info', ['This field is required.']) + response = self.client.post( + reverse("admin:admin_views_parentwithdependentchildren_add"), post_data + ) + self.assertFormError( + response, "adminform", "some_required_info", ["This field is required."] + ) msg = "The form 'adminform' in context 0 does not contain the non-field error 'Error'" with self.assertRaisesMessage(AssertionError, msg): - self.assertFormError(response, 'adminform', None, ['Error']) + self.assertFormError(response, "adminform", None, ["Error"]) self.assertFormsetError( - response, 'inline_admin_formset', 0, None, - ['Children must share a family name with their parents in this contrived test case'] + response, + "inline_admin_formset", + 0, + None, + [ + "Children must share a family name with their parents in this contrived test case" + ], ) msg = "The formset 'inline_admin_formset' in context 22 does not contain any non-form errors." with self.assertRaisesMessage(AssertionError, msg): - self.assertFormsetError(response, 'inline_admin_formset', None, None, ['Error']) + self.assertFormsetError( + response, "inline_admin_formset", None, None, ["Error"] + ) def test_change_view_form_and_formsets_run_validation(self): """ @@ -6580,26 +7789,38 @@ class AdminViewOnSiteTests(TestCase): Verifying that if the parent form fails validation, the inlines also run validation even if validation is contingent on parent form data """ - pwdc = ParentWithDependentChildren.objects.create(some_required_info=6, family_name='Test1') + pwdc = ParentWithDependentChildren.objects.create( + some_required_info=6, family_name="Test1" + ) # The form validation should fail because 'some_required_info' is # not included on the parent form, and the family_name of the parent # does not match that of the child post_data = { - 'family_name': 'Test2', - 'dependentchild_set-TOTAL_FORMS': '1', - 'dependentchild_set-INITIAL_FORMS': '0', - 'dependentchild_set-MAX_NUM_FORMS': '1', - 'dependentchild_set-0-id': '', - 'dependentchild_set-0-parent': str(pwdc.id), - 'dependentchild_set-0-family_name': 'Test1', + "family_name": "Test2", + "dependentchild_set-TOTAL_FORMS": "1", + "dependentchild_set-INITIAL_FORMS": "0", + "dependentchild_set-MAX_NUM_FORMS": "1", + "dependentchild_set-0-id": "", + "dependentchild_set-0-parent": str(pwdc.id), + "dependentchild_set-0-family_name": "Test1", } response = self.client.post( - reverse('admin:admin_views_parentwithdependentchildren_change', args=(pwdc.id,)), post_data + reverse( + "admin:admin_views_parentwithdependentchildren_change", args=(pwdc.id,) + ), + post_data, + ) + self.assertFormError( + response, "adminform", "some_required_info", ["This field is required."] ) - self.assertFormError(response, 'adminform', 'some_required_info', ['This field is required.']) self.assertFormsetError( - response, 'inline_admin_formset', 0, None, - ['Children must share a family name with their parents in this contrived test case'] + response, + "inline_admin_formset", + 0, + None, + [ + "Children must share a family name with their parents in this contrived test case" + ], ) def test_check(self): @@ -6613,33 +7834,48 @@ class AdminViewOnSiteTests(TestCase): CityAdmin.view_on_site = lambda obj: obj.get_absolute_url() self.assertEqual(admin.check(), []) CityAdmin.view_on_site = [] - self.assertEqual(admin.check(), [ - Error( - "The value of 'view_on_site' must be a callable or a boolean value.", - obj=CityAdmin, - id='admin.E025', - ), - ]) + self.assertEqual( + admin.check(), + [ + Error( + "The value of 'view_on_site' must be a callable or a boolean value.", + obj=CityAdmin, + id="admin.E025", + ), + ], + ) finally: # Restore the original values for the benefit of other tests. CityAdmin.view_on_site = True def test_false(self): "The 'View on site' button is not displayed if view_on_site is False" - response = self.client.get(reverse('admin:admin_views_restaurant_change', args=(self.r1.pk,))) + response = self.client.get( + reverse("admin:admin_views_restaurant_change", args=(self.r1.pk,)) + ) content_type_pk = ContentType.objects.get_for_model(Restaurant).pk - self.assertNotContains(response, reverse('admin:view_on_site', args=(content_type_pk, 1))) + self.assertNotContains( + response, reverse("admin:view_on_site", args=(content_type_pk, 1)) + ) def test_true(self): "The default behavior is followed if view_on_site is True" - response = self.client.get(reverse('admin:admin_views_city_change', args=(self.c1.pk,))) + response = self.client.get( + reverse("admin:admin_views_city_change", args=(self.c1.pk,)) + ) content_type_pk = ContentType.objects.get_for_model(City).pk - self.assertContains(response, reverse('admin:view_on_site', args=(content_type_pk, self.c1.pk))) + self.assertContains( + response, reverse("admin:view_on_site", args=(content_type_pk, self.c1.pk)) + ) def test_callable(self): "The right link is displayed if view_on_site is a callable" - response = self.client.get(reverse('admin:admin_views_worker_change', args=(self.w1.pk,))) - self.assertContains(response, '"/worker/%s/%s/"' % (self.w1.surname, self.w1.name)) + response = self.client.get( + reverse("admin:admin_views_worker_change", args=(self.w1.pk,)) + ) + self.assertContains( + response, '"/worker/%s/%s/"' % (self.w1.surname, self.w1.name) + ) def test_missing_get_absolute_url(self): "None is returned if model doesn't have get_absolute_url" @@ -6647,51 +7883,66 @@ class AdminViewOnSiteTests(TestCase): self.assertIsNone(model_admin.get_view_on_site_url(Worker())) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class InlineAdminViewOnSiteTest(TestCase): - @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - - cls.s1 = State.objects.create(name='New York') - cls.s2 = State.objects.create(name='Illinois') - cls.s3 = State.objects.create(name='California') - cls.c1 = City.objects.create(state=cls.s1, name='New York') - cls.c2 = City.objects.create(state=cls.s2, name='Chicago') - cls.c3 = City.objects.create(state=cls.s3, name='San Francisco') - cls.r1 = Restaurant.objects.create(city=cls.c1, name='Italian Pizza') - cls.r2 = Restaurant.objects.create(city=cls.c1, name='Boulevard') - cls.r3 = Restaurant.objects.create(city=cls.c2, name='Chinese Dinner') - cls.r4 = Restaurant.objects.create(city=cls.c2, name='Angels') - cls.r5 = Restaurant.objects.create(city=cls.c2, name='Take Away') - cls.r6 = Restaurant.objects.create(city=cls.c3, name='The Unknown Restaurant') - cls.w1 = Worker.objects.create(work_at=cls.r1, name='Mario', surname='Rossi') - cls.w2 = Worker.objects.create(work_at=cls.r1, name='Antonio', surname='Bianchi') - cls.w3 = Worker.objects.create(work_at=cls.r1, name='John', surname='Doe') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) + + cls.s1 = State.objects.create(name="New York") + cls.s2 = State.objects.create(name="Illinois") + cls.s3 = State.objects.create(name="California") + cls.c1 = City.objects.create(state=cls.s1, name="New York") + cls.c2 = City.objects.create(state=cls.s2, name="Chicago") + cls.c3 = City.objects.create(state=cls.s3, name="San Francisco") + cls.r1 = Restaurant.objects.create(city=cls.c1, name="Italian Pizza") + cls.r2 = Restaurant.objects.create(city=cls.c1, name="Boulevard") + cls.r3 = Restaurant.objects.create(city=cls.c2, name="Chinese Dinner") + cls.r4 = Restaurant.objects.create(city=cls.c2, name="Angels") + cls.r5 = Restaurant.objects.create(city=cls.c2, name="Take Away") + cls.r6 = Restaurant.objects.create(city=cls.c3, name="The Unknown Restaurant") + cls.w1 = Worker.objects.create(work_at=cls.r1, name="Mario", surname="Rossi") + cls.w2 = Worker.objects.create( + work_at=cls.r1, name="Antonio", surname="Bianchi" + ) + cls.w3 = Worker.objects.create(work_at=cls.r1, name="John", surname="Doe") def setUp(self): self.client.force_login(self.superuser) def test_false(self): "The 'View on site' button is not displayed if view_on_site is False" - response = self.client.get(reverse('admin:admin_views_state_change', args=(self.s1.pk,))) + response = self.client.get( + reverse("admin:admin_views_state_change", args=(self.s1.pk,)) + ) content_type_pk = ContentType.objects.get_for_model(City).pk - self.assertNotContains(response, reverse('admin:view_on_site', args=(content_type_pk, self.c1.pk))) + self.assertNotContains( + response, reverse("admin:view_on_site", args=(content_type_pk, self.c1.pk)) + ) def test_true(self): "The 'View on site' button is displayed if view_on_site is True" - response = self.client.get(reverse('admin:admin_views_city_change', args=(self.c1.pk,))) + response = self.client.get( + reverse("admin:admin_views_city_change", args=(self.c1.pk,)) + ) content_type_pk = ContentType.objects.get_for_model(Restaurant).pk - self.assertContains(response, reverse('admin:view_on_site', args=(content_type_pk, self.r1.pk))) + self.assertContains( + response, reverse("admin:view_on_site", args=(content_type_pk, self.r1.pk)) + ) def test_callable(self): "The right link is displayed if view_on_site is a callable" - response = self.client.get(reverse('admin:admin_views_restaurant_change', args=(self.r1.pk,))) - self.assertContains(response, '"/worker_inline/%s/%s/"' % (self.w1.surname, self.w1.name)) + response = self.client.get( + reverse("admin:admin_views_restaurant_change", args=(self.r1.pk,)) + ) + self.assertContains( + response, '"/worker_inline/%s/%s/"' % (self.w1.surname, self.w1.name) + ) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class GetFormsetsWithInlinesArgumentTest(TestCase): """ #23934 - When adding a new model instance in the admin, the 'obj' argument @@ -6703,31 +7954,43 @@ class GetFormsetsWithInlinesArgumentTest(TestCase): @classmethod def setUpTestData(cls): - cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') + cls.superuser = User.objects.create_superuser( + username="super", password="secret", email="super@example.com" + ) def setUp(self): self.client.force_login(self.superuser) def test_explicitly_provided_pk(self): - post_data = {'name': '1'} - response = self.client.post(reverse('admin:admin_views_explicitlyprovidedpk_add'), post_data) + post_data = {"name": "1"} + response = self.client.post( + reverse("admin:admin_views_explicitlyprovidedpk_add"), post_data + ) self.assertEqual(response.status_code, 302) - post_data = {'name': '2'} - response = self.client.post(reverse('admin:admin_views_explicitlyprovidedpk_change', args=(1,)), post_data) + post_data = {"name": "2"} + response = self.client.post( + reverse("admin:admin_views_explicitlyprovidedpk_change", args=(1,)), + post_data, + ) self.assertEqual(response.status_code, 302) def test_implicitly_generated_pk(self): - post_data = {'name': '1'} - response = self.client.post(reverse('admin:admin_views_implicitlygeneratedpk_add'), post_data) + post_data = {"name": "1"} + response = self.client.post( + reverse("admin:admin_views_implicitlygeneratedpk_add"), post_data + ) self.assertEqual(response.status_code, 302) - post_data = {'name': '2'} - response = self.client.post(reverse('admin:admin_views_implicitlygeneratedpk_change', args=(1,)), post_data) + post_data = {"name": "2"} + response = self.client.post( + reverse("admin:admin_views_implicitlygeneratedpk_change", args=(1,)), + post_data, + ) self.assertEqual(response.status_code, 302) -@override_settings(ROOT_URLCONF='admin_views.urls') +@override_settings(ROOT_URLCONF="admin_views.urls") class AdminSiteFinalCatchAllPatternTests(TestCase): """ Verifies the behaviour of the admin catch-all view. @@ -6738,107 +8001,116 @@ class AdminSiteFinalCatchAllPatternTests(TestCase): * Otherwise Http404. * Catch-all view disabled via AdminSite.final_catch_all_view. """ + def test_unknown_url_redirects_login_if_not_authenticated(self): - unknown_url = '/test_admin/admin/unknown/' + unknown_url = "/test_admin/admin/unknown/" response = self.client.get(unknown_url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), unknown_url)) + self.assertRedirects( + response, "%s?next=%s" % (reverse("admin:login"), unknown_url) + ) def test_unknown_url_404_if_authenticated(self): superuser = User.objects.create_superuser( - username='super', - password='secret', - email='super@example.com', + username="super", + password="secret", + email="super@example.com", ) self.client.force_login(superuser) - unknown_url = '/test_admin/admin/unknown/' + unknown_url = "/test_admin/admin/unknown/" response = self.client.get(unknown_url) self.assertEqual(response.status_code, 404) def test_known_url_redirects_login_if_not_authenticated(self): - known_url = reverse('admin:admin_views_article_changelist') + known_url = reverse("admin:admin_views_article_changelist") response = self.client.get(known_url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), known_url)) + self.assertRedirects( + response, "%s?next=%s" % (reverse("admin:login"), known_url) + ) def test_known_url_missing_slash_redirects_login_if_not_authenticated(self): - known_url = reverse('admin:admin_views_article_changelist')[:-1] + known_url = reverse("admin:admin_views_article_changelist")[:-1] response = self.client.get(known_url) # Redirects with the next URL also missing the slash. - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), known_url)) + self.assertRedirects( + response, "%s?next=%s" % (reverse("admin:login"), known_url) + ) def test_non_admin_url_shares_url_prefix(self): - url = reverse('non_admin')[:-1] + url = reverse("non_admin")[:-1] response = self.client.get(url) # Redirects with the next URL also missing the slash. - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), url)) + self.assertRedirects(response, "%s?next=%s" % (reverse("admin:login"), url)) def test_url_without_trailing_slash_if_not_authenticated(self): - url = reverse('admin:article_extra_json') + url = reverse("admin:article_extra_json") response = self.client.get(url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), url)) + self.assertRedirects(response, "%s?next=%s" % (reverse("admin:login"), url)) def test_unkown_url_without_trailing_slash_if_not_authenticated(self): - url = reverse('admin:article_extra_json')[:-1] + url = reverse("admin:article_extra_json")[:-1] response = self.client.get(url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), url)) + self.assertRedirects(response, "%s?next=%s" % (reverse("admin:login"), url)) @override_settings(APPEND_SLASH=True) def test_missing_slash_append_slash_true_unknown_url(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - unknown_url = '/test_admin/admin/unknown/' + unknown_url = "/test_admin/admin/unknown/" response = self.client.get(unknown_url[:-1]) self.assertEqual(response.status_code, 404) @override_settings(APPEND_SLASH=True) def test_missing_slash_append_slash_true(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin:admin_views_article_changelist') + known_url = reverse("admin:admin_views_article_changelist") response = self.client.get(known_url[:-1]) - self.assertRedirects(response, known_url, status_code=301, target_status_code=403) + self.assertRedirects( + response, known_url, status_code=301, target_status_code=403 + ) @override_settings(APPEND_SLASH=True) def test_missing_slash_append_slash_true_script_name(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin:admin_views_article_changelist') - response = self.client.get(known_url[:-1], SCRIPT_NAME='/prefix/') + known_url = reverse("admin:admin_views_article_changelist") + response = self.client.get(known_url[:-1], SCRIPT_NAME="/prefix/") self.assertRedirects( response, - '/prefix' + known_url, + "/prefix" + known_url, status_code=301, fetch_redirect_response=False, ) - @override_settings(APPEND_SLASH=True, FORCE_SCRIPT_NAME='/prefix/') + @override_settings(APPEND_SLASH=True, FORCE_SCRIPT_NAME="/prefix/") def test_missing_slash_append_slash_true_force_script_name(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin:admin_views_article_changelist') + known_url = reverse("admin:admin_views_article_changelist") response = self.client.get(known_url[:-1]) self.assertRedirects( response, - '/prefix' + known_url, + "/prefix" + known_url, status_code=301, fetch_redirect_response=False, ) @@ -6846,129 +8118,150 @@ class AdminSiteFinalCatchAllPatternTests(TestCase): @override_settings(APPEND_SLASH=True) def test_missing_slash_append_slash_true_non_staff_user(self): user = User.objects.create_user( - username='user', - password='secret', - email='user@example.com', + username="user", + password="secret", + email="user@example.com", is_staff=False, ) self.client.force_login(user) - known_url = reverse('admin:admin_views_article_changelist') + known_url = reverse("admin:admin_views_article_changelist") response = self.client.get(known_url[:-1]) - self.assertRedirects(response, '/test_admin/admin/login/?next=/test_admin/admin/admin_views/article') + self.assertRedirects( + response, + "/test_admin/admin/login/?next=/test_admin/admin/admin_views/article", + ) @override_settings(APPEND_SLASH=False) def test_missing_slash_append_slash_false(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin:admin_views_article_changelist') + known_url = reverse("admin:admin_views_article_changelist") response = self.client.get(known_url[:-1]) self.assertEqual(response.status_code, 404) @override_settings(APPEND_SLASH=True) def test_single_model_no_append_slash(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin9:admin_views_actor_changelist') + known_url = reverse("admin9:admin_views_actor_changelist") response = self.client.get(known_url[:-1]) self.assertEqual(response.status_code, 404) # Same tests above with final_catch_all_view=False. def test_unknown_url_404_if_not_authenticated_without_final_catch_all_view(self): - unknown_url = '/test_admin/admin10/unknown/' + unknown_url = "/test_admin/admin10/unknown/" response = self.client.get(unknown_url) self.assertEqual(response.status_code, 404) def test_unknown_url_404_if_authenticated_without_final_catch_all_view(self): superuser = User.objects.create_superuser( - username='super', - password='secret', - email='super@example.com', + username="super", + password="secret", + email="super@example.com", ) self.client.force_login(superuser) - unknown_url = '/test_admin/admin10/unknown/' + unknown_url = "/test_admin/admin10/unknown/" response = self.client.get(unknown_url) self.assertEqual(response.status_code, 404) - def test_known_url_redirects_login_if_not_authenticated_without_final_catch_all_view(self): - known_url = reverse('admin10:admin_views_article_changelist') + def test_known_url_redirects_login_if_not_authenticated_without_final_catch_all_view( + self, + ): + known_url = reverse("admin10:admin_views_article_changelist") response = self.client.get(known_url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin10:login'), known_url)) + self.assertRedirects( + response, "%s?next=%s" % (reverse("admin10:login"), known_url) + ) - def test_known_url_missing_slash_redirects_with_slash_if_not_authenticated_without_final_catch_all_view(self): - known_url = reverse('admin10:admin_views_article_changelist') + def test_known_url_missing_slash_redirects_with_slash_if_not_authenticated_without_final_catch_all_view( + self, + ): + known_url = reverse("admin10:admin_views_article_changelist") response = self.client.get(known_url[:-1]) - self.assertRedirects(response, known_url, status_code=301, fetch_redirect_response=False) + self.assertRedirects( + response, known_url, status_code=301, fetch_redirect_response=False + ) def test_non_admin_url_shares_url_prefix_without_final_catch_all_view(self): - url = reverse('non_admin10') + url = reverse("non_admin10") response = self.client.get(url[:-1]) self.assertRedirects(response, url, status_code=301) - def test_url_without_trailing_slash_if_not_authenticated_without_final_catch_all_view(self): - url = reverse('admin10:article_extra_json') + def test_url_without_trailing_slash_if_not_authenticated_without_final_catch_all_view( + self, + ): + url = reverse("admin10:article_extra_json") response = self.client.get(url) - self.assertRedirects(response, '%s?next=%s' % (reverse('admin10:login'), url)) + self.assertRedirects(response, "%s?next=%s" % (reverse("admin10:login"), url)) - def test_unkown_url_without_trailing_slash_if_not_authenticated_without_final_catch_all_view(self): - url = reverse('admin10:article_extra_json')[:-1] + def test_unkown_url_without_trailing_slash_if_not_authenticated_without_final_catch_all_view( + self, + ): + url = reverse("admin10:article_extra_json")[:-1] response = self.client.get(url) # Matches test_admin/admin10/admin_views/article/<path:object_id>/ - self.assertRedirects(response, url + '/', status_code=301, fetch_redirect_response=False) + self.assertRedirects( + response, url + "/", status_code=301, fetch_redirect_response=False + ) @override_settings(APPEND_SLASH=True) - def test_missing_slash_append_slash_true_unknown_url_without_final_catch_all_view(self): + def test_missing_slash_append_slash_true_unknown_url_without_final_catch_all_view( + self, + ): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - unknown_url = '/test_admin/admin10/unknown/' + unknown_url = "/test_admin/admin10/unknown/" response = self.client.get(unknown_url[:-1]) self.assertEqual(response.status_code, 404) @override_settings(APPEND_SLASH=True) def test_missing_slash_append_slash_true_without_final_catch_all_view(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin10:admin_views_article_changelist') + known_url = reverse("admin10:admin_views_article_changelist") response = self.client.get(known_url[:-1]) - self.assertRedirects(response, known_url, status_code=301, target_status_code=403) + self.assertRedirects( + response, known_url, status_code=301, target_status_code=403 + ) @override_settings(APPEND_SLASH=False) def test_missing_slash_append_slash_false_without_final_catch_all_view(self): superuser = User.objects.create_user( - username='staff', - password='secret', - email='staff@example.com', + username="staff", + password="secret", + email="staff@example.com", is_staff=True, ) self.client.force_login(superuser) - known_url = reverse('admin10:admin_views_article_changelist') + known_url = reverse("admin10:admin_views_article_changelist") response = self.client.get(known_url[:-1]) self.assertEqual(response.status_code, 404) # Outside admin. def test_non_admin_url_404_if_not_authenticated(self): - unknown_url = '/unknown/' + unknown_url = "/unknown/" response = self.client.get(unknown_url) # Does not redirect to the admin login. self.assertEqual(response.status_code, 404) diff --git a/tests/admin_views/urls.py b/tests/admin_views/urls.py index 355e082b68..c1e673d811 100644 --- a/tests/admin_views/urls.py +++ b/tests/admin_views/urls.py @@ -10,23 +10,31 @@ def non_admin_view(request): urlpatterns = [ - path('test_admin/admin/doc/', include('django.contrib.admindocs.urls')), - path('test_admin/admin/secure-view/', views.secure_view, name='secure_view'), - path('test_admin/admin/secure-view2/', views.secure_view2, name='secure_view2'), - path('test_admin/admin/', admin.site.urls), - path('test_admin/admin2/', customadmin.site.urls), - path('test_admin/admin3/', (admin.site.get_urls(), 'admin', 'admin3'), {'form_url': 'pony'}), - path('test_admin/admin4/', customadmin.simple_site.urls), - path('test_admin/admin5/', admin.site2.urls), - path('test_admin/admin6/', admin.site6.urls), - path('test_admin/admin7/', admin.site7.urls), + path("test_admin/admin/doc/", include("django.contrib.admindocs.urls")), + path("test_admin/admin/secure-view/", views.secure_view, name="secure_view"), + path("test_admin/admin/secure-view2/", views.secure_view2, name="secure_view2"), + path("test_admin/admin/", admin.site.urls), + path("test_admin/admin2/", customadmin.site.urls), + path( + "test_admin/admin3/", + (admin.site.get_urls(), "admin", "admin3"), + {"form_url": "pony"}, + ), + path("test_admin/admin4/", customadmin.simple_site.urls), + path("test_admin/admin5/", admin.site2.urls), + path("test_admin/admin6/", admin.site6.urls), + path("test_admin/admin7/", admin.site7.urls), # All admin views accept `extra_context` to allow adding it like this: - path('test_admin/admin8/', (admin.site.get_urls(), 'admin', 'admin-extra-context'), {'extra_context': {}}), - path('test_admin/admin9/', admin.site9.urls), - path('test_admin/admin10/', admin.site10.urls), - path('test_admin/has_permission_admin/', custom_has_permission_admin.site.urls), - path('test_admin/autocomplete_admin/', autocomplete_site.urls), + path( + "test_admin/admin8/", + (admin.site.get_urls(), "admin", "admin-extra-context"), + {"extra_context": {}}, + ), + path("test_admin/admin9/", admin.site9.urls), + path("test_admin/admin10/", admin.site10.urls), + path("test_admin/has_permission_admin/", custom_has_permission_admin.site.urls), + path("test_admin/autocomplete_admin/", autocomplete_site.urls), # Shares the admin URL prefix. - path('test_admin/admin/non_admin_view/', non_admin_view, name='non_admin'), - path('test_admin/admin10/non_admin_view/', non_admin_view, name='non_admin10'), + path("test_admin/admin/non_admin_view/", non_admin_view, name="non_admin"), + path("test_admin/admin10/non_admin_view/", non_admin_view, name="non_admin10"), ] diff --git a/tests/admin_views/views.py b/tests/admin_views/views.py index 430f084693..e3dd549ca4 100644 --- a/tests/admin_views/views.py +++ b/tests/admin_views/views.py @@ -7,6 +7,6 @@ def secure_view(request): return HttpResponse(str(request.POST)) -@staff_member_required(redirect_field_name='myfield') +@staff_member_required(redirect_field_name="myfield") def secure_view2(request): return HttpResponse(str(request.POST)) |