1. from django import forms
    
  2. from django.contrib import admin
    
  3. from django.core.exceptions import ValidationError
    
  4. from django.db import models
    
  5. 
    
  6. from .models import (
    
  7.     Author,
    
  8.     BinaryTree,
    
  9.     CapoFamiglia,
    
  10.     Chapter,
    
  11.     Child,
    
  12.     ChildModel1,
    
  13.     ChildModel2,
    
  14.     Class,
    
  15.     Consigliere,
    
  16.     Course,
    
  17.     CourseProxy,
    
  18.     CourseProxy1,
    
  19.     CourseProxy2,
    
  20.     EditablePKBook,
    
  21.     ExtraTerrestrial,
    
  22.     Fashionista,
    
  23.     FootNote,
    
  24.     Holder,
    
  25.     Holder2,
    
  26.     Holder3,
    
  27.     Holder4,
    
  28.     Holder5,
    
  29.     Inner,
    
  30.     Inner2,
    
  31.     Inner3,
    
  32.     Inner4Stacked,
    
  33.     Inner4Tabular,
    
  34.     Inner5Stacked,
    
  35.     Inner5Tabular,
    
  36.     NonAutoPKBook,
    
  37.     NonAutoPKBookChild,
    
  38.     Novel,
    
  39.     NovelReadonlyChapter,
    
  40.     OutfitItem,
    
  41.     ParentModelWithCustomPk,
    
  42.     Person,
    
  43.     Poll,
    
  44.     Profile,
    
  45.     ProfileCollection,
    
  46.     Question,
    
  47.     ReadOnlyInline,
    
  48.     ShoppingWeakness,
    
  49.     ShowInlineChild,
    
  50.     ShowInlineParent,
    
  51.     Sighting,
    
  52.     SomeChildModel,
    
  53.     SomeParentModel,
    
  54.     SottoCapo,
    
  55.     Teacher,
    
  56.     Title,
    
  57.     TitleCollection,
    
  58. )
    
  59. 
    
  60. site = admin.AdminSite(name="admin")
    
  61. 
    
  62. 
    
  63. class BookInline(admin.TabularInline):
    
  64.     model = Author.books.through
    
  65. 
    
  66. 
    
  67. class NonAutoPKBookTabularInline(admin.TabularInline):
    
  68.     model = NonAutoPKBook
    
  69.     classes = ("collapse",)
    
  70. 
    
  71. 
    
  72. class NonAutoPKBookChildTabularInline(admin.TabularInline):
    
  73.     model = NonAutoPKBookChild
    
  74.     classes = ("collapse",)
    
  75. 
    
  76. 
    
  77. class NonAutoPKBookStackedInline(admin.StackedInline):
    
  78.     model = NonAutoPKBook
    
  79.     classes = ("collapse",)
    
  80. 
    
  81. 
    
  82. class EditablePKBookTabularInline(admin.TabularInline):
    
  83.     model = EditablePKBook
    
  84. 
    
  85. 
    
  86. class EditablePKBookStackedInline(admin.StackedInline):
    
  87.     model = EditablePKBook
    
  88. 
    
  89. 
    
  90. class AuthorAdmin(admin.ModelAdmin):
    
  91.     inlines = [
    
  92.         BookInline,
    
  93.         NonAutoPKBookTabularInline,
    
  94.         NonAutoPKBookStackedInline,
    
  95.         EditablePKBookTabularInline,
    
  96.         EditablePKBookStackedInline,
    
  97.         NonAutoPKBookChildTabularInline,
    
  98.     ]
    
  99. 
    
  100. 
    
  101. class InnerInline(admin.StackedInline):
    
  102.     model = Inner
    
  103.     can_delete = False
    
  104.     readonly_fields = ("readonly",)  # For bug #13174 tests.
    
  105. 
    
  106. 
    
  107. class HolderAdmin(admin.ModelAdmin):
    
  108.     class Media:
    
  109.         js = ("my_awesome_admin_scripts.js",)
    
  110. 
    
  111. 
    
  112. class ReadOnlyInlineInline(admin.TabularInline):
    
  113.     model = ReadOnlyInline
    
  114.     readonly_fields = ["name"]
    
  115. 
    
  116. 
    
  117. class InnerInline2(admin.StackedInline):
    
  118.     model = Inner2
    
  119. 
    
  120.     class Media:
    
  121.         js = ("my_awesome_inline_scripts.js",)
    
  122. 
    
  123. 
    
  124. class InnerInline2Tabular(admin.TabularInline):
    
  125.     model = Inner2
    
  126. 
    
  127. 
    
  128. class CustomNumberWidget(forms.NumberInput):
    
  129.     class Media:
    
  130.         js = ("custom_number.js",)
    
  131. 
    
  132. 
    
  133. class InnerInline3(admin.StackedInline):
    
  134.     model = Inner3
    
  135.     formfield_overrides = {
    
  136.         models.IntegerField: {"widget": CustomNumberWidget},
    
  137.     }
    
  138. 
    
  139.     class Media:
    
  140.         js = ("my_awesome_inline_scripts.js",)
    
  141. 
    
  142. 
    
  143. class TitleForm(forms.ModelForm):
    
  144.     title1 = forms.CharField(max_length=100)
    
  145. 
    
  146.     def clean(self):
    
  147.         cleaned_data = self.cleaned_data
    
  148.         title1 = cleaned_data.get("title1")
    
  149.         title2 = cleaned_data.get("title2")
    
  150.         if title1 != title2:
    
  151.             raise ValidationError("The two titles must be the same")
    
  152.         return cleaned_data
    
  153. 
    
  154. 
    
  155. class TitleInline(admin.TabularInline):
    
  156.     model = Title
    
  157.     form = TitleForm
    
  158.     extra = 1
    
  159. 
    
  160. 
    
  161. class Inner4StackedInline(admin.StackedInline):
    
  162.     model = Inner4Stacked
    
  163.     show_change_link = True
    
  164. 
    
  165. 
    
  166. class Inner4TabularInline(admin.TabularInline):
    
  167.     model = Inner4Tabular
    
  168.     show_change_link = True
    
  169. 
    
  170. 
    
  171. class Holder4Admin(admin.ModelAdmin):
    
  172.     inlines = [Inner4StackedInline, Inner4TabularInline]
    
  173. 
    
  174. 
    
  175. class Inner5StackedInline(admin.StackedInline):
    
  176.     model = Inner5Stacked
    
  177.     classes = ("collapse",)
    
  178. 
    
  179. 
    
  180. class Inner5TabularInline(admin.TabularInline):
    
  181.     model = Inner5Tabular
    
  182.     classes = ("collapse",)
    
  183. 
    
  184. 
    
  185. class Holder5Admin(admin.ModelAdmin):
    
  186.     inlines = [Inner5StackedInline, Inner5TabularInline]
    
  187. 
    
  188. 
    
  189. class InlineWeakness(admin.TabularInline):
    
  190.     model = ShoppingWeakness
    
  191.     extra = 1
    
  192. 
    
  193. 
    
  194. class WeaknessForm(forms.ModelForm):
    
  195.     extra_field = forms.CharField()
    
  196. 
    
  197.     class Meta:
    
  198.         model = ShoppingWeakness
    
  199.         fields = "__all__"
    
  200. 
    
  201. 
    
  202. class WeaknessInlineCustomForm(admin.TabularInline):
    
  203.     model = ShoppingWeakness
    
  204.     form = WeaknessForm
    
  205. 
    
  206. 
    
  207. class FootNoteForm(forms.ModelForm):
    
  208.     extra_field = forms.CharField()
    
  209. 
    
  210.     class Meta:
    
  211.         model = FootNote
    
  212.         fields = "__all__"
    
  213. 
    
  214. 
    
  215. class FootNoteNonEditableInlineCustomForm(admin.TabularInline):
    
  216.     model = FootNote
    
  217.     form = FootNoteForm
    
  218. 
    
  219.     def has_change_permission(self, request, obj=None):
    
  220.         return False
    
  221. 
    
  222. 
    
  223. class QuestionInline(admin.TabularInline):
    
  224.     model = Question
    
  225.     readonly_fields = ["call_me"]
    
  226. 
    
  227.     def call_me(self, obj):
    
  228.         return "Callable in QuestionInline"
    
  229. 
    
  230. 
    
  231. class PollAdmin(admin.ModelAdmin):
    
  232.     inlines = [QuestionInline]
    
  233. 
    
  234.     def call_me(self, obj):
    
  235.         return "Callable in PollAdmin"
    
  236. 
    
  237. 
    
  238. class ChapterInline(admin.TabularInline):
    
  239.     model = Chapter
    
  240.     readonly_fields = ["call_me"]
    
  241. 
    
  242.     def call_me(self, obj):
    
  243.         return "Callable in ChapterInline"
    
  244. 
    
  245. 
    
  246. class NovelAdmin(admin.ModelAdmin):
    
  247.     inlines = [ChapterInline]
    
  248. 
    
  249. 
    
  250. class ReadOnlyChapterInline(admin.TabularInline):
    
  251.     model = Chapter
    
  252. 
    
  253.     def has_change_permission(self, request, obj=None):
    
  254.         return False
    
  255. 
    
  256. 
    
  257. class NovelReadonlyChapterAdmin(admin.ModelAdmin):
    
  258.     inlines = [ReadOnlyChapterInline]
    
  259. 
    
  260. 
    
  261. class ConsigliereInline(admin.TabularInline):
    
  262.     model = Consigliere
    
  263. 
    
  264. 
    
  265. class SottoCapoInline(admin.TabularInline):
    
  266.     model = SottoCapo
    
  267. 
    
  268. 
    
  269. class ProfileInline(admin.TabularInline):
    
  270.     model = Profile
    
  271.     extra = 1
    
  272. 
    
  273. 
    
  274. # admin for #18433
    
  275. class ChildModel1Inline(admin.TabularInline):
    
  276.     model = ChildModel1
    
  277. 
    
  278. 
    
  279. class ChildModel2Inline(admin.StackedInline):
    
  280.     model = ChildModel2
    
  281. 
    
  282. 
    
  283. # admin for #19425 and #18388
    
  284. class BinaryTreeAdmin(admin.TabularInline):
    
  285.     model = BinaryTree
    
  286. 
    
  287.     def get_extra(self, request, obj=None, **kwargs):
    
  288.         extra = 2
    
  289.         if obj:
    
  290.             return extra - obj.binarytree_set.count()
    
  291.         return extra
    
  292. 
    
  293.     def get_max_num(self, request, obj=None, **kwargs):
    
  294.         max_num = 3
    
  295.         if obj:
    
  296.             return max_num - obj.binarytree_set.count()
    
  297.         return max_num
    
  298. 
    
  299. 
    
  300. # admin for #19524
    
  301. class SightingInline(admin.TabularInline):
    
  302.     model = Sighting
    
  303. 
    
  304. 
    
  305. # admin and form for #18263
    
  306. class SomeChildModelForm(forms.ModelForm):
    
  307.     class Meta:
    
  308.         fields = "__all__"
    
  309.         model = SomeChildModel
    
  310.         widgets = {
    
  311.             "position": forms.HiddenInput,
    
  312.         }
    
  313.         labels = {"readonly_field": "Label from ModelForm.Meta"}
    
  314.         help_texts = {"readonly_field": "Help text from ModelForm.Meta"}
    
  315. 
    
  316.     def __init__(self, *args, **kwargs):
    
  317.         super().__init__(*args, **kwargs)
    
  318.         self.fields["name"].label = "new label"
    
  319. 
    
  320. 
    
  321. class SomeChildModelInline(admin.TabularInline):
    
  322.     model = SomeChildModel
    
  323.     form = SomeChildModelForm
    
  324.     readonly_fields = ("readonly_field",)
    
  325. 
    
  326. 
    
  327. class StudentInline(admin.StackedInline):
    
  328.     model = Child
    
  329.     extra = 1
    
  330.     fieldsets = [
    
  331.         ("Name", {"fields": ("name",), "classes": ("collapse",)}),
    
  332.     ]
    
  333. 
    
  334. 
    
  335. class TeacherAdmin(admin.ModelAdmin):
    
  336.     inlines = [StudentInline]
    
  337. 
    
  338. 
    
  339. class AuthorTabularInline(admin.TabularInline):
    
  340.     model = Author
    
  341. 
    
  342. 
    
  343. class FashonistaStackedInline(admin.StackedInline):
    
  344.     model = Fashionista
    
  345. 
    
  346. 
    
  347. # Admin for #30231
    
  348. class ClassStackedHorizontal(admin.StackedInline):
    
  349.     model = Class
    
  350.     extra = 1
    
  351.     filter_horizontal = ["person"]
    
  352. 
    
  353. 
    
  354. class ClassAdminStackedHorizontal(admin.ModelAdmin):
    
  355.     inlines = [ClassStackedHorizontal]
    
  356. 
    
  357. 
    
  358. class ClassTabularHorizontal(admin.TabularInline):
    
  359.     model = Class
    
  360.     extra = 1
    
  361.     filter_horizontal = ["person"]
    
  362. 
    
  363. 
    
  364. class ClassAdminTabularHorizontal(admin.ModelAdmin):
    
  365.     inlines = [ClassTabularHorizontal]
    
  366. 
    
  367. 
    
  368. class ClassTabularVertical(admin.TabularInline):
    
  369.     model = Class
    
  370.     extra = 1
    
  371.     filter_vertical = ["person"]
    
  372. 
    
  373. 
    
  374. class ClassAdminTabularVertical(admin.ModelAdmin):
    
  375.     inlines = [ClassTabularVertical]
    
  376. 
    
  377. 
    
  378. class ClassStackedVertical(admin.StackedInline):
    
  379.     model = Class
    
  380.     extra = 1
    
  381.     filter_vertical = ["person"]
    
  382. 
    
  383. 
    
  384. class ClassAdminStackedVertical(admin.ModelAdmin):
    
  385.     inlines = [ClassStackedVertical]
    
  386. 
    
  387. 
    
  388. class ChildHiddenFieldForm(forms.ModelForm):
    
  389.     class Meta:
    
  390.         model = SomeChildModel
    
  391.         fields = ["name", "position", "parent"]
    
  392.         widgets = {"position": forms.HiddenInput}
    
  393. 
    
  394.     def _post_clean(self):
    
  395.         super()._post_clean()
    
  396.         if self.instance is not None and self.instance.position == 1:
    
  397.             self.add_error(None, ValidationError("A non-field error"))
    
  398. 
    
  399. 
    
  400. class ChildHiddenFieldTabularInline(admin.TabularInline):
    
  401.     model = SomeChildModel
    
  402.     form = ChildHiddenFieldForm
    
  403. 
    
  404. 
    
  405. class ChildHiddenFieldInFieldsGroupStackedInline(admin.StackedInline):
    
  406.     model = SomeChildModel
    
  407.     form = ChildHiddenFieldForm
    
  408.     fields = [("name", "position")]
    
  409. 
    
  410. 
    
  411. class ChildHiddenFieldOnSingleLineStackedInline(admin.StackedInline):
    
  412.     model = SomeChildModel
    
  413.     form = ChildHiddenFieldForm
    
  414.     fields = ("name", "position")
    
  415. 
    
  416. 
    
  417. class ShowInlineChildInline(admin.StackedInline):
    
  418.     model = ShowInlineChild
    
  419. 
    
  420. 
    
  421. class ShowInlineParentAdmin(admin.ModelAdmin):
    
  422.     def get_inlines(self, request, obj):
    
  423.         if obj is not None and obj.show_inlines:
    
  424.             return [ShowInlineChildInline]
    
  425.         return []
    
  426. 
    
  427. 
    
  428. site.register(TitleCollection, inlines=[TitleInline])
    
  429. # Test bug #12561 and #12778
    
  430. # only ModelAdmin media
    
  431. site.register(Holder, HolderAdmin, inlines=[InnerInline])
    
  432. # ModelAdmin and Inline media
    
  433. site.register(Holder2, HolderAdmin, inlines=[InnerInline2, InnerInline2Tabular])
    
  434. # only Inline media
    
  435. site.register(Holder3, inlines=[InnerInline3])
    
  436. 
    
  437. site.register(Poll, PollAdmin)
    
  438. site.register(Novel, NovelAdmin)
    
  439. site.register(NovelReadonlyChapter, NovelReadonlyChapterAdmin)
    
  440. site.register(Fashionista, inlines=[InlineWeakness])
    
  441. site.register(Holder4, Holder4Admin)
    
  442. site.register(Holder5, Holder5Admin)
    
  443. site.register(Author, AuthorAdmin)
    
  444. site.register(
    
  445.     CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline, ReadOnlyInlineInline]
    
  446. )
    
  447. site.register(ProfileCollection, inlines=[ProfileInline])
    
  448. site.register(ParentModelWithCustomPk, inlines=[ChildModel1Inline, ChildModel2Inline])
    
  449. site.register(BinaryTree, inlines=[BinaryTreeAdmin])
    
  450. site.register(ExtraTerrestrial, inlines=[SightingInline])
    
  451. site.register(SomeParentModel, inlines=[SomeChildModelInline])
    
  452. site.register([Question, Inner4Stacked, Inner4Tabular])
    
  453. site.register(Teacher, TeacherAdmin)
    
  454. site.register(Chapter, inlines=[FootNoteNonEditableInlineCustomForm])
    
  455. site.register(OutfitItem, inlines=[WeaknessInlineCustomForm])
    
  456. site.register(Person, inlines=[AuthorTabularInline, FashonistaStackedInline])
    
  457. site.register(Course, ClassAdminStackedHorizontal)
    
  458. site.register(CourseProxy, ClassAdminStackedVertical)
    
  459. site.register(CourseProxy1, ClassAdminTabularVertical)
    
  460. site.register(CourseProxy2, ClassAdminTabularHorizontal)
    
  461. site.register(ShowInlineParent, ShowInlineParentAdmin)
    
  462. # Used to test hidden fields in tabular and stacked inlines.
    
  463. site2 = admin.AdminSite(name="tabular_inline_hidden_field_admin")
    
  464. site2.register(SomeParentModel, inlines=[ChildHiddenFieldTabularInline])
    
  465. site3 = admin.AdminSite(name="stacked_inline_hidden_field_in_group_admin")
    
  466. site3.register(SomeParentModel, inlines=[ChildHiddenFieldInFieldsGroupStackedInline])
    
  467. site4 = admin.AdminSite(name="stacked_inline_hidden_field_on_single_line_admin")
    
  468. site4.register(SomeParentModel, inlines=[ChildHiddenFieldOnSingleLineStackedInline])