============================================ 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( '{} {}', 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 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( '{}', 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 model’s [``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 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): 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 `` 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`