1. import datetime
    
  2. from io import StringIO
    
  3. from wsgiref.util import FileWrapper
    
  4. 
    
  5. from django import forms
    
  6. from django.contrib import admin
    
  7. from django.contrib.admin import BooleanFieldListFilter
    
  8. from django.contrib.admin.views.main import ChangeList
    
  9. from django.contrib.auth.admin import GroupAdmin, UserAdmin
    
  10. from django.contrib.auth.models import Group, User
    
  11. from django.core.exceptions import ValidationError
    
  12. from django.core.mail import EmailMessage
    
  13. from django.db import models
    
  14. from django.forms.models import BaseModelFormSet
    
  15. from django.http import HttpResponse, JsonResponse, StreamingHttpResponse
    
  16. from django.urls import path
    
  17. from django.utils.html import format_html
    
  18. from django.utils.safestring import mark_safe
    
  19. from django.views.decorators.common import no_append_slash
    
  20. 
    
  21. from .forms import MediaActionForm
    
  22. from .models import (
    
  23.     Actor,
    
  24.     AdminOrderedAdminMethod,
    
  25.     AdminOrderedCallable,
    
  26.     AdminOrderedField,
    
  27.     AdminOrderedModelMethod,
    
  28.     Album,
    
  29.     Answer,
    
  30.     Answer2,
    
  31.     Article,
    
  32.     BarAccount,
    
  33.     Book,
    
  34.     Bookmark,
    
  35.     Box,
    
  36.     Category,
    
  37.     Chapter,
    
  38.     ChapterXtra1,
    
  39.     Child,
    
  40.     ChildOfReferer,
    
  41.     Choice,
    
  42.     City,
    
  43.     Collector,
    
  44.     Color,
    
  45.     Color2,
    
  46.     ComplexSortedPerson,
    
  47.     Country,
    
  48.     CoverLetter,
    
  49.     CustomArticle,
    
  50.     CyclicOne,
    
  51.     CyclicTwo,
    
  52.     DependentChild,
    
  53.     DooHickey,
    
  54.     EmptyModel,
    
  55.     EmptyModelHidden,
    
  56.     EmptyModelMixin,
    
  57.     EmptyModelVisible,
    
  58.     ExplicitlyProvidedPK,
    
  59.     ExternalSubscriber,
    
  60.     Fabric,
    
  61.     FancyDoodad,
    
  62.     FieldOverridePost,
    
  63.     FilteredManager,
    
  64.     FooAccount,
    
  65.     FoodDelivery,
    
  66.     FunkyTag,
    
  67.     Gadget,
    
  68.     Gallery,
    
  69.     GenRelReference,
    
  70.     Grommet,
    
  71.     ImplicitlyGeneratedPK,
    
  72.     Ingredient,
    
  73.     InlineReference,
    
  74.     InlineReferer,
    
  75.     Inquisition,
    
  76.     Language,
    
  77.     Link,
    
  78.     MainPrepopulated,
    
  79.     ModelWithStringPrimaryKey,
    
  80.     NotReferenced,
    
  81.     OldSubscriber,
    
  82.     OtherStory,
    
  83.     Paper,
    
  84.     Parent,
    
  85.     ParentWithDependentChildren,
    
  86.     ParentWithUUIDPK,
    
  87.     Person,
    
  88.     Persona,
    
  89.     Picture,
    
  90.     Pizza,
    
  91.     Plot,
    
  92.     PlotDetails,
    
  93.     PlotProxy,
    
  94.     PluggableSearchPerson,
    
  95.     Podcast,
    
  96.     Post,
    
  97.     PrePopulatedPost,
    
  98.     PrePopulatedPostLargeSlug,
    
  99.     PrePopulatedSubPost,
    
  100.     Promo,
    
  101.     Question,
    
  102.     ReadablePizza,
    
  103.     ReadOnlyPizza,
    
  104.     ReadOnlyRelatedField,
    
  105.     Recipe,
    
  106.     Recommendation,
    
  107.     Recommender,
    
  108.     ReferencedByGenRel,
    
  109.     ReferencedByInline,
    
  110.     ReferencedByParent,
    
  111.     RelatedPrepopulated,
    
  112.     RelatedWithUUIDPKModel,
    
  113.     Report,
    
  114.     Reservation,
    
  115.     Restaurant,
    
  116.     RowLevelChangePermissionModel,
    
  117.     Section,
    
  118.     ShortMessage,
    
  119.     Simple,
    
  120.     Sketch,
    
  121.     Song,
    
  122.     State,
    
  123.     Story,
    
  124.     StumpJoke,
    
  125.     Subscriber,
    
  126.     SuperVillain,
    
  127.     Telegram,
    
  128.     Thing,
    
  129.     Topping,
    
  130.     Traveler,
    
  131.     UnchangeableObject,
    
  132.     UndeletableObject,
    
  133.     UnorderedObject,
    
  134.     UserMessenger,
    
  135.     UserProxy,
    
  136.     Villain,
    
  137.     Vodcast,
    
  138.     Whatsit,
    
  139.     Widget,
    
  140.     Worker,
    
  141.     WorkHour,
    
  142. )
    
  143. 
    
  144. 
    
  145. @admin.display(ordering="date")
    
  146. def callable_year(dt_value):
    
  147.     try:
    
  148.         return dt_value.year
    
  149.     except AttributeError:
    
  150.         return None
    
  151. 
    
  152. 
    
  153. class ArticleInline(admin.TabularInline):
    
  154.     model = Article
    
  155.     fk_name = "section"
    
  156.     prepopulated_fields = {"title": ("content",)}
    
  157.     fieldsets = (
    
  158.         ("Some fields", {"classes": ("collapse",), "fields": ("title", "content")}),
    
  159.         ("Some other fields", {"classes": ("wide",), "fields": ("date", "section")}),
    
  160.     )
    
  161. 
    
  162. 
    
  163. class ChapterInline(admin.TabularInline):
    
  164.     model = Chapter
    
  165. 
    
  166. 
    
  167. class ChapterXtra1Admin(admin.ModelAdmin):
    
  168.     list_filter = (
    
  169.         "chap",
    
  170.         "chap__title",
    
  171.         "chap__book",
    
  172.         "chap__book__name",
    
  173.         "chap__book__promo",
    
  174.         "chap__book__promo__name",
    
  175.         "guest_author__promo__book",
    
  176.     )
    
  177. 
    
  178. 
    
  179. class ArticleForm(forms.ModelForm):
    
  180.     extra_form_field = forms.BooleanField(required=False)
    
  181. 
    
  182.     class Meta:
    
  183.         fields = "__all__"
    
  184.         model = Article
    
  185. 
    
  186. 
    
  187. class ArticleAdminWithExtraUrl(admin.ModelAdmin):
    
  188.     def get_urls(self):
    
  189.         urlpatterns = super().get_urls()
    
  190.         urlpatterns.append(
    
  191.             path(
    
  192.                 "extra.json",
    
  193.                 self.admin_site.admin_view(self.extra_json),
    
  194.                 name="article_extra_json",
    
  195.             )
    
  196.         )
    
  197.         return urlpatterns
    
  198. 
    
  199.     def extra_json(self, request):
    
  200.         return JsonResponse({})
    
  201. 
    
  202. 
    
  203. class ArticleAdmin(ArticleAdminWithExtraUrl):
    
  204.     list_display = (
    
  205.         "content",
    
  206.         "date",
    
  207.         callable_year,
    
  208.         "model_year",
    
  209.         "modeladmin_year",
    
  210.         "model_year_reversed",
    
  211.         "section",
    
  212.         lambda obj: obj.title,
    
  213.         "order_by_expression",
    
  214.         "model_property_year",
    
  215.         "model_month",
    
  216.         "order_by_f_expression",
    
  217.         "order_by_orderby_expression",
    
  218.     )
    
  219.     list_editable = ("section",)
    
  220.     list_filter = ("date", "section")
    
  221.     autocomplete_fields = ("section",)
    
  222.     view_on_site = False
    
  223.     form = ArticleForm
    
  224.     fieldsets = (
    
  225.         (
    
  226.             "Some fields",
    
  227.             {
    
  228.                 "classes": ("collapse",),
    
  229.                 "fields": ("title", "content", "extra_form_field"),
    
  230.             },
    
  231.         ),
    
  232.         (
    
  233.             "Some other fields",
    
  234.             {"classes": ("wide",), "fields": ("date", "section", "sub_section")},
    
  235.         ),
    
  236.     )
    
  237. 
    
  238.     # These orderings aren't particularly useful but show that expressions can
    
  239.     # be used for admin_order_field.
    
  240.     @admin.display(ordering=models.F("date") + datetime.timedelta(days=3))
    
  241.     def order_by_expression(self, obj):
    
  242.         return obj.model_year
    
  243. 
    
  244.     @admin.display(ordering=models.F("date"))
    
  245.     def order_by_f_expression(self, obj):
    
  246.         return obj.model_year
    
  247. 
    
  248.     @admin.display(ordering=models.F("date").asc(nulls_last=True))
    
  249.     def order_by_orderby_expression(self, obj):
    
  250.         return obj.model_year
    
  251. 
    
  252.     def changelist_view(self, request):
    
  253.         return super().changelist_view(request, extra_context={"extra_var": "Hello!"})
    
  254. 
    
  255.     @admin.display(ordering="date", description=None)
    
  256.     def modeladmin_year(self, obj):
    
  257.         return obj.date.year
    
  258. 
    
  259.     def delete_model(self, request, obj):
    
  260.         EmailMessage(
    
  261.             "Greetings from a deleted object",
    
  262.             "I hereby inform you that some user deleted me",
    
  263.             "[email protected]",
    
  264.             ["[email protected]"],
    
  265.         ).send()
    
  266.         return super().delete_model(request, obj)
    
  267. 
    
  268.     def save_model(self, request, obj, form, change=True):
    
  269.         EmailMessage(
    
  270.             "Greetings from a created object",
    
  271.             "I hereby inform you that some user created me",
    
  272.             "[email protected]",
    
  273.             ["[email protected]"],
    
  274.         ).send()
    
  275.         return super().save_model(request, obj, form, change)
    
  276. 
    
  277. 
    
  278. class ArticleAdmin2(admin.ModelAdmin):
    
  279.     def has_module_permission(self, request):
    
  280.         return False
    
  281. 
    
  282. 
    
  283. class RowLevelChangePermissionModelAdmin(admin.ModelAdmin):
    
  284.     def has_change_permission(self, request, obj=None):
    
  285.         """Only allow changing objects with even id number"""
    
  286.         return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0)
    
  287. 
    
  288.     def has_view_permission(self, request, obj=None):
    
  289.         """Only allow viewing objects if id is a multiple of 3."""
    
  290.         return request.user.is_staff and obj is not None and obj.id % 3 == 0
    
  291. 
    
  292. 
    
  293. class CustomArticleAdmin(admin.ModelAdmin):
    
  294.     """
    
  295.     Tests various hooks for using custom templates and contexts.
    
  296.     """
    
  297. 
    
  298.     change_list_template = "custom_admin/change_list.html"
    
  299.     change_form_template = "custom_admin/change_form.html"
    
  300.     add_form_template = "custom_admin/add_form.html"
    
  301.     object_history_template = "custom_admin/object_history.html"
    
  302.     delete_confirmation_template = "custom_admin/delete_confirmation.html"
    
  303.     delete_selected_confirmation_template = (
    
  304.         "custom_admin/delete_selected_confirmation.html"
    
  305.     )
    
  306.     popup_response_template = "custom_admin/popup_response.html"
    
  307. 
    
  308.     def changelist_view(self, request):
    
  309.         return super().changelist_view(request, extra_context={"extra_var": "Hello!"})
    
  310. 
    
  311. 
    
  312. class ThingAdmin(admin.ModelAdmin):
    
  313.     list_filter = ("color", "color__warm", "color__value", "pub_date")
    
  314. 
    
  315. 
    
  316. class InquisitionAdmin(admin.ModelAdmin):
    
  317.     list_display = ("leader", "country", "expected", "sketch")
    
  318. 
    
  319.     @admin.display
    
  320.     def sketch(self, obj):
    
  321.         # A method with the same name as a reverse accessor.
    
  322.         return "list-display-sketch"
    
  323. 
    
  324. 
    
  325. class SketchAdmin(admin.ModelAdmin):
    
  326.     raw_id_fields = ("inquisition", "defendant0", "defendant1")
    
  327. 
    
  328. 
    
  329. class FabricAdmin(admin.ModelAdmin):
    
  330.     list_display = ("surface",)
    
  331.     list_filter = ("surface",)
    
  332. 
    
  333. 
    
  334. class BasePersonModelFormSet(BaseModelFormSet):
    
  335.     def clean(self):
    
  336.         for person_dict in self.cleaned_data:
    
  337.             person = person_dict.get("id")
    
  338.             alive = person_dict.get("alive")
    
  339.             if person and alive and person.name == "Grace Hopper":
    
  340.                 raise ValidationError("Grace is not a Zombie")
    
  341. 
    
  342. 
    
  343. class PersonAdmin(admin.ModelAdmin):
    
  344.     list_display = ("name", "gender", "alive")
    
  345.     list_editable = ("gender", "alive")
    
  346.     list_filter = ("gender",)
    
  347.     search_fields = ("^name",)
    
  348.     save_as = True
    
  349. 
    
  350.     def get_changelist_formset(self, request, **kwargs):
    
  351.         return super().get_changelist_formset(
    
  352.             request, formset=BasePersonModelFormSet, **kwargs
    
  353.         )
    
  354. 
    
  355.     def get_queryset(self, request):
    
  356.         # Order by a field that isn't in list display, to be able to test
    
  357.         # whether ordering is preserved.
    
  358.         return super().get_queryset(request).order_by("age")
    
  359. 
    
  360. 
    
  361. class FooAccountAdmin(admin.StackedInline):
    
  362.     model = FooAccount
    
  363.     extra = 1
    
  364. 
    
  365. 
    
  366. class BarAccountAdmin(admin.StackedInline):
    
  367.     model = BarAccount
    
  368.     extra = 1
    
  369. 
    
  370. 
    
  371. class PersonaAdmin(admin.ModelAdmin):
    
  372.     inlines = (FooAccountAdmin, BarAccountAdmin)
    
  373. 
    
  374. 
    
  375. class SubscriberAdmin(admin.ModelAdmin):
    
  376.     actions = ["mail_admin"]
    
  377.     action_form = MediaActionForm
    
  378. 
    
  379.     def delete_queryset(self, request, queryset):
    
  380.         SubscriberAdmin.overridden = True
    
  381.         super().delete_queryset(request, queryset)
    
  382. 
    
  383.     @admin.action
    
  384.     def mail_admin(self, request, selected):
    
  385.         EmailMessage(
    
  386.             "Greetings from a ModelAdmin action",
    
  387.             "This is the test email from an admin action",
    
  388.             "[email protected]",
    
  389.             ["[email protected]"],
    
  390.         ).send()
    
  391. 
    
  392. 
    
  393. @admin.action(description="External mail (Another awesome action)")
    
  394. def external_mail(modeladmin, request, selected):
    
  395.     EmailMessage(
    
  396.         "Greetings from a function action",
    
  397.         "This is the test email from a function action",
    
  398.         "[email protected]",
    
  399.         ["[email protected]"],
    
  400.     ).send()
    
  401. 
    
  402. 
    
  403. @admin.action(description="Redirect to (Awesome action)")
    
  404. def redirect_to(modeladmin, request, selected):
    
  405.     from django.http import HttpResponseRedirect
    
  406. 
    
  407.     return HttpResponseRedirect("/some-where-else/")
    
  408. 
    
  409. 
    
  410. @admin.action(description="Download subscription")
    
  411. def download(modeladmin, request, selected):
    
  412.     buf = StringIO("This is the content of the file")
    
  413.     return StreamingHttpResponse(FileWrapper(buf))
    
  414. 
    
  415. 
    
  416. @admin.action(description="No permission to run")
    
  417. def no_perm(modeladmin, request, selected):
    
  418.     return HttpResponse(content="No permission to perform this action", status=403)
    
  419. 
    
  420. 
    
  421. class ExternalSubscriberAdmin(admin.ModelAdmin):
    
  422.     actions = [redirect_to, external_mail, download, no_perm]
    
  423. 
    
  424. 
    
  425. class PodcastAdmin(admin.ModelAdmin):
    
  426.     list_display = ("name", "release_date")
    
  427.     list_editable = ("release_date",)
    
  428.     date_hierarchy = "release_date"
    
  429.     ordering = ("name",)
    
  430. 
    
  431. 
    
  432. class VodcastAdmin(admin.ModelAdmin):
    
  433.     list_display = ("name", "released")
    
  434.     list_editable = ("released",)
    
  435. 
    
  436.     ordering = ("name",)
    
  437. 
    
  438. 
    
  439. class ChildInline(admin.StackedInline):
    
  440.     model = Child
    
  441. 
    
  442. 
    
  443. class ParentAdmin(admin.ModelAdmin):
    
  444.     model = Parent
    
  445.     inlines = [ChildInline]
    
  446.     save_as = True
    
  447.     list_display = (
    
  448.         "id",
    
  449.         "name",
    
  450.     )
    
  451.     list_display_links = ("id",)
    
  452.     list_editable = ("name",)
    
  453. 
    
  454.     def save_related(self, request, form, formsets, change):
    
  455.         super().save_related(request, form, formsets, change)
    
  456.         first_name, last_name = form.instance.name.split()
    
  457.         for child in form.instance.child_set.all():
    
  458.             if len(child.name.split()) < 2:
    
  459.                 child.name = child.name + " " + last_name
    
  460.                 child.save()
    
  461. 
    
  462. 
    
  463. class EmptyModelAdmin(admin.ModelAdmin):
    
  464.     def get_queryset(self, request):
    
  465.         return super().get_queryset(request).filter(pk__gt=1)
    
  466. 
    
  467. 
    
  468. class OldSubscriberAdmin(admin.ModelAdmin):
    
  469.     actions = None
    
  470. 
    
  471. 
    
  472. class PictureInline(admin.TabularInline):
    
  473.     model = Picture
    
  474.     extra = 1
    
  475. 
    
  476. 
    
  477. class GalleryAdmin(admin.ModelAdmin):
    
  478.     inlines = [PictureInline]
    
  479. 
    
  480. 
    
  481. class PictureAdmin(admin.ModelAdmin):
    
  482.     pass
    
  483. 
    
  484. 
    
  485. class LanguageAdmin(admin.ModelAdmin):
    
  486.     list_display = ["iso", "shortlist", "english_name", "name"]
    
  487.     list_editable = ["shortlist"]
    
  488. 
    
  489. 
    
  490. class RecommendationAdmin(admin.ModelAdmin):
    
  491.     show_full_result_count = False
    
  492.     search_fields = (
    
  493.         "=titletranslation__text",
    
  494.         "=the_recommender__titletranslation__text",
    
  495.     )
    
  496. 
    
  497. 
    
  498. class WidgetInline(admin.StackedInline):
    
  499.     model = Widget
    
  500. 
    
  501. 
    
  502. class DooHickeyInline(admin.StackedInline):
    
  503.     model = DooHickey
    
  504. 
    
  505. 
    
  506. class GrommetInline(admin.StackedInline):
    
  507.     model = Grommet
    
  508. 
    
  509. 
    
  510. class WhatsitInline(admin.StackedInline):
    
  511.     model = Whatsit
    
  512. 
    
  513. 
    
  514. class FancyDoodadInline(admin.StackedInline):
    
  515.     model = FancyDoodad
    
  516. 
    
  517. 
    
  518. class CategoryAdmin(admin.ModelAdmin):
    
  519.     list_display = ("id", "collector", "order")
    
  520.     list_editable = ("order",)
    
  521. 
    
  522. 
    
  523. class CategoryInline(admin.StackedInline):
    
  524.     model = Category
    
  525. 
    
  526. 
    
  527. class CollectorAdmin(admin.ModelAdmin):
    
  528.     inlines = [
    
  529.         WidgetInline,
    
  530.         DooHickeyInline,
    
  531.         GrommetInline,
    
  532.         WhatsitInline,
    
  533.         FancyDoodadInline,
    
  534.         CategoryInline,
    
  535.     ]
    
  536. 
    
  537. 
    
  538. class LinkInline(admin.TabularInline):
    
  539.     model = Link
    
  540.     extra = 1
    
  541. 
    
  542.     readonly_fields = ("posted", "multiline", "readonly_link_content")
    
  543. 
    
  544.     @admin.display
    
  545.     def multiline(self, instance):
    
  546.         return "InlineMultiline\ntest\nstring"
    
  547. 
    
  548. 
    
  549. class SubPostInline(admin.TabularInline):
    
  550.     model = PrePopulatedSubPost
    
  551. 
    
  552.     prepopulated_fields = {"subslug": ("subtitle",)}
    
  553. 
    
  554.     def get_readonly_fields(self, request, obj=None):
    
  555.         if obj and obj.published:
    
  556.             return ("subslug",)
    
  557.         return self.readonly_fields
    
  558. 
    
  559.     def get_prepopulated_fields(self, request, obj=None):
    
  560.         if obj and obj.published:
    
  561.             return {}
    
  562.         return self.prepopulated_fields
    
  563. 
    
  564. 
    
  565. class PrePopulatedPostAdmin(admin.ModelAdmin):
    
  566.     list_display = ["title", "slug"]
    
  567.     prepopulated_fields = {"slug": ("title",)}
    
  568. 
    
  569.     inlines = [SubPostInline]
    
  570. 
    
  571.     def get_readonly_fields(self, request, obj=None):
    
  572.         if obj and obj.published:
    
  573.             return ("slug",)
    
  574.         return self.readonly_fields
    
  575. 
    
  576.     def get_prepopulated_fields(self, request, obj=None):
    
  577.         if obj and obj.published:
    
  578.             return {}
    
  579.         return self.prepopulated_fields
    
  580. 
    
  581. 
    
  582. class PrePopulatedPostReadOnlyAdmin(admin.ModelAdmin):
    
  583.     prepopulated_fields = {"slug": ("title",)}
    
  584. 
    
  585.     def has_change_permission(self, *args, **kwargs):
    
  586.         return False
    
  587. 
    
  588. 
    
  589. class PostAdmin(admin.ModelAdmin):
    
  590.     list_display = ["title", "public"]
    
  591.     readonly_fields = (
    
  592.         "posted",
    
  593.         "awesomeness_level",
    
  594.         "coolness",
    
  595.         "value",
    
  596.         "multiline",
    
  597.         "multiline_html",
    
  598.         lambda obj: "foo",
    
  599.         "readonly_content",
    
  600.     )
    
  601. 
    
  602.     inlines = [LinkInline]
    
  603. 
    
  604.     @admin.display
    
  605.     def coolness(self, instance):
    
  606.         if instance.pk:
    
  607.             return "%d amount of cool." % instance.pk
    
  608.         else:
    
  609.             return "Unknown coolness."
    
  610. 
    
  611.     @admin.display(description="Value in $US")
    
  612.     def value(self, instance):
    
  613.         return 1000
    
  614. 
    
  615.     @admin.display
    
  616.     def multiline(self, instance):
    
  617.         return "Multiline\ntest\nstring"
    
  618. 
    
  619.     @admin.display
    
  620.     def multiline_html(self, instance):
    
  621.         return mark_safe("Multiline<br>\nhtml<br>\ncontent")
    
  622. 
    
  623. 
    
  624. class FieldOverridePostForm(forms.ModelForm):
    
  625.     model = FieldOverridePost
    
  626. 
    
  627.     class Meta:
    
  628.         help_texts = {
    
  629.             "posted": "Overridden help text for the date",
    
  630.         }
    
  631.         labels = {
    
  632.             "public": "Overridden public label",
    
  633.         }
    
  634. 
    
  635. 
    
  636. class FieldOverridePostAdmin(PostAdmin):
    
  637.     form = FieldOverridePostForm
    
  638. 
    
  639. 
    
  640. class CustomChangeList(ChangeList):
    
  641.     def get_queryset(self, request):
    
  642.         return self.root_queryset.order_by("pk").filter(pk=9999)  # Doesn't exist
    
  643. 
    
  644. 
    
  645. class GadgetAdmin(admin.ModelAdmin):
    
  646.     def get_changelist(self, request, **kwargs):
    
  647.         return CustomChangeList
    
  648. 
    
  649. 
    
  650. class ToppingAdmin(admin.ModelAdmin):
    
  651.     readonly_fields = ("pizzas",)
    
  652. 
    
  653. 
    
  654. class PizzaAdmin(admin.ModelAdmin):
    
  655.     readonly_fields = ("toppings",)
    
  656. 
    
  657. 
    
  658. class ReadOnlyRelatedFieldAdmin(admin.ModelAdmin):
    
  659.     readonly_fields = ("chapter", "language", "user")
    
  660. 
    
  661. 
    
  662. class StudentAdmin(admin.ModelAdmin):
    
  663.     search_fields = ("name",)
    
  664. 
    
  665. 
    
  666. class ReadOnlyPizzaAdmin(admin.ModelAdmin):
    
  667.     readonly_fields = ("name", "toppings")
    
  668. 
    
  669.     def has_add_permission(self, request):
    
  670.         return False
    
  671. 
    
  672.     def has_change_permission(self, request, obj=None):
    
  673.         return True
    
  674. 
    
  675.     def has_delete_permission(self, request, obj=None):
    
  676.         return True
    
  677. 
    
  678. 
    
  679. class WorkHourAdmin(admin.ModelAdmin):
    
  680.     list_display = ("datum", "employee")
    
  681.     list_filter = ("employee",)
    
  682. 
    
  683. 
    
  684. class FoodDeliveryAdmin(admin.ModelAdmin):
    
  685.     list_display = ("reference", "driver", "restaurant")
    
  686.     list_editable = ("driver", "restaurant")
    
  687. 
    
  688. 
    
  689. class CoverLetterAdmin(admin.ModelAdmin):
    
  690.     """
    
  691.     A ModelAdmin with a custom get_queryset() method that uses defer(), to test
    
  692.     verbose_name display in messages shown after adding/editing CoverLetter
    
  693.     instances. Note that the CoverLetter model defines a __str__ method.
    
  694.     For testing fix for ticket #14529.
    
  695.     """
    
  696. 
    
  697.     def get_queryset(self, request):
    
  698.         return super().get_queryset(request).defer("date_written")
    
  699. 
    
  700. 
    
  701. class PaperAdmin(admin.ModelAdmin):
    
  702.     """
    
  703.     A ModelAdmin with a custom get_queryset() method that uses only(), to test
    
  704.     verbose_name display in messages shown after adding/editing Paper
    
  705.     instances.
    
  706.     For testing fix for ticket #14529.
    
  707.     """
    
  708. 
    
  709.     def get_queryset(self, request):
    
  710.         return super().get_queryset(request).only("title")
    
  711. 
    
  712. 
    
  713. class ShortMessageAdmin(admin.ModelAdmin):
    
  714.     """
    
  715.     A ModelAdmin with a custom get_queryset() method that uses defer(), to test
    
  716.     verbose_name display in messages shown after adding/editing ShortMessage
    
  717.     instances.
    
  718.     For testing fix for ticket #14529.
    
  719.     """
    
  720. 
    
  721.     def get_queryset(self, request):
    
  722.         return super().get_queryset(request).defer("timestamp")
    
  723. 
    
  724. 
    
  725. class TelegramAdmin(admin.ModelAdmin):
    
  726.     """
    
  727.     A ModelAdmin with a custom get_queryset() method that uses only(), to test
    
  728.     verbose_name display in messages shown after adding/editing Telegram
    
  729.     instances. Note that the Telegram model defines a __str__ method.
    
  730.     For testing fix for ticket #14529.
    
  731.     """
    
  732. 
    
  733.     def get_queryset(self, request):
    
  734.         return super().get_queryset(request).only("title")
    
  735. 
    
  736. 
    
  737. class StoryForm(forms.ModelForm):
    
  738.     class Meta:
    
  739.         widgets = {"title": forms.HiddenInput}
    
  740. 
    
  741. 
    
  742. class StoryAdmin(admin.ModelAdmin):
    
  743.     list_display = ("id", "title", "content")
    
  744.     list_display_links = ("title",)  # 'id' not in list_display_links
    
  745.     list_editable = ("content",)
    
  746.     form = StoryForm
    
  747.     ordering = ["-id"]
    
  748. 
    
  749. 
    
  750. class OtherStoryAdmin(admin.ModelAdmin):
    
  751.     list_display = ("id", "title", "content")
    
  752.     list_display_links = ("title", "id")  # 'id' in list_display_links
    
  753.     list_editable = ("content",)
    
  754.     ordering = ["-id"]
    
  755. 
    
  756. 
    
  757. class ComplexSortedPersonAdmin(admin.ModelAdmin):
    
  758.     list_display = ("name", "age", "is_employee", "colored_name")
    
  759.     ordering = ("name",)
    
  760. 
    
  761.     @admin.display(ordering="name")
    
  762.     def colored_name(self, obj):
    
  763.         return format_html('<span style="color: #ff00ff;">{}</span>', obj.name)
    
  764. 
    
  765. 
    
  766. class PluggableSearchPersonAdmin(admin.ModelAdmin):
    
  767.     list_display = ("name", "age")
    
  768.     search_fields = ("name",)
    
  769. 
    
  770.     def get_search_results(self, request, queryset, search_term):
    
  771.         queryset, may_have_duplicates = super().get_search_results(
    
  772.             request,
    
  773.             queryset,
    
  774.             search_term,
    
  775.         )
    
  776.         try:
    
  777.             search_term_as_int = int(search_term)
    
  778.         except ValueError:
    
  779.             pass
    
  780.         else:
    
  781.             queryset |= self.model.objects.filter(age=search_term_as_int)
    
  782.         return queryset, may_have_duplicates
    
  783. 
    
  784. 
    
  785. class AlbumAdmin(admin.ModelAdmin):
    
  786.     list_filter = ["title"]
    
  787. 
    
  788. 
    
  789. class QuestionAdmin(admin.ModelAdmin):
    
  790.     ordering = ["-posted"]
    
  791.     search_fields = ["question"]
    
  792.     autocomplete_fields = ["related_questions"]
    
  793. 
    
  794. 
    
  795. class AnswerAdmin(admin.ModelAdmin):
    
  796.     autocomplete_fields = ["question"]
    
  797. 
    
  798. 
    
  799. class PrePopulatedPostLargeSlugAdmin(admin.ModelAdmin):
    
  800.     prepopulated_fields = {"slug": ("title",)}
    
  801. 
    
  802. 
    
  803. class AdminOrderedFieldAdmin(admin.ModelAdmin):
    
  804.     ordering = ("order",)
    
  805.     list_display = ("stuff", "order")
    
  806. 
    
  807. 
    
  808. class AdminOrderedModelMethodAdmin(admin.ModelAdmin):
    
  809.     ordering = ("order",)
    
  810.     list_display = ("stuff", "some_order")
    
  811. 
    
  812. 
    
  813. class AdminOrderedAdminMethodAdmin(admin.ModelAdmin):
    
  814.     @admin.display(ordering="order")
    
  815.     def some_admin_order(self, obj):
    
  816.         return obj.order
    
  817. 
    
  818.     ordering = ("order",)
    
  819.     list_display = ("stuff", "some_admin_order")
    
  820. 
    
  821. 
    
  822. @admin.display(ordering="order")
    
  823. def admin_ordered_callable(obj):
    
  824.     return obj.order
    
  825. 
    
  826. 
    
  827. class AdminOrderedCallableAdmin(admin.ModelAdmin):
    
  828.     ordering = ("order",)
    
  829.     list_display = ("stuff", admin_ordered_callable)
    
  830. 
    
  831. 
    
  832. class ReportAdmin(admin.ModelAdmin):
    
  833.     def extra(self, request):
    
  834.         return HttpResponse()
    
  835. 
    
  836.     def get_urls(self):
    
  837.         # Corner case: Don't call parent implementation
    
  838.         return [path("extra/", self.extra, name="cable_extra")]
    
  839. 
    
  840. 
    
  841. class CustomTemplateBooleanFieldListFilter(BooleanFieldListFilter):
    
  842.     template = "custom_filter_template.html"
    
  843. 
    
  844. 
    
  845. class CustomTemplateFilterColorAdmin(admin.ModelAdmin):
    
  846.     list_filter = (("warm", CustomTemplateBooleanFieldListFilter),)
    
  847. 
    
  848. 
    
  849. # For Selenium Prepopulated tests -------------------------------------
    
  850. class RelatedPrepopulatedInline1(admin.StackedInline):
    
  851.     fieldsets = (
    
  852.         (
    
  853.             None,
    
  854.             {
    
  855.                 "fields": (
    
  856.                     ("fk", "m2m"),
    
  857.                     ("pubdate", "status"),
    
  858.                     (
    
  859.                         "name",
    
  860.                         "slug1",
    
  861.                         "slug2",
    
  862.                     ),
    
  863.                 ),
    
  864.             },
    
  865.         ),
    
  866.     )
    
  867.     formfield_overrides = {models.CharField: {"strip": False}}
    
  868.     model = RelatedPrepopulated
    
  869.     extra = 1
    
  870.     autocomplete_fields = ["fk", "m2m"]
    
  871.     prepopulated_fields = {
    
  872.         "slug1": ["name", "pubdate"],
    
  873.         "slug2": ["status", "name"],
    
  874.     }
    
  875. 
    
  876. 
    
  877. class RelatedPrepopulatedInline2(admin.TabularInline):
    
  878.     model = RelatedPrepopulated
    
  879.     extra = 1
    
  880.     autocomplete_fields = ["fk", "m2m"]
    
  881.     prepopulated_fields = {
    
  882.         "slug1": ["name", "pubdate"],
    
  883.         "slug2": ["status", "name"],
    
  884.     }
    
  885. 
    
  886. 
    
  887. class RelatedPrepopulatedInline3(admin.TabularInline):
    
  888.     model = RelatedPrepopulated
    
  889.     extra = 0
    
  890.     autocomplete_fields = ["fk", "m2m"]
    
  891. 
    
  892. 
    
  893. class RelatedPrepopulatedStackedInlineNoFieldsets(admin.StackedInline):
    
  894.     model = RelatedPrepopulated
    
  895.     extra = 1
    
  896.     prepopulated_fields = {
    
  897.         "slug1": ["name", "pubdate"],
    
  898.         "slug2": ["status"],
    
  899.     }
    
  900. 
    
  901. 
    
  902. class MainPrepopulatedAdmin(admin.ModelAdmin):
    
  903.     inlines = [
    
  904.         RelatedPrepopulatedInline1,
    
  905.         RelatedPrepopulatedInline2,
    
  906.         RelatedPrepopulatedInline3,
    
  907.         RelatedPrepopulatedStackedInlineNoFieldsets,
    
  908.     ]
    
  909.     fieldsets = (
    
  910.         (
    
  911.             None,
    
  912.             {"fields": (("pubdate", "status"), ("name", "slug1", "slug2", "slug3"))},
    
  913.         ),
    
  914.     )
    
  915.     formfield_overrides = {models.CharField: {"strip": False}}
    
  916.     prepopulated_fields = {
    
  917.         "slug1": ["name", "pubdate"],
    
  918.         "slug2": ["status", "name"],
    
  919.         "slug3": ["name"],
    
  920.     }
    
  921. 
    
  922. 
    
  923. class UnorderedObjectAdmin(admin.ModelAdmin):
    
  924.     list_display = ["id", "name"]
    
  925.     list_display_links = ["id"]
    
  926.     list_editable = ["name"]
    
  927.     list_per_page = 2
    
  928. 
    
  929. 
    
  930. class UndeletableObjectAdmin(admin.ModelAdmin):
    
  931.     def change_view(self, *args, **kwargs):
    
  932.         kwargs["extra_context"] = {"show_delete": False}
    
  933.         return super().change_view(*args, **kwargs)
    
  934. 
    
  935. 
    
  936. class UnchangeableObjectAdmin(admin.ModelAdmin):
    
  937.     def get_urls(self):
    
  938.         # Disable change_view, but leave other urls untouched
    
  939.         urlpatterns = super().get_urls()
    
  940.         return [p for p in urlpatterns if p.name and not p.name.endswith("_change")]
    
  941. 
    
  942. 
    
  943. @admin.display
    
  944. def callable_on_unknown(obj):
    
  945.     return obj.unknown
    
  946. 
    
  947. 
    
  948. class AttributeErrorRaisingAdmin(admin.ModelAdmin):
    
  949.     list_display = [callable_on_unknown]
    
  950. 
    
  951. 
    
  952. class CustomManagerAdmin(admin.ModelAdmin):
    
  953.     def get_queryset(self, request):
    
  954.         return FilteredManager.objects
    
  955. 
    
  956. 
    
  957. class MessageTestingAdmin(admin.ModelAdmin):
    
  958.     actions = [
    
  959.         "message_debug",
    
  960.         "message_info",
    
  961.         "message_success",
    
  962.         "message_warning",
    
  963.         "message_error",
    
  964.         "message_extra_tags",
    
  965.     ]
    
  966. 
    
  967.     @admin.action
    
  968.     def message_debug(self, request, selected):
    
  969.         self.message_user(request, "Test debug", level="debug")
    
  970. 
    
  971.     @admin.action
    
  972.     def message_info(self, request, selected):
    
  973.         self.message_user(request, "Test info", level="info")
    
  974. 
    
  975.     @admin.action
    
  976.     def message_success(self, request, selected):
    
  977.         self.message_user(request, "Test success", level="success")
    
  978. 
    
  979.     @admin.action
    
  980.     def message_warning(self, request, selected):
    
  981.         self.message_user(request, "Test warning", level="warning")
    
  982. 
    
  983.     @admin.action
    
  984.     def message_error(self, request, selected):
    
  985.         self.message_user(request, "Test error", level="error")
    
  986. 
    
  987.     @admin.action
    
  988.     def message_extra_tags(self, request, selected):
    
  989.         self.message_user(request, "Test tags", extra_tags="extra_tag")
    
  990. 
    
  991. 
    
  992. class ChoiceList(admin.ModelAdmin):
    
  993.     list_display = ["choice"]
    
  994.     readonly_fields = ["choice"]
    
  995.     fields = ["choice"]
    
  996. 
    
  997. 
    
  998. class DependentChildAdminForm(forms.ModelForm):
    
  999.     """
    
  1000.     Issue #20522
    
  1001.     Form to test child dependency on parent object's validation
    
  1002.     """
    
  1003. 
    
  1004.     def clean(self):
    
  1005.         parent = self.cleaned_data.get("parent")
    
  1006.         if parent.family_name and parent.family_name != self.cleaned_data.get(
    
  1007.             "family_name"
    
  1008.         ):
    
  1009.             raise ValidationError(
    
  1010.                 "Children must share a family name with their parents "
    
  1011.                 + "in this contrived test case"
    
  1012.             )
    
  1013.         return super().clean()
    
  1014. 
    
  1015. 
    
  1016. class DependentChildInline(admin.TabularInline):
    
  1017.     model = DependentChild
    
  1018.     form = DependentChildAdminForm
    
  1019. 
    
  1020. 
    
  1021. class ParentWithDependentChildrenAdmin(admin.ModelAdmin):
    
  1022.     inlines = [DependentChildInline]
    
  1023. 
    
  1024. 
    
  1025. # Tests for ticket 11277 ----------------------------------
    
  1026. 
    
  1027. 
    
  1028. class FormWithoutHiddenField(forms.ModelForm):
    
  1029.     first = forms.CharField()
    
  1030.     second = forms.CharField()
    
  1031. 
    
  1032. 
    
  1033. class FormWithoutVisibleField(forms.ModelForm):
    
  1034.     first = forms.CharField(widget=forms.HiddenInput)
    
  1035.     second = forms.CharField(widget=forms.HiddenInput)
    
  1036. 
    
  1037. 
    
  1038. class FormWithVisibleAndHiddenField(forms.ModelForm):
    
  1039.     first = forms.CharField(widget=forms.HiddenInput)
    
  1040.     second = forms.CharField()
    
  1041. 
    
  1042. 
    
  1043. class EmptyModelVisibleAdmin(admin.ModelAdmin):
    
  1044.     form = FormWithoutHiddenField
    
  1045.     fieldsets = (
    
  1046.         (
    
  1047.             None,
    
  1048.             {
    
  1049.                 "fields": (("first", "second"),),
    
  1050.             },
    
  1051.         ),
    
  1052.     )
    
  1053. 
    
  1054. 
    
  1055. class EmptyModelHiddenAdmin(admin.ModelAdmin):
    
  1056.     form = FormWithoutVisibleField
    
  1057.     fieldsets = EmptyModelVisibleAdmin.fieldsets
    
  1058. 
    
  1059. 
    
  1060. class EmptyModelMixinAdmin(admin.ModelAdmin):
    
  1061.     form = FormWithVisibleAndHiddenField
    
  1062.     fieldsets = EmptyModelVisibleAdmin.fieldsets
    
  1063. 
    
  1064. 
    
  1065. class CityInlineAdmin(admin.TabularInline):
    
  1066.     model = City
    
  1067.     view_on_site = False
    
  1068. 
    
  1069. 
    
  1070. class StateAdminForm(forms.ModelForm):
    
  1071.     nolabel_form_field = forms.BooleanField(required=False)
    
  1072. 
    
  1073.     class Meta:
    
  1074.         model = State
    
  1075.         fields = "__all__"
    
  1076.         labels = {"name": "State name (from form’s Meta.labels)"}
    
  1077. 
    
  1078.     @property
    
  1079.     def changed_data(self):
    
  1080.         data = super().changed_data
    
  1081.         if data:
    
  1082.             # Add arbitrary name to changed_data to test
    
  1083.             # change message construction.
    
  1084.             return data + ["not_a_form_field"]
    
  1085.         return data
    
  1086. 
    
  1087. 
    
  1088. class StateAdmin(admin.ModelAdmin):
    
  1089.     inlines = [CityInlineAdmin]
    
  1090.     form = StateAdminForm
    
  1091. 
    
  1092. 
    
  1093. class RestaurantInlineAdmin(admin.TabularInline):
    
  1094.     model = Restaurant
    
  1095.     view_on_site = True
    
  1096. 
    
  1097. 
    
  1098. class CityAdmin(admin.ModelAdmin):
    
  1099.     inlines = [RestaurantInlineAdmin]
    
  1100.     view_on_site = True
    
  1101. 
    
  1102.     def get_formset_kwargs(self, request, obj, inline, prefix):
    
  1103.         return {
    
  1104.             **super().get_formset_kwargs(request, obj, inline, prefix),
    
  1105.             "form_kwargs": {"initial": {"name": "overridden_name"}},
    
  1106.         }
    
  1107. 
    
  1108. 
    
  1109. class WorkerAdmin(admin.ModelAdmin):
    
  1110.     def view_on_site(self, obj):
    
  1111.         return "/worker/%s/%s/" % (obj.surname, obj.name)
    
  1112. 
    
  1113. 
    
  1114. class WorkerInlineAdmin(admin.TabularInline):
    
  1115.     model = Worker
    
  1116. 
    
  1117.     def view_on_site(self, obj):
    
  1118.         return "/worker_inline/%s/%s/" % (obj.surname, obj.name)
    
  1119. 
    
  1120. 
    
  1121. class RestaurantAdmin(admin.ModelAdmin):
    
  1122.     inlines = [WorkerInlineAdmin]
    
  1123.     view_on_site = False
    
  1124. 
    
  1125.     def get_changeform_initial_data(self, request):
    
  1126.         return {"name": "overridden_value"}
    
  1127. 
    
  1128. 
    
  1129. class FunkyTagAdmin(admin.ModelAdmin):
    
  1130.     list_display = ("name", "content_object")
    
  1131. 
    
  1132. 
    
  1133. class InlineReferenceInline(admin.TabularInline):
    
  1134.     model = InlineReference
    
  1135. 
    
  1136. 
    
  1137. class InlineRefererAdmin(admin.ModelAdmin):
    
  1138.     inlines = [InlineReferenceInline]
    
  1139. 
    
  1140. 
    
  1141. class PlotReadonlyAdmin(admin.ModelAdmin):
    
  1142.     readonly_fields = ("plotdetails",)
    
  1143. 
    
  1144. 
    
  1145. class GetFormsetsArgumentCheckingAdmin(admin.ModelAdmin):
    
  1146.     fields = ["name"]
    
  1147. 
    
  1148.     def add_view(self, request, *args, **kwargs):
    
  1149.         request.is_add_view = True
    
  1150.         return super().add_view(request, *args, **kwargs)
    
  1151. 
    
  1152.     def change_view(self, request, *args, **kwargs):
    
  1153.         request.is_add_view = False
    
  1154.         return super().change_view(request, *args, **kwargs)
    
  1155. 
    
  1156.     def get_formsets_with_inlines(self, request, obj=None):
    
  1157.         if request.is_add_view and obj is not None:
    
  1158.             raise Exception(
    
  1159.                 "'obj' passed to get_formsets_with_inlines wasn't None during add_view"
    
  1160.             )
    
  1161.         if not request.is_add_view and obj is None:
    
  1162.             raise Exception(
    
  1163.                 "'obj' passed to get_formsets_with_inlines was None during change_view"
    
  1164.             )
    
  1165.         return super().get_formsets_with_inlines(request, obj)
    
  1166. 
    
  1167. 
    
  1168. class CountryAdmin(admin.ModelAdmin):
    
  1169.     search_fields = ["name"]
    
  1170. 
    
  1171. 
    
  1172. class TravelerAdmin(admin.ModelAdmin):
    
  1173.     autocomplete_fields = ["living_country"]
    
  1174. 
    
  1175. 
    
  1176. site = admin.AdminSite(name="admin")
    
  1177. site.site_url = "/my-site-url/"
    
  1178. site.register(Article, ArticleAdmin)
    
  1179. site.register(CustomArticle, CustomArticleAdmin)
    
  1180. site.register(
    
  1181.     Section,
    
  1182.     save_as=True,
    
  1183.     inlines=[ArticleInline],
    
  1184.     readonly_fields=["name_property"],
    
  1185.     search_fields=["name"],
    
  1186. )
    
  1187. site.register(ModelWithStringPrimaryKey)
    
  1188. site.register(Color)
    
  1189. site.register(Thing, ThingAdmin)
    
  1190. site.register(Actor)
    
  1191. site.register(Inquisition, InquisitionAdmin)
    
  1192. site.register(Sketch, SketchAdmin)
    
  1193. site.register(Person, PersonAdmin)
    
  1194. site.register(Persona, PersonaAdmin)
    
  1195. site.register(Subscriber, SubscriberAdmin)
    
  1196. site.register(ExternalSubscriber, ExternalSubscriberAdmin)
    
  1197. site.register(OldSubscriber, OldSubscriberAdmin)
    
  1198. site.register(Podcast, PodcastAdmin)
    
  1199. site.register(Vodcast, VodcastAdmin)
    
  1200. site.register(Parent, ParentAdmin)
    
  1201. site.register(EmptyModel, EmptyModelAdmin)
    
  1202. site.register(Fabric, FabricAdmin)
    
  1203. site.register(Gallery, GalleryAdmin)
    
  1204. site.register(Picture, PictureAdmin)
    
  1205. site.register(Language, LanguageAdmin)
    
  1206. site.register(Recommendation, RecommendationAdmin)
    
  1207. site.register(Recommender)
    
  1208. site.register(Collector, CollectorAdmin)
    
  1209. site.register(Category, CategoryAdmin)
    
  1210. site.register(Post, PostAdmin)
    
  1211. site.register(FieldOverridePost, FieldOverridePostAdmin)
    
  1212. site.register(Gadget, GadgetAdmin)
    
  1213. site.register(Villain)
    
  1214. site.register(SuperVillain)
    
  1215. site.register(Plot)
    
  1216. site.register(PlotDetails)
    
  1217. site.register(PlotProxy, PlotReadonlyAdmin)
    
  1218. site.register(Bookmark)
    
  1219. site.register(CyclicOne)
    
  1220. site.register(CyclicTwo)
    
  1221. site.register(WorkHour, WorkHourAdmin)
    
  1222. site.register(Reservation)
    
  1223. site.register(FoodDelivery, FoodDeliveryAdmin)
    
  1224. site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin)
    
  1225. site.register(Paper, PaperAdmin)
    
  1226. site.register(CoverLetter, CoverLetterAdmin)
    
  1227. site.register(ShortMessage, ShortMessageAdmin)
    
  1228. site.register(Telegram, TelegramAdmin)
    
  1229. site.register(Story, StoryAdmin)
    
  1230. site.register(OtherStory, OtherStoryAdmin)
    
  1231. site.register(Report, ReportAdmin)
    
  1232. site.register(MainPrepopulated, MainPrepopulatedAdmin)
    
  1233. site.register(UnorderedObject, UnorderedObjectAdmin)
    
  1234. site.register(UndeletableObject, UndeletableObjectAdmin)
    
  1235. site.register(UnchangeableObject, UnchangeableObjectAdmin)
    
  1236. site.register(State, StateAdmin)
    
  1237. site.register(City, CityAdmin)
    
  1238. site.register(Restaurant, RestaurantAdmin)
    
  1239. site.register(Worker, WorkerAdmin)
    
  1240. site.register(FunkyTag, FunkyTagAdmin)
    
  1241. site.register(ReferencedByParent)
    
  1242. site.register(ChildOfReferer)
    
  1243. site.register(ReferencedByInline)
    
  1244. site.register(InlineReferer, InlineRefererAdmin)
    
  1245. site.register(ReferencedByGenRel)
    
  1246. site.register(GenRelReference)
    
  1247. site.register(ParentWithUUIDPK)
    
  1248. site.register(RelatedPrepopulated, search_fields=["name"])
    
  1249. site.register(RelatedWithUUIDPKModel)
    
  1250. site.register(ReadOnlyRelatedField, ReadOnlyRelatedFieldAdmin)
    
  1251. 
    
  1252. # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
    
  1253. # That way we cover all four cases:
    
  1254. #     related ForeignKey object registered in admin
    
  1255. #     related ForeignKey object not registered in admin
    
  1256. #     related OneToOne object registered in admin
    
  1257. #     related OneToOne object not registered in admin
    
  1258. # when deleting Book so as exercise all four paths through
    
  1259. # contrib.admin.utils's get_deleted_objects function.
    
  1260. site.register(Book, inlines=[ChapterInline])
    
  1261. site.register(Promo)
    
  1262. site.register(ChapterXtra1, ChapterXtra1Admin)
    
  1263. site.register(Pizza, PizzaAdmin)
    
  1264. site.register(ReadOnlyPizza, ReadOnlyPizzaAdmin)
    
  1265. site.register(ReadablePizza)
    
  1266. site.register(Topping, ToppingAdmin)
    
  1267. site.register(Album, AlbumAdmin)
    
  1268. site.register(Song)
    
  1269. site.register(Question, QuestionAdmin)
    
  1270. site.register(Answer, AnswerAdmin, date_hierarchy="question__posted")
    
  1271. site.register(Answer2, date_hierarchy="question__expires")
    
  1272. site.register(PrePopulatedPost, PrePopulatedPostAdmin)
    
  1273. site.register(ComplexSortedPerson, ComplexSortedPersonAdmin)
    
  1274. site.register(FilteredManager, CustomManagerAdmin)
    
  1275. site.register(PluggableSearchPerson, PluggableSearchPersonAdmin)
    
  1276. site.register(PrePopulatedPostLargeSlug, PrePopulatedPostLargeSlugAdmin)
    
  1277. site.register(AdminOrderedField, AdminOrderedFieldAdmin)
    
  1278. site.register(AdminOrderedModelMethod, AdminOrderedModelMethodAdmin)
    
  1279. site.register(AdminOrderedAdminMethod, AdminOrderedAdminMethodAdmin)
    
  1280. site.register(AdminOrderedCallable, AdminOrderedCallableAdmin)
    
  1281. site.register(Color2, CustomTemplateFilterColorAdmin)
    
  1282. site.register(Simple, AttributeErrorRaisingAdmin)
    
  1283. site.register(UserMessenger, MessageTestingAdmin)
    
  1284. site.register(Choice, ChoiceList)
    
  1285. site.register(ParentWithDependentChildren, ParentWithDependentChildrenAdmin)
    
  1286. site.register(EmptyModelHidden, EmptyModelHiddenAdmin)
    
  1287. site.register(EmptyModelVisible, EmptyModelVisibleAdmin)
    
  1288. site.register(EmptyModelMixin, EmptyModelMixinAdmin)
    
  1289. site.register(StumpJoke)
    
  1290. site.register(Recipe)
    
  1291. site.register(Ingredient)
    
  1292. site.register(NotReferenced)
    
  1293. site.register(ExplicitlyProvidedPK, GetFormsetsArgumentCheckingAdmin)
    
  1294. site.register(ImplicitlyGeneratedPK, GetFormsetsArgumentCheckingAdmin)
    
  1295. site.register(UserProxy)
    
  1296. site.register(Box)
    
  1297. site.register(Country, CountryAdmin)
    
  1298. site.register(Traveler, TravelerAdmin)
    
  1299. 
    
  1300. # Register core models we need in our tests
    
  1301. site.register(User, UserAdmin)
    
  1302. site.register(Group, GroupAdmin)
    
  1303. 
    
  1304. # Used to test URL namespaces
    
  1305. site2 = admin.AdminSite(name="namespaced_admin")
    
  1306. site2.register(User, UserAdmin)
    
  1307. site2.register(Group, GroupAdmin)
    
  1308. site2.register(ParentWithUUIDPK)
    
  1309. site2.register(
    
  1310.     RelatedWithUUIDPKModel,
    
  1311.     list_display=["pk", "parent"],
    
  1312.     list_editable=["parent"],
    
  1313.     raw_id_fields=["parent"],
    
  1314. )
    
  1315. site2.register(Person, save_as_continue=False)
    
  1316. site2.register(ReadOnlyRelatedField, ReadOnlyRelatedFieldAdmin)
    
  1317. site2.register(Language)
    
  1318. 
    
  1319. site7 = admin.AdminSite(name="admin7")
    
  1320. site7.register(Article, ArticleAdmin2)
    
  1321. site7.register(Section)
    
  1322. site7.register(PrePopulatedPost, PrePopulatedPostReadOnlyAdmin)
    
  1323. site7.register(
    
  1324.     Pizza,
    
  1325.     filter_horizontal=["toppings"],
    
  1326.     fieldsets=(
    
  1327.         (
    
  1328.             "Collapsible",
    
  1329.             {
    
  1330.                 "classes": ["collapse"],
    
  1331.                 "fields": ["toppings"],
    
  1332.             },
    
  1333.         ),
    
  1334.     ),
    
  1335. )
    
  1336. site7.register(
    
  1337.     Question,
    
  1338.     filter_horizontal=["related_questions"],
    
  1339.     fieldsets=(
    
  1340.         (
    
  1341.             "Not collapsible",
    
  1342.             {
    
  1343.                 "fields": ["related_questions"],
    
  1344.             },
    
  1345.         ),
    
  1346.     ),
    
  1347. )
    
  1348. 
    
  1349. 
    
  1350. # Used to test ModelAdmin.sortable_by and get_sortable_by().
    
  1351. class ArticleAdmin6(admin.ModelAdmin):
    
  1352.     list_display = (
    
  1353.         "content",
    
  1354.         "date",
    
  1355.         callable_year,
    
  1356.         "model_year",
    
  1357.         "modeladmin_year",
    
  1358.         "model_year_reversed",
    
  1359.         "section",
    
  1360.     )
    
  1361.     sortable_by = ("date", callable_year)
    
  1362. 
    
  1363.     @admin.display(ordering="date")
    
  1364.     def modeladmin_year(self, obj):
    
  1365.         return obj.date.year
    
  1366. 
    
  1367. 
    
  1368. class ActorAdmin6(admin.ModelAdmin):
    
  1369.     list_display = ("name", "age")
    
  1370.     sortable_by = ("name",)
    
  1371. 
    
  1372.     def get_sortable_by(self, request):
    
  1373.         return ("age",)
    
  1374. 
    
  1375. 
    
  1376. class ChapterAdmin6(admin.ModelAdmin):
    
  1377.     list_display = ("title", "book")
    
  1378.     sortable_by = ()
    
  1379. 
    
  1380. 
    
  1381. class ColorAdmin6(admin.ModelAdmin):
    
  1382.     list_display = ("value",)
    
  1383. 
    
  1384.     def get_sortable_by(self, request):
    
  1385.         return ()
    
  1386. 
    
  1387. 
    
  1388. site6 = admin.AdminSite(name="admin6")
    
  1389. site6.register(Article, ArticleAdmin6)
    
  1390. site6.register(Actor, ActorAdmin6)
    
  1391. site6.register(Chapter, ChapterAdmin6)
    
  1392. site6.register(Color, ColorAdmin6)
    
  1393. 
    
  1394. 
    
  1395. class ArticleAdmin9(admin.ModelAdmin):
    
  1396.     def has_change_permission(self, request, obj=None):
    
  1397.         # Simulate that the user can't change a specific object.
    
  1398.         return obj is None
    
  1399. 
    
  1400. 
    
  1401. class ActorAdmin9(admin.ModelAdmin):
    
  1402.     def get_urls(self):
    
  1403.         # Opt-out of append slash for single model.
    
  1404.         urls = super().get_urls()
    
  1405.         for pattern in urls:
    
  1406.             pattern.callback = no_append_slash(pattern.callback)
    
  1407.         return urls
    
  1408. 
    
  1409. 
    
  1410. site9 = admin.AdminSite(name="admin9")
    
  1411. site9.register(Article, ArticleAdmin9)
    
  1412. site9.register(Actor, ActorAdmin9)
    
  1413. 
    
  1414. site10 = admin.AdminSite(name="admin10")
    
  1415. site10.final_catch_all_view = False
    
  1416. site10.register(Article, ArticleAdminWithExtraUrl)