0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-29 17:36:49 +01:00
wagtail/docs/reference/contrib/modeladmin/indexview.rst
Andy Babic 37bbbb9dba Improved code separation in contrib.modeladmin (#3467)
* Split `helpers.py` into separate `url.py`, `permission.py` and `button.py`, dedicated to those separate concerns and update the docs accordingly

* Move `ThumbnailMixin` out to `mixins.py` and update documentation accordingly

* Ad #NOQA to import lines to hush pep errors

* Alphabetise helper import order

* - Delete `helpers/helpers.py`
- wagtal -> wagtail in docs
2017-03-28 10:34:03 +01:00

656 lines
22 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.
Example
```
list_display = ('first_name', 'last_name')
```
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__()`` (``__unicode__()`` on Python 2) 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(self, PersonAdmin).get_empty_value_display(field_name)
The ``__str__()`` (``__unicode__()`` on Python 2) 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 = ('first_name', 'colored_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_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_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 all handled via Django's queryset API, rather than using Wagtail's
search backend. This means it will work for all models, whatever search backend
your project is using, and without any additional setup or configuration.
.. _modeladmin_ordering:
---------------------------
``ModelAdmin.ordering``
---------------------------
**Expected value**: A list or tuple in the same format as a models
[``ordering``](https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) 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):
list_display = ('first_name', 'last_name')
def get_queryset(self, request):
qs = super(PersonAdmin, self).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` 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(PersonAdmin, self).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(EventAdmin, self).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 = 'http://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`