0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-30 19:20:56 +01:00
wagtail/docs/reference/contrib/modeladmin/indexview.rst
2020-05-14 15:50:47 +01:00

768 lines
25 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

============================================
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 its 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 models
:attr:`~django.db.models.Options.ordering` parameter.
Set ``ordering`` to specify the default ordering of objects when listed by
IndexView. If not provided, the models 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):
field_name == 'balance':
if balance <= Decimal('-100.00'):
return ['brand-danger']
if balance <= Decimal('-0.00'):
return ['brand-warning']
if 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`