Documentation - fix up ModelAdmin items

- Link to ModelAdmin showed incorrectly as `Index app` not `ModelAdmin app`
- Many code snippets were nested, causing double nesting to appear in published docs
pull/10038/head
LB Johnston 2023-02-05 16:43:27 +10:00 zatwierdzone przez Matt Westcott
rodzic 8131ed282c
commit 8908c38dcd
6 zmienionych plików z 462 dodań i 466 usunięć

Wyświetl plik

@ -1,6 +1,6 @@
# Creating admin views
The most common use for adding custom views to the Wagtail admin is to provide an interface for managing a Django model. The [Index](../reference/contrib/modeladmin/index) app makes this simple, providing ready-made views for listing, creating, and editing objects with minimal configuration.
The most common use for adding custom views to the Wagtail admin is to provide an interface for managing a Django model. The [](../reference/contrib/modeladmin/index) app makes this simple, providing ready-made views for listing, creating, and editing objects with minimal configuration.
For other kinds of admin views that don't fit this pattern, you can write your own Django views and register them as part of the Wagtail admin through [hooks](admin_hooks). In this example, we'll implement a view that displays a calendar for the current year, using [the calendar module](https://docs.python.org/3/library/calendar.html) from Python's standard library.

Wyświetl plik

@ -13,70 +13,70 @@
To change the way your `MyPageModel` is displayed in the CreateView and the EditView, simply define an `edit_handler` or `panels` attribute on your model class.
```python
class MyPageModel(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
address = models.TextField()
class MyPageModel(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
address = models.TextField()
panels = [
MultiFieldPanel([
FieldRowPanel([
FieldPanel('first_name', classname='fn'),
FieldPanel('last_name', classname='ln'),
]),
FieldPanel('address', classname='custom1',)
])
]
panels = [
MultiFieldPanel([
FieldRowPanel([
FieldPanel('first_name', classname='fn'),
FieldPanel('last_name', classname='ln'),
]),
FieldPanel('address', classname='custom1',)
])
]
```
Or alternatively:
```python
class MyPageModel(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
address = models.TextField()
class MyPageModel(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
address = models.TextField()
custom_panels = [
MultiFieldPanel([
FieldRowPanel([
FieldPanel('first_name', classname='fn'),
FieldPanel('last_name', classname='ln'),
]),
FieldPanel('address', classname='custom1',)
])
]
edit_handler = ObjectList(custom_panels)
# or
edit_handler = TabbedInterface([
ObjectList(custom_panels, heading='First Tab'),
ObjectList(...)
custom_panels = [
MultiFieldPanel([
FieldRowPanel([
FieldPanel('first_name', classname='fn'),
FieldPanel('last_name', classname='ln'),
]),
FieldPanel('address', classname='custom1',)
])
]
edit_handler = ObjectList(custom_panels)
# or
edit_handler = TabbedInterface([
ObjectList(custom_panels, heading='First Tab'),
ObjectList(...)
])
```
`edit_handler` and `panels` can alternatively be defined on a `ModelAdmin` definition. This feature is especially useful for use cases where you have to work with models that are 'out of reach' (due to being part of a third-party package, for example).
```python
class BookAdmin(ModelAdmin):
model = Book
class BookAdmin(ModelAdmin):
model = Book
panels = [
FieldPanel('title'),
FieldPanel('author'),
]
panels = [
FieldPanel('title'),
FieldPanel('author'),
]
```
Or alternatively:
```python
class BookAdmin(ModelAdmin):
model = Book
class BookAdmin(ModelAdmin):
model = Book
custom_panels = [
FieldPanel('title'),
FieldPanel('author'),
]
edit_handler = ObjectList(custom_panels)
custom_panels = [
FieldPanel('title'),
FieldPanel('author'),
]
edit_handler = ObjectList(custom_panels)
```
(modeladmin_form_view_extra_css)=

Wyświetl plik

@ -84,26 +84,26 @@ This lets you use Wagtail-specific layouts in an otherwise traditional Django mo
`wagtail_hooks.py` in your app directory would look something like this:
```python
from wagtail.contrib.modeladmin.options import (
ModelAdmin, modeladmin_register)
from .models import Book
from wagtail.contrib.modeladmin.options import (
ModelAdmin, modeladmin_register)
from .models import Book
class BookAdmin(ModelAdmin):
model = Book
base_url_path = 'bookadmin' # customise the URL from default to admin/bookadmin
menu_label = 'Book' # ditch this to use verbose_name_plural from model
menu_icon = 'pilcrow' # change as required
menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd)
add_to_settings_menu = False # or True to add your model to the Settings sub-menu
exclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer view
add_to_admin_menu = True # or False to exclude your model from the menu
list_display = ('title', 'author')
list_filter = ('author',)
search_fields = ('title', 'author')
class BookAdmin(ModelAdmin):
model = Book
base_url_path = 'bookadmin' # customise the URL from default to admin/bookadmin
menu_label = 'Book' # ditch this to use verbose_name_plural from model
menu_icon = 'pilcrow' # change as required
menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd)
add_to_settings_menu = False # or True to add your model to the Settings sub-menu
exclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer view
add_to_admin_menu = True # or False to exclude your model from the menu
list_display = ('title', 'author')
list_filter = ('author',)
search_fields = ('title', 'author')
# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(BookAdmin)
# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(BookAdmin)
```
(modeladmin_example_complex)=
@ -117,49 +117,48 @@ Assume we've defined `Book`, `Author`, and `Genre` models in `models.py`.
`wagtail_hooks.py` in your app directory would look something like this:
```python
from wagtail.contrib.modeladmin.options import (
ModelAdmin, ModelAdminGroup, modeladmin_register)
from .models import (
Book, Author, Genre)
from wagtail.contrib.modeladmin.options import (
ModelAdmin, ModelAdminGroup, modeladmin_register)
from .models import (
Book, Author, Genre)
class BookAdmin(ModelAdmin):
model = Book
menu_label = 'Book' # ditch this to use verbose_name_plural from model
menu_icon = 'pilcrow' # change as required
list_display = ('title', 'author')
list_filter = ('genre', 'author')
search_fields = ('title', 'author')
class BookAdmin(ModelAdmin):
model = Book
menu_label = 'Book' # ditch this to use verbose_name_plural from model
menu_icon = 'pilcrow' # change as required
list_display = ('title', 'author')
list_filter = ('genre', 'author')
search_fields = ('title', 'author')
class AuthorAdmin(ModelAdmin):
model = Author
menu_label = 'Author' # ditch this to use verbose_name_plural from model
menu_icon = 'user' # change as required
list_display = ('first_name', 'last_name')
list_filter = ('first_name', 'last_name')
search_fields = ('first_name', 'last_name')
class AuthorAdmin(ModelAdmin):
model = Author
menu_label = 'Author' # ditch this to use verbose_name_plural from model
menu_icon = 'user' # change as required
list_display = ('first_name', 'last_name')
list_filter = ('first_name', 'last_name')
search_fields = ('first_name', 'last_name')
class GenreAdmin(ModelAdmin):
model = Genre
menu_label = 'Genre' # ditch this to use verbose_name_plural from model
menu_icon = 'group' # change as required
list_display = ('name',)
list_filter = ('name',)
search_fields = ('name',)
class GenreAdmin(ModelAdmin):
model = Genre
menu_label = 'Genre' # ditch this to use verbose_name_plural from model
menu_icon = 'group' # change as required
list_display = ('name',)
list_filter = ('name',)
search_fields = ('name',)
class LibraryGroup(ModelAdminGroup):
menu_label = 'Library'
menu_icon = 'folder-open-inverse' # change as required
menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd)
items = (BookAdmin, AuthorAdmin, GenreAdmin)
class LibraryGroup(ModelAdminGroup):
menu_label = 'Library'
menu_icon = 'folder-open-inverse' # change as required
menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd)
items = (BookAdmin, AuthorAdmin, GenreAdmin)
# When using a ModelAdminGroup class to group several ModelAdmin classes together,
# you only need to register the ModelAdminGroup class with Wagtail:
modeladmin_register(LibraryGroup)
# When using a ModelAdminGroup class to group several ModelAdmin classes together,
# you only need to register the ModelAdminGroup class with Wagtail:
modeladmin_register(LibraryGroup)
```
(modeladmin_multi_registration)=
@ -169,21 +168,20 @@ Assume we've defined `Book`, `Author`, and `Genre` models in `models.py`.
Each time you call `modeladmin_register(MyAdmin)` it creates a new top-level menu item in Wagtail's left sidebar. You can call this multiple times within the same `wagtail_hooks.py` file if you want. The example below will create 3 top-level menus.
```python
class BookAdmin(ModelAdmin):
model = Book
...
class BookAdmin(ModelAdmin):
model = Book
...
class MovieAdmin(ModelAdmin):
model = MovieModel
...
class MovieAdmin(ModelAdmin):
model = MovieModel
...
class MusicAdminGroup(ModelAdminGroup):
menu_label = _("Music")
items = (AlbumAdmin, ArtistAdmin)
...
class MusicAdminGroup(ModelAdminGroup):
menu_label = _("Music")
items = (AlbumAdmin, ArtistAdmin)
...
modeladmin_register(BookAdmin)
modeladmin_register(MovieAdmin)
modeladmin_register(MusicAdminGroup)
modeladmin_register(BookAdmin)
modeladmin_register(MovieAdmin)
modeladmin_register(MusicAdminGroup)
```

Wyświetl plik

@ -26,48 +26,48 @@ You have three possible values that can be used in `list_display`:
- A field of the model. For example:
```python
from wagtail.contrib.modeladmin.options import ModelAdmin
from .models import Person
from wagtail.contrib.modeladmin.options import ModelAdmin
from .models import Person
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('first_name', 'last_name')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('first_name', 'last_name')
```
- The name of a custom method on your `ModelAdmin` class, that accepts a single parameter for the model instance. For example:
```python
from wagtail.contrib.modeladmin.options import ModelAdmin
from .models import Person
from wagtail.contrib.modeladmin.options import ModelAdmin
from .models import Person
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('upper_case_name',)
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('upper_case_name',)
def upper_case_name(self, obj):
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
upper_case_name.short_description = 'Name'
def upper_case_name(self, obj):
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
upper_case_name.short_description = 'Name'
```
- The name of a method on your `Model` class that accepts only `self` as an argument. For example:
```python
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('name', 'decade_born_in')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('name', 'decade_born_in')
```
A few special cases to note about `list_display`:
@ -77,27 +77,27 @@ A few special cases to note about `list_display`:
- If the string provided is a method of the model or `ModelAdmin` class, Django will HTML-escape the output by default. To escape user input and allow your own unescaped tags, use `format_html()`. For example:
```python
from django.db import models
from django.utils.html import format_html
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from django.utils.html import format_html
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
def styled_name(self):
return format_html(
'<span style="color: #{};">{} {}</span>',
self.color_code,
self.first_name,
self.last_name,
)
def styled_name(self):
return format_html(
'<span style="color: #{};">{} {}</span>',
self.color_code,
self.first_name,
self.last_name,
)
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('first_name', 'last_name', 'styled_name')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('first_name', 'last_name', 'styled_name')
```
- If the value of a field is `None`, an empty string, or an iterable without elements, Wagtail will display a dash (-) for that column. You can override this by setting `empty_value_display` on your `ModelAdmin` class. For example:
@ -113,58 +113,58 @@ A few special cases to note about `list_display`:
Or, if you'd like to change the value used depending on the field, you can override `ModelAdmin`'s `get_empty_value_display()` method, like so:
```python
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
name = models.CharField(max_length=100)
nickname = models.CharField(blank=True, max_length=100)
likes_cat_gifs = models.NullBooleanField()
class Person(models.Model):
name = models.CharField(max_length=100)
nickname = models.CharField(blank=True, max_length=100)
likes_cat_gifs = models.NullBooleanField()
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('name', 'nickname', 'likes_cat_gifs')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('name', 'nickname', 'likes_cat_gifs')
def get_empty_value_display(self, field_name=None):
if field_name == 'nickname':
return 'None given'
if field_name == 'likes_cat_gifs':
return 'Unanswered'
return super().get_empty_value_display(field_name)
def get_empty_value_display(self, field_name=None):
if field_name == 'nickname':
return 'None given'
if field_name == 'likes_cat_gifs':
return 'Unanswered'
return super().get_empty_value_display(field_name)
```
The `__str__()` method is just as valid in `list_display` as any other model method, so its perfectly OK to do this:
```python
list_display = ('__str__', 'some_other_field')
list_display = ('__str__', 'some_other_field')
```
By default, the ability to sort results by an item in `list_display` is only offered when it's a field that has an actual database value (because sorting is done at the database level). However, if the output of the method is representative of a database field, you can indicate this fact by setting the `admin_order_field` attribute on that method, like so:
```python
from django.db import models
from django.utils.html import format_html
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from django.utils.html import format_html
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
def styled_first_name(self):
return format_html(
'<span style="color: #{};">{}</span>',
self.color_code,
self.first_name,
)
styled_first_name.admin_order_field = 'first_name'
def styled_first_name(self):
return format_html(
'<span style="color: #{};">{}</span>',
self.color_code,
self.first_name,
)
styled_first_name.admin_order_field = 'first_name'
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('styled_first_name', 'last_name')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('styled_first_name', 'last_name')
```
The above will tell Wagtail to order by the `first_name` field when trying to sort by `styled_first_name` in the index view.
@ -183,17 +183,17 @@ A few special cases to note about `list_display`:
`admin_order_field` supports query lookups to sort by values on related models, too. This example includes an “author first name” column in the list display and allows sorting it by first name:
```python
from django.db import models
from django.db import models
class Blog(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Person, on_delete=models.CASCADE)
class Blog(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Person, on_delete=models.CASCADE)
def author_first_name(self, obj):
return obj.author.first_name
def author_first_name(self, obj):
return obj.author.first_name
author_first_name.admin_order_field = 'author__first_name'
author_first_name.admin_order_field = 'author__first_name'
```
- Elements of `list_display` can also be properties. Please note however, that due to the way properties work in Python, setting `short_description` on a property is only possible when using the `property()` function and **not** with the `@property` decorator.
@ -201,22 +201,22 @@ A few special cases to note about `list_display`:
For example:
```python
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def full_name_property(self):
return self.first_name + ' ' + self.last_name
full_name_property.short_description = "Full name of the person"
def full_name_property(self):
return self.first_name + ' ' + self.last_name
full_name_property.short_description = "Full name of the person"
full_name = property(full_name_property)
full_name = property(full_name_property)
class PersonAdmin(ModelAdmin):
list_display = ('full_name',)
class PersonAdmin(ModelAdmin):
list_display = ('full_name',)
```
(modeladmin_list_export)=
@ -228,8 +228,8 @@ A few special cases to note about `list_display`:
Set `list_export` to set the fields you wish to be exported as columns when downloading a spreadsheet version of your index_view
```python
class PersonAdmin(ModelAdmin):
list_export = ('is_staff', 'company')
class PersonAdmin(ModelAdmin):
list_export = ('is_staff', 'company')
```
(modeladmin_list_filter)=
@ -241,8 +241,8 @@ Set `list_export` to set the fields you wish to be exported as columns when down
Set `list_filter` to activate filters in the right sidebar of the list page for your model. For example:
```python
class PersonAdmin(ModelAdmin):
list_filter = ('is_staff', 'company')
class PersonAdmin(ModelAdmin):
list_filter = ('is_staff', 'company')
```
(modeladmin_export_filename)=
@ -252,8 +252,8 @@ Set `list_filter` to activate filters in the right sidebar of the list page for
**Expected value**: A string specifying the filename of an exported spreadsheet, without file extensions.
```python
class PersonAdmin(ModelAdmin):
export_filename = 'people_spreadsheet'
class PersonAdmin(ModelAdmin):
export_filename = 'people_spreadsheet'
```
(modeladmin_search_fields)=
@ -277,13 +277,13 @@ The default value is `DjangoORMSearchHandler`, which uses the Django ORM to perf
If you would prefer to use the built-in Wagtail search backend to search your models, you can use the `WagtailBackendSearchHandler` class instead. For example:
```python
from wagtail.contrib.modeladmin.helpers import WagtailBackendSearchHandler
from wagtail.contrib.modeladmin.helpers import WagtailBackendSearchHandler
from .models import Person
from .models import Person
class PersonAdmin(ModelAdmin):
model = Person
search_handler_class = WagtailBackendSearchHandler
class PersonAdmin(ModelAdmin):
model = Person
search_handler_class = WagtailBackendSearchHandler
```
### Extra considerations when using `WagtailBackendSearchHandler`
@ -309,15 +309,15 @@ Be sure to test things thoroughly in a development environment (ideally using th
For example, to override the `WagtailBackendSearchHandler` default operator you could do the following:
```python
from wagtail.contrib.modeladmin.helpers import WagtailBackendSearchHandler
from wagtail.search.utils import OR
from wagtail.contrib.modeladmin.helpers import WagtailBackendSearchHandler
from wagtail.search.utils import OR
from .models import IndexedModel
from .models import IndexedModel
class DemoAdmin(ModelAdmin):
model = IndexedModel
search_handler_class = WagtailBackendSearchHandler
extra_search_kwargs = {'operator': OR}
class DemoAdmin(ModelAdmin):
model = IndexedModel
search_handler_class = WagtailBackendSearchHandler
extra_search_kwargs = {'operator': OR}
```
(modeladmin_ordering)=
@ -350,23 +350,23 @@ limiting objects by the current logged-in user is possible.
For example:
```python
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
managed_by = models.ForeignKey('auth.User', on_delete=models.CASCADE)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
managed_by = models.ForeignKey('auth.User', on_delete=models.CASCADE)
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('first_name', 'last_name')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('first_name', 'last_name')
def get_queryset(self, request):
qs = super().get_queryset(request)
# Only show people managed by the current user
return qs.filter(managed_by=request.user)
def get_queryset(self, request):
qs = super().get_queryset(request)
# Only show people managed by the current user
return qs.filter(managed_by=request.user)
```
(modeladmin_get_extra_attrs_for_row)=
@ -382,27 +382,27 @@ If you want to add additional CSS classes, simply provide those class names as a
For example, if you wanted to add some additional class names based on field values, you could do something like:
```python
from decimal import Decimal
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from decimal import Decimal
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class BankAccount(models.Model):
name = models.CharField(max_length=50)
account_number = models.CharField(max_length=50)
balance = models.DecimalField(max_digits=5, num_places=2)
class BankAccount(models.Model):
name = models.CharField(max_length=50)
account_number = models.CharField(max_length=50)
balance = models.DecimalField(max_digits=5, num_places=2)
class BankAccountAdmin(ModelAdmin):
list_display = ('name', 'account_number', 'balance')
class BankAccountAdmin(ModelAdmin):
list_display = ('name', 'account_number', 'balance')
def get_extra_attrs_for_row(self, obj, context):
if obj.balance < Decimal('0.00'):
classname = 'balance-negative'
else:
classname = 'balance-positive'
return {
'class': classname,
}
def get_extra_attrs_for_row(self, obj, context):
if obj.balance < Decimal('0.00'):
classname = 'balance-negative'
else:
classname = 'balance-positive'
return {
'class': classname,
}
```
(modeladmin_get_extra_class_names_for_field_col)=
@ -420,30 +420,30 @@ For example, if you'd like to apply some conditional formatting to a cell
depending on the row's value, you could do something like:
```python
from decimal import Decimal
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from decimal import Decimal
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class BankAccount(models.Model):
name = models.CharField(max_length=50)
account_number = models.CharField(max_length=50)
balance = models.DecimalField(max_digits=5, num_places=2)
class BankAccount(models.Model):
name = models.CharField(max_length=50)
account_number = models.CharField(max_length=50)
balance = models.DecimalField(max_digits=5, num_places=2)
class BankAccountAdmin(ModelAdmin):
list_display = ('name', 'account_number', 'balance')
class BankAccountAdmin(ModelAdmin):
list_display = ('name', 'account_number', 'balance')
def get_extra_class_names_for_field_col(self, obj, field_name):
if field_name == 'balance':
if obj.balance <= Decimal('-100.00'):
return ['brand-danger']
elif obj.balance <= Decimal('-0.00'):
return ['brand-warning']
elif obj.balance <= Decimal('50.00'):
return ['brand-info']
else:
return ['brand-success']
return []
def get_extra_class_names_for_field_col(self, obj, field_name):
if field_name == 'balance':
if obj.balance <= Decimal('-100.00'):
return ['brand-danger']
elif obj.balance <= Decimal('-0.00'):
return ['brand-warning']
elif obj.balance <= Decimal('50.00'):
return ['brand-info']
else:
return ['brand-success']
return []
```
(modeladmin_get_extra_attrs_for_field_col)=
@ -461,59 +461,59 @@ For example, you might like to add some tooltip text to a certain column, to
help give the value more context:
```python
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
name = models.CharField(max_length=100)
likes_cat_gifs = models.NullBooleanField()
class Person(models.Model):
name = models.CharField(max_length=100)
likes_cat_gifs = models.NullBooleanField()
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('name', 'likes_cat_gifs')
class PersonAdmin(ModelAdmin):
model = Person
list_display = ('name', 'likes_cat_gifs')
def get_extra_attrs_for_field_col(self, obj, field_name=None):
attrs = super().get_extra_attrs_for_field_col(obj, field_name)
if field_name == 'likes_cat_gifs' and obj.likes_cat_gifs is None:
attrs.update({
'title': (
'The person was shown several cat gifs, but failed to '
'indicate a preference.'
),
})
return attrs
def get_extra_attrs_for_field_col(self, obj, field_name=None):
attrs = super().get_extra_attrs_for_field_col(obj, field_name)
if field_name == 'likes_cat_gifs' and obj.likes_cat_gifs is None:
attrs.update({
'title': (
'The person was shown several cat gifs, but failed to '
'indicate a preference.'
),
})
return attrs
```
Or you might like to add one or more data attributes to help implement some kind of interactivity using JavaScript:
```python
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.options import ModelAdmin
class Event(models.Model):
title = models.CharField(max_length=255)
start_date = models.DateField()
end_date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
class Event(models.Model):
title = models.CharField(max_length=255)
start_date = models.DateField()
end_date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
class EventAdmin(ModelAdmin):
model = Event
list_display = ('title', 'start_date', 'end_date')
class EventAdmin(ModelAdmin):
model = Event
list_display = ('title', 'start_date', 'end_date')
def get_extra_attrs_for_field_col(self, obj, field_name=None):
attrs = super().get_extra_attrs_for_field_col(obj, field_name)
if field_name == 'start_date':
# Add the start time as data to the 'start_date' cell
attrs.update({ 'data-time': obj.start_time.strftime('%H:%M') })
elif field_name == 'end_date':
# Add the end time as data to the 'end_date' cell
attrs.update({ 'data-time': obj.end_time.strftime('%H:%M') })
return attrs
def get_extra_attrs_for_field_col(self, obj, field_name=None):
attrs = super().get_extra_attrs_for_field_col(obj, field_name)
if field_name == 'start_date':
# Add the start time as data to the 'start_date' cell
attrs.update({ 'data-time': obj.start_time.strftime('%H:%M') })
elif field_name == 'end_date':
# Add the end time as data to the 'end_date' cell
attrs.update({ 'data-time': obj.end_time.strftime('%H:%M') })
return attrs
```
(modeladmin_thumbnailmixin)=
@ -524,47 +524,47 @@ If you're using `wagtailimages.Image` to define an image for each item in your m
as well as `ModelAdmin` when defining your `ModelAdmin` class, and change a few attributes to change the thumbnail to your liking, like so:
```python
from django.db import models
from wagtail.contrib.modeladmin.mixins import ThumbnailMixin
from wagtail.contrib.modeladmin.options import ModelAdmin
from django.db import models
from wagtail.contrib.modeladmin.mixins import ThumbnailMixin
from wagtail.contrib.modeladmin.options import ModelAdmin
class Person(models.Model):
name = models.CharField(max_length=255)
avatar = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, null=True)
likes_cat_gifs = models.NullBooleanField()
class Person(models.Model):
name = models.CharField(max_length=255)
avatar = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, null=True)
likes_cat_gifs = models.NullBooleanField()
class PersonAdmin(ThumbnailMixin, ModelAdmin):
class PersonAdmin(ThumbnailMixin, ModelAdmin):
# Add 'admin_thumb' to list_display, where you want the thumbnail to appear
list_display = ('admin_thumb', 'name', 'likes_cat_gifs')
# Add 'admin_thumb' to list_display, where you want the thumbnail to appear
list_display = ('admin_thumb', 'name', 'likes_cat_gifs')
# Optionally tell IndexView to add buttons to a different column (if the
# first column contains the thumbnail, the buttons are likely better off
# displayed elsewhere)
list_display_add_buttons = 'name'
# Optionally tell IndexView to add buttons to a different column (if the
# first column contains the thumbnail, the buttons are likely better off
# displayed elsewhere)
list_display_add_buttons = 'name'
"""
Set 'thumb_image_field_name' to the name of the ForeignKey field that
links to 'wagtailimages.Image'
"""
thumb_image_field_name = 'avatar'
"""
Set 'thumb_image_field_name' to the name of the ForeignKey field that
links to 'wagtailimages.Image'
"""
thumb_image_field_name = 'avatar'
# Optionally override the filter spec used to create each thumb
thumb_image_filter_spec = 'fill-100x100' # this is the default
# Optionally override the filter spec used to create each thumb
thumb_image_filter_spec = 'fill-100x100' # this is the default
# Optionally override the 'width' attribute value added to each `<img>` tag
thumb_image_width = 50 # this is the default
# Optionally override the 'width' attribute value added to each `<img>` tag
thumb_image_width = 50 # this is the default
# Optionally override the class name added to each `<img>` tag
thumb_classname = 'admin-thumb' # this is the default
# Optionally override the class name added to each `<img>` tag
thumb_classname = 'admin-thumb' # this is the default
# Optionally override the text that appears in the column header
thumb_col_header_text = 'image' # this is the default
# Optionally override the text that appears in the column header
thumb_col_header_text = 'image' # this is the default
# Optionally specify a fallback image to be used when the object doesn't
# have an image set, or the image has been deleted. It can an image from
# your static files folder, or an external URL.
thumb_default = 'https://lorempixel.com/100/100'
# Optionally specify a fallback image to be used when the object doesn't
# have an image set, or the image has been deleted. It can an image from
# your static files folder, or an external URL.
thumb_default = 'https://lorempixel.com/100/100'
```
(modeladmin_list_display_add_buttons)=

Wyświetl plik

@ -90,16 +90,15 @@ For reference, `modeladmin` looks for templates with the following names for eac
To add extra information to a block within one of the above Wagtail templates, use Django's `{{ block.super }}` within the `{% block ... %}` that you wish to extend. For example, if you wish to display an image in an edit form below the fields of the model that is being edited, you could do the following:
```html+django
{% extends "modeladmin/edit.html" %}
{% load static %}
{% extends "modeladmin/edit.html" %}
{% load static %}
{% block content %}
{{ block.super }}
<div>
<img src="{% get_media_prefix %}{{ instance.image }}"/>
</div>
{% endblock %}
{% block content %}
{{ block.super }}
<div>
<img src="{% get_media_prefix %}{{ instance.image }}"/>
</div>
{% endblock %}
```
If for any reason you'd rather bypass the above behaviour and explicitly specify a template for a specific view, you can set either of the following attributes on your `ModelAdmin` class:
@ -127,18 +126,18 @@ For all of the views offered by `ModelAdmin`, the class provides an attribute th
For example, if you'd like to create your own view class and use it for the `IndexView`, you would do the following:
```python
from wagtail.contrib.modeladmin.views import IndexView
from wagtail.contrib.modeladmin.options import ModelAdmin
from .models import MyModel
from wagtail.contrib.modeladmin.views import IndexView
from wagtail.contrib.modeladmin.options import ModelAdmin
from .models import MyModel
class MyCustomIndexView(IndexView):
# New functionality and existing method overrides added here
...
class MyCustomIndexView(IndexView):
# New functionality and existing method overrides added here
...
class MyModelAdmin(ModelAdmin):
model = MyModel
index_view_class = MyCustomIndexView
class MyModelAdmin(ModelAdmin):
model = MyModel
index_view_class = MyCustomIndexView
```
Or, if you have no need for any of `IndexView`'s existing functionality in your view and would rather create your own view from scratch, `modeladmin` will support that too. However, it's highly recommended that you use `modeladmin.views.WMABaseView` as a base for your view. It'll make integrating with your `ModelAdmin` class much easier and will provide a bunch of useful attributes and methods to get you started.
@ -172,32 +171,32 @@ If you find that the above helper classes don't work for your needs, you can eas
Once your class is defined, set the `url_helper_class` attribute on your `ModelAdmin` class to use your custom URLHelper, like so:
```python
from wagtail.contrib.modeladmin.helpers import AdminURLHelper
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import MyModel
from wagtail.contrib.modeladmin.helpers import AdminURLHelper
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import MyModel
class MyURLHelper(AdminURLHelper):
...
class MyURLHelper(AdminURLHelper):
...
class MyModelAdmin(ModelAdmin):
model = MyModel
url_helper_class = MyURLHelper
class MyModelAdmin(ModelAdmin):
model = MyModel
url_helper_class = MyURLHelper
modeladmin_register(MyModelAdmin)
modeladmin_register(MyModelAdmin)
```
Or, if you have a more complicated use case, where simply setting that attribute isn't possible (due to circular imports, for example) or doesn't meet your needs, you can override the `get_url_helper_class` method, like so:
```python
class MyModelAdmin(ModelAdmin):
model = MyModel
class MyModelAdmin(ModelAdmin):
model = MyModel
def get_url_helper_class(self):
if self.some_attribute is True:
return MyURLHelper
return AdminURLHelper
def get_url_helper_class(self):
if self.some_attribute is True:
return MyURLHelper
return AdminURLHelper
```
(modeladmin_permission_helper_class)=
@ -209,32 +208,32 @@ By default, the `modeladmin.helpers.permission.PagePermissionHelper` class is us
If you find that the above helper classes don't work for your needs, you can easily create your own helper class, by sub-classing `PermissionHelper` (or `PagePermissionHelper` if your model extends Wagtail's `Page` model), and making any necessary additions/overrides. Once defined, you set the `permission_helper_class` attribute on your `ModelAdmin` class to use your custom class instead of the default, like so:
```python
from wagtail.contrib.modeladmin.helpers import PermissionHelper
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import MyModel
from wagtail.contrib.modeladmin.helpers import PermissionHelper
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import MyModel
class MyPermissionHelper(PermissionHelper):
...
class MyPermissionHelper(PermissionHelper):
...
class MyModelAdmin(ModelAdmin):
model = MyModel
permission_helper_class = MyPermissionHelper
class MyModelAdmin(ModelAdmin):
model = MyModel
permission_helper_class = MyPermissionHelper
modeladmin_register(MyModelAdmin)
modeladmin_register(MyModelAdmin)
```
Or, if you have a more complicated use case, where simply setting an attribute isn't possible or doesn't meet your needs, you can override the `get_permission_helper_class` method, like so:
```python
class MyModelAdmin(ModelAdmin):
model = MyModel
class MyModelAdmin(ModelAdmin):
model = MyModel
def get_permission_helper_class(self):
if self.some_attribute is True:
return MyPermissionHelper
return PermissionHelper
def get_permission_helper_class(self):
if self.some_attribute is True:
return MyPermissionHelper
return PermissionHelper
```
(modeladmin_button_helper_class)=
@ -246,68 +245,69 @@ By default, the `modeladmin.helpers.button.PageButtonHelper` class is used when
If you wish to add or change buttons for your model's IndexView, you'll need to create your own button helper class by sub-classing `ButtonHelper` or `PageButtonHelper` (if your model extend's Wagtail's `Page` model), and make any necessary additions/overrides. Once defined, you set the `button_helper_class` attribute on your `ModelAdmin` class to use your custom class instead of the default, like so:
```python
from wagtail.contrib.modeladmin.helpers import ButtonHelper
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import MyModel
from wagtail.contrib.modeladmin.helpers import ButtonHelper
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import MyModel
class MyButtonHelper(ButtonHelper):
def add_button(self, classnames_add=None, classnames_exclude=None):
if classnames_add is None:
classnames_add = []
if classnames_exclude is None:
classnames_exclude = []
classnames = self.add_button_classnames + classnames_add
cn = self.finalise_classname(classnames, classnames_exclude)
return {
'url': self.url_helper.create_url,
'label': _('Add %s') % self.verbose_name,
'classname': cn,
'title': _('Add a new %s') % self.verbose_name,
}
class MyButtonHelper(ButtonHelper):
def add_button(self, classnames_add=None, classnames_exclude=None):
if classnames_add is None:
classnames_add = []
if classnames_exclude is None:
classnames_exclude = []
classnames = self.add_button_classnames + classnames_add
cn = self.finalise_classname(classnames, classnames_exclude)
return {
'url': self.url_helper.create_url,
'label': _('Add %s') % self.verbose_name,
'classname': cn,
'title': _('Add a new %s') % self.verbose_name,
}
def inspect_button(self, pk, classnames_add=None, classnames_exclude=None):
...
def inspect_button(self, pk, classnames_add=None, classnames_exclude=None):
...
def edit_button(self, pk, classnames_add=None, classnames_exclude=None):
...
def edit_button(self, pk, classnames_add=None, classnames_exclude=None):
...
def delete_button(self, pk, classnames_add=None, classnames_exclude=None):
...
def delete_button(self, pk, classnames_add=None, classnames_exclude=None):
...
class MyModelAdmin(ModelAdmin):
model = MyModel
button_helper_class = MyButtonHelper
class MyModelAdmin(ModelAdmin):
model = MyModel
button_helper_class = MyButtonHelper
modeladmin_register(MyModelAdmin)
modeladmin_register(MyModelAdmin)
```
To customise the buttons found in the ModelAdmin List View you can change the returned dictionary in the `add_button`, `delete_button`, `edit_button` or `inspect_button` methods. For example if you wanted to change the `Delete` button you could modify the `delete_button` method in your `ButtonHelper` like so:
```python
class MyButtonHelper(ButtonHelper):
...
def delete_button(self, pk, classnames_add=None, classnames_exclude=None):
...
return {
'url': reverse("your_custom_url"),
'label': _('Delete'),
'classname': "custom-css-class",
'title': _('Delete this item')
}
class MyButtonHelper(ButtonHelper):
#...
def delete_button(self, pk, classnames_add=None, classnames_exclude=None):
#...
return {
'url': reverse("your_custom_url"),
'label': _('Delete'),
'classname': "custom-css-class",
'title': _('Delete this item')
}
```
Or, if you have a more complicated use case, where simply setting an attribute isn't possible or doesn't meet your needs, you can override the `get_button_helper_class` method, like so:
```python
class MyModelAdmin(ModelAdmin):
model = MyModel
class MyModelAdmin(ModelAdmin):
model = MyModel
def get_button_helper_class(self):
if self.some_attribute is True:
return MyButtonHelper
return ButtonHelper
def get_button_helper_class(self):
if self.some_attribute is True:
return MyButtonHelper
return ButtonHelper
```
(modeladmin_helpers_in_custom_views)=

Wyświetl plik

@ -34,37 +34,36 @@ model has an `author` field (a `ForeignKey` to the `Author` model)
to allow a single author to be specified for each post.
```python
# file: wagtail_hooks.py
# file: wagtail_hooks.py
from wagtail.admin.widgets import PageListingButton
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from wagtail import hooks
from wagtail.admin.widgets import PageListingButton
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from wagtail import hooks
# Author & BlogPage model not shown in this example
from models import Author
# Author & BlogPage model not shown in this example
from models import Author
# ensure our modeladmin is created
class AuthorModelAdmin(ModelAdmin):
model = Author
menu_order = 200
# ensure our modeladmin is created
class AuthorModelAdmin(ModelAdmin):
model = Author
menu_order = 200
# Creating an instance of `AuthorModelAdmin`
author_modeladmin = AuthorModelAdmin()
# Creating an instance of `AuthorModelAdmin`
author_modeladmin = AuthorModelAdmin()
@hooks.register('register_page_listing_buttons')
def add_author_edit_buttons(page, page_perms, next_url=None):
"""
For pages that have an author, add an additional button to the page listing,
linking to the 'edit' page for that author.
"""
author_id = getattr(page, 'author_id', None)
if author_id:
# the url helper will return something like: /admin/my-app/author/edit/2/
author_edit_url = author_modeladmin.url_helper.get_action_url('edit', author_id)
yield PageListingButton('Edit Author', author_edit_url, priority=10)
@hooks.register('register_page_listing_buttons')
def add_author_edit_buttons(page, page_perms, next_url=None):
"""
For pages that have an author, add an additional button to the page listing,
linking to the 'edit' page for that author.
"""
author_id = getattr(page, 'author_id', None)
if author_id:
# the url helper will return something like: /admin/my-app/author/edit/2/
author_edit_url = author_modeladmin.url_helper.get_action_url('edit', author_id)
yield PageListingButton('Edit Author', author_edit_url, priority=10)
modeladmin_register(AuthorModelAdmin)
modeladmin_register(AuthorModelAdmin)
```
As you can see from the example above, when using `get_action_url()` to
@ -98,20 +97,19 @@ shortcut available; `url_helper.index_url` and `url_helper.create_url`.
For example:
```python
from .wagtail_hooks import AuthorModelAdmin
from .wagtail_hooks import AuthorModelAdmin
url_helper = AuthorModelAdmin().url_helper
url_helper = AuthorModelAdmin().url_helper
index_url = url_helper.get_action_url('index')
# OR we can use the 'index_url' shortcut
also_index_url = url_helper.index_url # note: do not call this property as a function
# both will output /admin/my-app/author
index_url = url_helper.get_action_url('index')
# OR we can use the 'index_url' shortcut
also_index_url = url_helper.index_url # note: do not call this property as a function
# both will output /admin/my-app/author
create_url = url_helper.get_action_url('create')
# OR we can use the 'create_url' shortcut
also_create_url = url_helper.create_url # note: do not call this property as a function
# both will output /admin/my-app/author/create
create_url = url_helper.get_action_url('create')
# OR we can use the 'create_url' shortcut
also_create_url = url_helper.create_url # note: do not call this property as a function
# both will output /admin/my-app/author/create
```
```{note}