Browse Source

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
LB Johnston 2 years ago
parent
commit
8908c38dcd

+ 1 - 1
docs/extending/admin_views.md

@@ -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.
 

+ 47 - 47
docs/reference/contrib/modeladmin/create_edit_delete_views.md

@@ -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()
-
-        panels = [
-            MultiFieldPanel([
-                FieldRowPanel([
-                    FieldPanel('first_name', classname='fn'),
-                    FieldPanel('last_name', classname='ln'),
-                ]),
-                FieldPanel('address', classname='custom1',)
-            ])
-        ]
+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',)
+        ])
+    ]
 ```
 
 Or alternatively:
 
 ```python
-    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(...)
+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(...)
+    ])
 ```
 
 `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
-
-        custom_panels = [
-            FieldPanel('title'),
-            FieldPanel('author'),
-        ]
-        edit_handler = ObjectList(custom_panels)
+class BookAdmin(ModelAdmin):
+    model = Book
+
+    custom_panels = [
+        FieldPanel('title'),
+        FieldPanel('author'),
+    ]
+    edit_handler = ObjectList(custom_panels)
 ```
 
 (modeladmin_form_view_extra_css)=

+ 78 - 80
docs/reference/contrib/modeladmin/index.md

@@ -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
-
-
-    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)
+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')
+
+# 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)
-
-
-    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 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)
-
-    # When using a ModelAdminGroup class to group several ModelAdmin classes together,
-    # you only need to register the ModelAdminGroup class with Wagtail:
-    modeladmin_register(LibraryGroup)
+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 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 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)
 ```
 
 (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 MovieAdmin(ModelAdmin):
-        model = MovieModel
-        ...
-
-    class MusicAdminGroup(ModelAdminGroup):
-        menu_label = _("Music")
-        items = (AlbumAdmin, ArtistAdmin)
-        ...
-
-    modeladmin_register(BookAdmin)
-    modeladmin_register(MovieAdmin)
-    modeladmin_register(MusicAdminGroup)
+class BookAdmin(ModelAdmin):
+    model = Book
+    ...
+
+class MovieAdmin(ModelAdmin):
+    model = MovieModel
+    ...
+
+class MusicAdminGroup(ModelAdminGroup):
+    menu_label = _("Music")
+    items = (AlbumAdmin, ArtistAdmin)
+    ...
+
+modeladmin_register(BookAdmin)
+modeladmin_register(MovieAdmin)
+modeladmin_register(MusicAdminGroup)
 ```

+ 244 - 244
docs/reference/contrib/modeladmin/indexview.md

@@ -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')
-
-            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)
+    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)
     ```
 
     The `__str__()` method is just as valid in `list_display` as any other model method, so it’s 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
-
-    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')
-
-        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,
-            }
+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 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,
+        }
 ```
 
 (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
-
-    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')
-
-        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 []
+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 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 []
 ```
 
 (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')
-
-        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
+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
 ```
 
 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
-
-
-    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')
-
-        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
+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 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
 ```
 
 (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)=

+ 95 - 95
docs/reference/contrib/modeladmin/primer.md

@@ -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 %}
-
-    {% block content %}
-        {{ block.super }}
-        <div>
-            <img src="{% get_media_prefix %}{{ instance.image }}"/>
-        </div>
-    {% endblock %}
+{% extends "modeladmin/edit.html" %}
+{% load static %}
+
+{% 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
-
-
-    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):
-            ...
+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,
+        }
+
+    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)=

+ 40 - 42
docs/reference/contrib/modeladmin/tips_and_tricks/reversing_urls.md

@@ -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
-
-    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
-
-    # ensure our modeladmin is created
-    class AuthorModelAdmin(ModelAdmin):
-        model = Author
-        menu_order = 200
-
-    # 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)
-
-    modeladmin_register(AuthorModelAdmin)
+# file: wagtail_hooks.py
+
+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
+
+# ensure our modeladmin is created
+class AuthorModelAdmin(ModelAdmin):
+    model = Author
+    menu_order = 200
+
+# 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)
+
+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}