123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- ============================================
- Customising ``IndexView`` - the listing view
- ============================================
- For the sake of consistency, this section of the docs will refer to the listing
- view as ``IndexView``, because that is the view class that does all the heavy
- lifting.
- You can use the following attributes and methods on the ``ModelAdmin`` class to
- alter how your model data is treated and represented by the ``IndexView``.
- .. contents::
- :local:
- :depth: 1
- .. _modeladmin_list_display:
- ---------------------------
- ``ModelAdmin.list_display``
- ---------------------------
- **Expected value**: A list or tuple, where each item is the name of a field or
- single-argument callable on your model, or a similarly simple method defined
- on the ``ModelAdmin`` class itself.
- Default value: ``('__str__',)``
- Set ``list_display`` to control which fields are displayed in the ``IndexView``
- for your model.
- You have three possible values that can be used in ``list_display``:
- - A field of the model. For example:
- .. code-block:: python
- from wagtail.contrib.modeladmin.options import ModelAdmin
- from .models import Person
- 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:
- .. code-block:: python
- from wagtail.contrib.modeladmin.options import ModelAdmin
- from .models import Person
- 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'
- - The name of a method on your ``Model`` class that accepts only ``self`` as
- an argument. For example:
- .. code-block:: python
- 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()
- 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')
- A few special cases to note about ``list_display``:
- - If the field is a ``ForeignKey``, Django will display the output of
- ``__str__()`` of the related object.
- - 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:
- .. code-block:: python
- 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)
- def colored_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', 'colored_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:
- .. code-block:: python
- from wagtail.contrib.modeladmin.options import ModelAdmin
- class PersonAdmin(ModelAdmin):
- empty_value_display = 'N/A'
- ...
- 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:
- .. code-block:: python
- 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 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:
- .. code-block:: python
- 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:
- .. code-block:: python
- 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)
- def colored_first_name(self):
- return format_html(
- '<span style="color: #{};">{}</span>',
- self.color_code,
- self.first_name,
- )
- colored_first_name.admin_order_field = 'first_name'
- class PersonAdmin(ModelAdmin):
- model = Person
- list_display = ('colored_first_name', 'last_name')
- The above will tell Wagtail to order by the ``first_name`` field when
- trying to sort by ``colored_first_name`` in the index view.
- To indicate descending order with ``admin_order_field`` you can use a
- hyphen prefix on the field name. Using the above example, this would look
- like:
- .. code-block:: python
- colored_first_name.admin_order_field = '-first_name'
- ``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:
- .. code-block:: python
- from django.db import models
- 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
- 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.
- For example:
- .. code-block:: python
- 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)
- 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)
- class PersonAdmin(ModelAdmin):
- list_display = ('full_name',)
- .. _modeladmin_list_export:
- ---------------------------
- ``ModelAdmin.list_export``
- ---------------------------
- **Expected value**: A list or tuple, where each item is the name of a field or
- single-argument callable on your model, or a similarly simple method defined
- on the ``ModelAdmin`` class itself.
- Set ``list_export`` to set the fields you wish to be exported as columns when
- downloading a spreadsheet version of your index_view
- .. code-block:: python
- class PersonAdmin(ModelAdmin):
- list_export = ('is_staff', 'company')
- .. _modeladmin_list_filter:
- ---------------------------
- ``ModelAdmin.list_filter``
- ---------------------------
- **Expected value**: A list or tuple, where each item is the name of model field
- of type ``BooleanField``, ``CharField``, ``DateField``, ``DateTimeField``,
- ``IntegerField`` or ``ForeignKey``.
- Set ``list_filter`` to activate filters in the right sidebar of the list page
- for your model. For example:
- .. code-block:: python
- class PersonAdmin(ModelAdmin):
- list_filter = ('is_staff', 'company')
- .. _modeladmin_export_filename:
- ------------------------------
- ``ModelAdmin.export_filename``
- ------------------------------
- **Expected value**: A string specifying the filename of an exported spreadsheet,
- without file extensions.
- .. code-block:: python
- class PersonAdmin(ModelAdmin):
- export_filename = 'people_spreadsheet'
- .. _modeladmin_search_fields:
- ----------------------------
- ``ModelAdmin.search_fields``
- ----------------------------
- **Expected value**: A list or tuple, where each item is the name of a model
- field of type ``CharField``, ``TextField``, ``RichTextField`` or
- ``StreamField``.
- Set ``search_fields`` to enable a search box at the top of the index page
- for your model. You should add names of any fields on the model that should
- be searched whenever somebody submits a search query using the search box.
- Searching is handled via Django's QuerySet API by default,
- see `ModelAdmin.search_handler_class`_ about changing this behaviour.
- This means by default it will work for all models, whatever search backend
- your project is using, and without any additional setup or configuration.
- .. _modeladmin_search_handler_class:
- -----------------------------------
- ``ModelAdmin.search_handler_class``
- -----------------------------------
- **Expected value**: A subclass of
- ``wagtail.contrib.modeladmin.helpers.search.BaseSearchHandler``
- The default value is ``DjangoORMSearchHandler``, which uses the Django ORM to
- perform lookups on the fields specified by ``search_fields``.
- 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:
- .. code-block:: python
- from wagtail.contrib.modeladmin.helpers import WagtailBackendSearchHandler
- from .models import Person
- class PersonAdmin(ModelAdmin):
- model = Person
- search_handler_class = WagtailBackendSearchHandler
- Extra considerations when using ``WagtailBackendSearchHandler``
- ===============================================================
- ``ModelAdmin.search_fields`` is used differently
- ------------------------------------------------
- The value of ``search_fields`` is passed to the underlying search backend to
- limit the fields used when matching. Each item in the list must be indexed
- on your model using :ref:`wagtailsearch_index_searchfield`.
- To allow matching on **any** indexed field, set the ``search_fields`` attribute
- on your ``ModelAdmin`` class to ``None``, or remove it completely.
- Indexing extra fields using ``index.FilterField``
- -------------------------------------------------
- The underlying search backend must be able to interpret all of the fields and
- relationships used in the queryset created by ``IndexView``, including those
- used in ``prefetch()`` or ``select_related()`` queryset methods, or used in
- ``list_display``, ``list_filter`` or ``ordering``.
- Be sure to test things thoroughly in a development environment (ideally
- using the same search backend as you use in production). Wagtail will raise
- an ``IndexError`` if the backend encounters something it does not understand,
- and will tell you what you need to change.
- .. _modeladmin_extra_search_kwargs:
- ----------------------------------
- ``ModelAdmin.extra_search_kwargs``
- ----------------------------------
- **Expected value**: A dictionary of keyword arguments that will be passed on to the ``search()`` method of
- ``search_handler_class``.
- For example, to override the ``WagtailBackendSearchHandler`` default operator you could do the following:
- .. code-block:: python
- from wagtail.contrib.modeladmin.helpers import WagtailBackendSearchHandler
- from wagtail.search.utils import OR
- from .models import IndexedModel
- class DemoAdmin(ModelAdmin):
- model = IndexedModel
- search_handler_class = WagtailBackendSearchHandler
- extra_search_kwargs = {'operator': OR}
- .. _modeladmin_ordering:
- ---------------------------
- ``ModelAdmin.ordering``
- ---------------------------
- **Expected value**: A list or tuple in the same format as a model’s
- :attr:`~django.db.models.Options.ordering` parameter.
- Set ``ordering`` to specify the default ordering of objects when listed by
- IndexView. If not provided, the model’s default ordering will be respected.
- If you need to specify a dynamic order (for example, depending on user or
- language) you can override the ``get_ordering()`` method instead.
- .. _modeladmin_list_per_page:
- ----------------------------
- ``ModelAdmin.list_per_page``
- ----------------------------
- **Expected value**: A positive integer
- Set ``list_per_page`` to control how many items appear on each paginated page
- of the index view. By default, this is set to ``100``.
- .. _modeladmin_get_queryset:
- -----------------------------
- ``ModelAdmin.get_queryset()``
- -----------------------------
- **Must return**: A QuerySet
- The ``get_queryset`` method returns the 'base' QuerySet for your model, to
- which any filters and search queries are applied. By default, the ``all()``
- method of your model's default manager is used. But, if for any reason you
- only want a certain sub-set of objects to appear in the IndexView listing,
- overriding the ``get_queryset`` method on your ``ModelAdmin`` class can help
- you with that. The method takes an ``HttpRequest`` object as a parameter, so
- limiting objects by the current logged-in user is possible.
- For example:
- .. code-block:: python
- 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 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)
- .. _modeladmin_get_extra_attrs_for_row:
- ----------------------------------------------------
- ``ModelAdmin.get_extra_attrs_for_row()``
- ----------------------------------------------------
- **Must return**: A dictionary
- The ``get_extra_attrs_for_row`` method allows you to add html attributes to
- the opening ``<tr>`` tag for each result, in addition to the ``data-object_pk`` and
- ``class`` attributes already added by the ``result_row_display`` template tag.
- If you want to add additional CSS classes, simply provide those class names
- as a string value using the ``'class'`` key, and the ``odd``/``even`` will be appended
- to your custom class names when rendering.
- For example, if you wanted to add some additional class names based on field
- values, you could do something like:
- .. code-block:: 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,
- }
- .. _modeladmin_get_extra_class_names_for_field_col:
- ----------------------------------------------------
- ``ModelAdmin.get_extra_class_names_for_field_col()``
- ----------------------------------------------------
- **Must return**: A list
- The ``get_extra_class_names_for_field_col`` method allows you to add additional
- CSS class names to any of the columns defined by ``list_display`` for your
- model. The method takes two parameters:
- - ``obj``: the object being represented by the current row
- - ``field_name``: the item from ``list_display`` being represented by the
- current column
- 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:
- .. code-block:: 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 []
- .. _modeladmin_get_extra_attrs_for_field_col:
- ----------------------------------------------------
- ``ModelAdmin.get_extra_attrs_for_field_col()``
- ----------------------------------------------------
- **Must return**: A dictionary
- The ``get_extra_attrs_for_field_col`` method allows you to add additional HTML
- attributes to any of the columns defined in ``list_display``. Like the
- ``get_extra_class_names_for_field_col`` method above, this method takes two
- parameters:
- - ``obj``: the object being represented by the current row
- - ``field_name``: the item from ``list_display`` being represented by the
- current column
- For example, you might like to add some tooltip text to a certain column, to
- help give the value more context:
- .. code-block:: python
- 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 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:
- .. code-block:: 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
- .. _modeladmin_thumbnailmixin:
- ----------------------------------------------------
- ``wagtail.contrib.modeladmin.mixins.ThumbnailMixin``
- ----------------------------------------------------
- If you're using ``wagtailimages.Image`` to define an image for each item in
- your model, ``ThumbnailMixin`` can help you add thumbnail versions of that
- image to each row in ``IndexView``. To use it, simply extend ``ThumbnailMixin``
- as well as ``ModelAdmin`` when defining your ``ModelAdmin`` class, and
- change a few attributes to change the thumbnail to your liking, like so:
- .. code-block:: python
- 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 PersonAdmin(ThumbnailMixin, ModelAdmin):
- # 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'
- """
- 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 '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 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'
- .. _modeladmin_list_display_add_buttons:
- ---------------------------------------
- ``ModelAdmin.list_display_add_buttons``
- ---------------------------------------
- **Expected value**: A string matching one of the items in ``list_display``.
- If for any reason you'd like to change which column the action buttons appear
- in for each row, you can specify a different column using
- ``list_display_add_buttons`` on your ``ModelAdmin`` class. The value must
- match one of the items your class's ``list_display`` attribute. By default,
- buttons are added to the first column of each row.
- See the ``ThumbnailMixin`` example above to see how
- ``list_display_add_buttons`` can be used.
- .. _modeladmin_index_view_extra_css:
- -----------------------------------
- ``ModelAdmin.index_view_extra_css``
- -----------------------------------
- **Expected value**: A list of path names of additional stylesheets to be added
- to the ``IndexView``
- See the following part of the docs to find out more:
- :ref:`modeladmin_adding_css_and_js`
- .. _modeladmin_index_view_extra_js:
- -----------------------------------
- ``ModelAdmin.index_view_extra_js``
- -----------------------------------
- **Expected value**: A list of path names of additional js files to be added
- to the ``IndexView``
- See the following part of the docs to find out more:
- :ref:`modeladmin_adding_css_and_js`
- .. _modeladmin_index_template_name:
- ---------------------------------------
- ``ModelAdmin.index_template_name``
- ---------------------------------------
- **Expected value**: The path to a custom template to use for ``IndexView``
- See the following part of the docs to find out more:
- :ref:`modeladmin_overriding_templates`
- .. _modeladmin_index_view_class:
- ---------------------------------------
- ``ModelAdmin.index_view_class``
- ---------------------------------------
- **Expected value**: A custom ``view`` class to replace
- ``modeladmin.views.IndexView``
- See the following part of the docs to find out more:
- :ref:`modeladmin_overriding_views`
|