0
# Admin Interface and Panels
1
2
Customizable admin interface with panel system for organizing edit forms, menu configuration, and extensible admin UI components. Wagtail's admin interface provides a user-friendly content management experience with powerful customization capabilities.
3
4
## Capabilities
5
6
### Panel System
7
8
Panel classes for organizing edit forms and controlling the admin interface layout.
9
10
```python { .api }
11
class Panel:
12
"""
13
Base panel class for edit forms.
14
15
All panel types inherit from this base class.
16
"""
17
def __init__(self, heading='', classname='', help_text='', **kwargs):
18
"""
19
Initialize panel with display options.
20
21
Parameters:
22
heading (str): Panel heading text
23
classname (str): CSS class for styling
24
help_text (str): Help text displayed to users
25
"""
26
27
class FieldPanel(Panel):
28
"""
29
Panel for editing a single model field.
30
31
The most commonly used panel type for basic field editing.
32
"""
33
def __init__(self, field_name, widget=None, heading='', classname='', help_text='', **kwargs):
34
"""
35
Initialize field panel.
36
37
Parameters:
38
field_name (str): Name of the model field to edit
39
widget (Widget): Custom form widget to use
40
heading (str): Custom heading for the field
41
classname (str): CSS class for styling
42
help_text (str): Help text for the field
43
"""
44
45
class MultiFieldPanel(Panel):
46
"""
47
Panel that groups multiple fields together under a common heading.
48
"""
49
def __init__(self, children, heading='', classname='', help_text='', **kwargs):
50
"""
51
Initialize multi-field panel.
52
53
Parameters:
54
children (list): List of child panels to group
55
heading (str): Heading for the group
56
classname (str): CSS class for styling
57
help_text (str): Help text for the group
58
"""
59
60
class TitleFieldPanel(FieldPanel):
61
"""
62
Special panel for title fields that enables automatic slug generation.
63
"""
64
def __init__(self, field_name, **kwargs):
65
"""Initialize title field panel with slug generation."""
66
67
class PublishingPanel(Panel):
68
"""
69
Panel that displays publishing workflow controls and status.
70
"""
71
def __init__(self, **kwargs):
72
"""Initialize publishing panel."""
73
74
class CommentPanel(Panel):
75
"""
76
Panel that displays the comments interface for collaborative editing.
77
"""
78
def __init__(self, **kwargs):
79
"""Initialize comment panel."""
80
81
class InlinePanel(Panel):
82
"""
83
Panel for editing related objects inline within the parent object's form.
84
"""
85
def __init__(self, relation_name, panels=None, heading='', label='', min_num=None, max_num=None, classname='', help_text='', **kwargs):
86
"""
87
Initialize inline panel.
88
89
Parameters:
90
relation_name (str): Name of the foreign key relation
91
panels (list): List of panels for editing related objects
92
heading (str): Panel heading
93
label (str): Label for individual items
94
min_num (int): Minimum number of items required
95
max_num (int): Maximum number of items allowed
96
classname (str): CSS class for styling
97
help_text (str): Help text for the panel
98
"""
99
100
class ObjectList(Panel):
101
"""
102
Container for organizing multiple panels into logical groups.
103
"""
104
def __init__(self, children, heading='', classname='', **kwargs):
105
"""
106
Initialize object list.
107
108
Parameters:
109
children (list): List of child panels
110
heading (str): Group heading
111
classname (str): CSS class for styling
112
"""
113
114
class TabbedInterface(Panel):
115
"""
116
Creates a tabbed interface with multiple panel groups.
117
"""
118
def __init__(self, children, **kwargs):
119
"""
120
Initialize tabbed interface.
121
122
Parameters:
123
children (list): List of ObjectList panels for each tab
124
"""
125
```
126
127
### Menu System
128
129
Classes for customizing the admin navigation menu.
130
131
```python { .api }
132
class MenuItem:
133
"""
134
Individual menu item in the admin interface.
135
"""
136
def __init__(self, label, url, name=None, icon_name=None, attrs=None, order=1000, **kwargs):
137
"""
138
Initialize menu item.
139
140
Parameters:
141
label (str): Display text for the menu item
142
url (str): URL the menu item links to
143
name (str): Unique identifier for the menu item
144
icon_name (str): Icon to display with the menu item
145
attrs (dict): Additional HTML attributes
146
order (int): Sort order for menu positioning
147
"""
148
149
class Menu:
150
"""
151
Container for organizing menu items.
152
"""
153
def __init__(self, register_hook_name=None, construct_hook_name=None):
154
"""Initialize menu container."""
155
156
def register_menu_item(self, menu_item):
157
"""Register a menu item with this menu."""
158
159
class SubmenuMenuItem(MenuItem):
160
"""
161
Menu item that contains nested menu items.
162
"""
163
def __init__(self, label, menu, **kwargs):
164
"""
165
Initialize submenu item.
166
167
Parameters:
168
label (str): Display text for the submenu
169
menu (Menu): Menu object containing child items
170
"""
171
```
172
173
### ViewSets
174
175
ViewSet classes for creating complete admin interfaces for models.
176
177
```python { .api }
178
class ViewSet:
179
"""
180
Base viewset class providing a collection of views for a specific functionality.
181
"""
182
name: str
183
url_prefix: str
184
185
def get_urlpatterns(self):
186
"""Get URL patterns for this viewset."""
187
188
def get_admin_menu_item(self):
189
"""Get menu item for admin navigation."""
190
191
class ModelViewSet(ViewSet):
192
"""
193
Complete CRUD interface for Django models.
194
195
Provides list, create, edit, delete, and inspect views.
196
"""
197
model: Model
198
form_class: ModelForm
199
index_template_name: str
200
create_template_name: str
201
edit_template_name: str
202
delete_template_name: str
203
204
def __init__(self, name, model=None, **kwargs):
205
"""
206
Initialize model viewset.
207
208
Parameters:
209
name (str): Unique name for the viewset
210
model (Model): Django model to manage
211
"""
212
213
def get_queryset(self, request):
214
"""Get the queryset for listing objects."""
215
216
def get_form_class(self, for_update=False):
217
"""Get the form class for creating/editing objects."""
218
219
def get_edit_handler(self):
220
"""Get the edit handler (panel configuration) for forms."""
221
222
class PageViewSet(ModelViewSet):
223
"""
224
Specialized viewset for page models with additional page-specific functionality.
225
"""
226
template_name: str
227
228
def get_edit_handler(self):
229
"""Get edit handler with page-specific panels."""
230
```
231
232
### Admin Forms and Widgets
233
234
Form classes and widgets for the admin interface.
235
236
```python { .api }
237
class WagtailAdminModelForm(forms.ModelForm):
238
"""
239
Base form class for Wagtail admin forms.
240
241
Provides consistent styling and functionality.
242
"""
243
def __init__(self, *args, **kwargs):
244
"""Initialize admin form with Wagtail styling."""
245
246
class WagtailAdminPageForm(WagtailAdminModelForm):
247
"""
248
Form class specifically for page editing with additional page functionality.
249
"""
250
def clean(self):
251
"""Validate page-specific constraints."""
252
253
# Widget classes for form fields
254
class AdminAutoHeightTextInput(forms.Textarea):
255
"""Text input that automatically adjusts height."""
256
257
class AdminPageChooser(forms.Select):
258
"""Widget for choosing pages with search interface."""
259
260
class AdminImageChooser(forms.ClearableFileInput):
261
"""Widget for choosing images with preview."""
262
263
class AdminDocumentChooser(forms.ClearableFileInput):
264
"""Widget for choosing documents with metadata."""
265
266
class AdminTagWidget(forms.TextInput):
267
"""Widget for tag input with autocomplete."""
268
269
class AdminDateInput(forms.DateInput):
270
"""Date input widget with calendar picker."""
271
272
class AdminTimeInput(forms.TimeInput):
273
"""Time input widget with time picker."""
274
275
class AdminDateTimeInput(forms.DateTimeInput):
276
"""DateTime input widget with combined picker."""
277
```
278
279
### Permission System
280
281
Classes for managing admin permissions and access control.
282
283
```python { .api }
284
class PermissionHelper:
285
"""
286
Helper class for checking permissions in admin views.
287
"""
288
def __init__(self, model, inspect_view_enabled=False):
289
"""
290
Initialize permission helper.
291
292
Parameters:
293
model (Model): Model to check permissions for
294
inspect_view_enabled (bool): Whether inspect view is available
295
"""
296
297
def user_can_list(self, user):
298
"""Check if user can view the listing page."""
299
300
def user_can_create(self, user):
301
"""Check if user can create new objects."""
302
303
def user_can_inspect_obj(self, user, obj):
304
"""Check if user can inspect a specific object."""
305
306
def user_can_edit_obj(self, user, obj):
307
"""Check if user can edit a specific object."""
308
309
def user_can_delete_obj(self, user, obj):
310
"""Check if user can delete a specific object."""
311
312
class PagePermissionHelper(PermissionHelper):
313
"""
314
Permission helper specifically for page models with page-specific permissions.
315
"""
316
def user_can_edit_obj(self, user, obj):
317
"""Check page-specific edit permissions."""
318
```
319
320
## Usage Examples
321
322
### Customizing Page Edit Interface
323
324
```python
325
from wagtail.models import Page
326
from wagtail.fields import RichTextField, StreamField
327
from wagtail.admin.panels import (
328
FieldPanel, MultiFieldPanel, InlinePanel, TabbedInterface, ObjectList
329
)
330
from django.db import models
331
332
class BlogPage(Page):
333
"""Blog page with custom admin interface."""
334
date = models.DateField("Post date")
335
intro = models.CharField(max_length=250)
336
body = RichTextField(blank=True)
337
featured_image = models.ForeignKey(
338
'wagtailimages.Image',
339
null=True,
340
blank=True,
341
on_delete=models.SET_NULL,
342
related_name='+'
343
)
344
345
# Organize fields into logical groups
346
content_panels = Page.content_panels + [
347
MultiFieldPanel([
348
FieldPanel('date'),
349
FieldPanel('intro'),
350
], heading="Article Information"),
351
FieldPanel('body'),
352
FieldPanel('featured_image'),
353
]
354
355
# Create tabbed interface
356
edit_handler = TabbedInterface([
357
ObjectList(content_panels, heading='Content'),
358
ObjectList(Page.promote_panels, heading='Promote'),
359
ObjectList(Page.settings_panels, heading='Settings'),
360
])
361
```
362
363
### Creating Custom Admin Views
364
365
```python
366
from wagtail.admin.viewsets import ModelViewSet
367
from wagtail.admin.ui.tables import Column, DateColumn
368
from django.db import models
369
370
class EventPage(Page):
371
"""Event page model."""
372
event_date = models.DateField()
373
location = models.CharField(max_length=255)
374
capacity = models.IntegerField()
375
376
class EventPageViewSet(ModelViewSet):
377
"""Custom admin interface for events."""
378
model = EventPage
379
icon = 'date'
380
menu_label = 'Events'
381
menu_order = 200
382
add_to_settings_menu = False
383
384
# Customize the listing page
385
list_display = ['title', 'event_date', 'location', 'capacity', 'live']
386
list_filter = ['event_date', 'live']
387
search_fields = ['title', 'location']
388
389
# Custom columns for the listing
390
def get_columns(self):
391
return [
392
Column('title', label='Event Title', sort_key='title'),
393
DateColumn('event_date', label='Date', sort_key='event_date'),
394
Column('location', label='Location'),
395
Column('capacity', label='Capacity'),
396
]
397
398
# Register the viewset
399
event_viewset = EventPageViewSet('events')
400
```
401
402
### Adding Custom Menu Items
403
404
```python
405
from wagtail import hooks
406
from wagtail.admin.menu import MenuItem, Menu, SubmenuMenuItem
407
from django.urls import reverse, path
408
from django.http import HttpResponse
409
410
@hooks.register('register_admin_urls')
411
def register_admin_urls():
412
"""Register custom admin URLs."""
413
return [
414
path('reports/', my_reports_view, name='my_reports'),
415
path('tools/', my_tools_view, name='my_tools'),
416
]
417
418
def my_reports_view(request):
419
"""Custom reports view."""
420
return HttpResponse('<h1>Reports</h1>')
421
422
def my_tools_view(request):
423
"""Custom tools view."""
424
return HttpResponse('<h1>Tools</h1>')
425
426
@hooks.register('construct_main_menu')
427
def add_reports_menu_item(request, menu_items):
428
"""Add reports to main menu."""
429
menu_items.append(
430
MenuItem(
431
'Reports',
432
reverse('my_reports'),
433
icon_name='doc-full-inverse',
434
order=300
435
)
436
)
437
438
@hooks.register('construct_main_menu')
439
def add_tools_submenu(request, menu_items):
440
"""Add tools submenu."""
441
tools_menu = Menu()
442
tools_menu.register_menu_item(
443
MenuItem('Tool 1', reverse('my_tools'), icon_name='cog')
444
)
445
446
menu_items.append(
447
SubmenuMenuItem(
448
'Tools',
449
tools_menu,
450
icon_name='cogs',
451
order=400
452
)
453
)
454
```
455
456
### Custom Form Widgets
457
458
```python
459
from wagtail.admin.widgets import AdminPageChooser, AdminImageChooser
460
from wagtail.admin.panels import FieldPanel
461
from django import forms
462
from django.db import models
463
464
class CustomWidget(forms.TextInput):
465
"""Custom admin widget with special functionality."""
466
template_name = 'admin/widgets/custom_widget.html'
467
468
class Media:
469
css = {'all': ('css/custom-widget.css',)}
470
js = ('js/custom-widget.js',)
471
472
class LandingPage(Page):
473
"""Landing page with custom form widgets."""
474
related_page = models.ForeignKey(
475
'wagtailcore.Page',
476
null=True,
477
blank=True,
478
on_delete=models.SET_NULL
479
)
480
hero_image = models.ForeignKey(
481
'wagtailimages.Image',
482
null=True,
483
blank=True,
484
on_delete=models.SET_NULL
485
)
486
special_field = models.CharField(max_length=100)
487
488
content_panels = Page.content_panels + [
489
FieldPanel('related_page', widget=AdminPageChooser(page_type='blog.BlogPage')),
490
FieldPanel('hero_image', widget=AdminImageChooser()),
491
FieldPanel('special_field', widget=CustomWidget()),
492
]
493
```
494
495
### Inline Editing
496
497
```python
498
from wagtail.models import Orderable
499
from modelcluster.fields import ParentalKey
500
501
class GalleryImage(Orderable):
502
"""Individual image in a gallery."""
503
page = ParentalKey('GalleryPage', related_name='gallery_images', on_delete=models.CASCADE)
504
image = models.ForeignKey('wagtailimages.Image', on_delete=models.CASCADE)
505
caption = models.CharField(max_length=250, blank=True)
506
507
panels = [
508
FieldPanel('image'),
509
FieldPanel('caption'),
510
]
511
512
class GalleryPage(Page):
513
"""Page with inline image gallery editing."""
514
intro = RichTextField(blank=True)
515
516
content_panels = Page.content_panels + [
517
FieldPanel('intro'),
518
InlinePanel(
519
'gallery_images',
520
label="Gallery Images",
521
min_num=1,
522
max_num=20,
523
heading="Image Gallery"
524
),
525
]
526
```