indexview.rst 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. ============================================
  2. Customising ``IndexView`` - the listing view
  3. ============================================
  4. For the sake of consistency, this section of the docs will refer to the listing
  5. view as ``IndexView``, because that is the view class that does all the heavy
  6. lifting.
  7. You can use the following attributes and methods on the ``ModelAdmin`` class to
  8. alter how your model data is treated and represented by the ``IndexView``.
  9. .. contents::
  10. :local:
  11. :depth: 1
  12. .. _modeladmin_list_display:
  13. ---------------------------
  14. ``ModelAdmin.list_display``
  15. ---------------------------
  16. **Expected value**: A list or tuple, where each item is the name of a field or
  17. single-argument callable on your model, or a similarly simple method defined
  18. on the ``ModelAdmin`` class itself.
  19. Default value: ``('__str__',)``
  20. Set ``list_display`` to control which fields are displayed in the IndexView
  21. for your model.
  22. Example
  23. ```
  24. list_display = ('first_name', 'last_name')
  25. ```
  26. You have three possible values that can be used in list_display:
  27. - A field of the model. For example:
  28. .. code-block:: python
  29. from wagtail.contrib.modeladmin.options import ModelAdmin
  30. from .models import Person
  31. class PersonAdmin(ModelAdmin):
  32. model = Person
  33. list_display = ('first_name', 'last_name')
  34. - The name of a custom method on your ``ModelAdmin`` class, that accepts a
  35. single parameter for the model instance. For example:
  36. .. code-block:: python
  37. from wagtail.contrib.modeladmin.options import ModelAdmin
  38. from .models import Person
  39. class PersonAdmin(ModelAdmin):
  40. model = Person
  41. list_display = ('upper_case_name',)
  42. def upper_case_name(self, obj):
  43. return ("%s %s" % (obj.first_name, obj.last_name)).upper()
  44. upper_case_name.short_description = 'Name'
  45. - The name of a method on your ``Model`` class that accepts only ``self`` as
  46. an argument. For example:
  47. .. code-block:: python
  48. from django.db import models
  49. from wagtail.contrib.modeladmin.options import ModelAdmin
  50. class Person(models.Model):
  51. name = models.CharField(max_length=50)
  52. birthday = models.DateField()
  53. def decade_born_in(self):
  54. return self.birthday.strftime('%Y')[:3] + "0's"
  55. decade_born_in.short_description = 'Birth decade'
  56. class PersonAdmin(ModelAdmin):
  57. model = Person
  58. list_display = ('name', 'decade_born_in')
  59. A few special cases to note about ``list_display``:
  60. - If the field is a ``ForeignKey``, Django will display the output of
  61. ``__str__()`` (``__unicode__()`` on Python 2) of the related object.
  62. - If the string provided is a method of the model or ``ModelAdmin`` class,
  63. Django will HTML-escape the output by default. To escape user input and
  64. allow your own unescaped tags, use ``format_html()``. For example:
  65. .. code-block:: python
  66. from django.db import models
  67. from django.utils.html import format_html
  68. from wagtail.contrib.modeladmin.options import ModelAdmin
  69. class Person(models.Model):
  70. first_name = models.CharField(max_length=50)
  71. last_name = models.CharField(max_length=50)
  72. color_code = models.CharField(max_length=6)
  73. def colored_name(self):
  74. return format_html(
  75. '<span style="color: #{};">{} {}</span>',
  76. self.color_code,
  77. self.first_name,
  78. self.last_name,
  79. )
  80. class PersonAdmin(ModelAdmin):
  81. model = Person
  82. list_display = ('first_name', 'last_name', 'colored_name')
  83. - If the value of a field is ``None``, an empty string, or an iterable
  84. without elements, Wagtail will display a dash (-) for that column. You can
  85. override this by setting ``empty_value_display`` on your ``ModelAdmin``
  86. class. For example:
  87. .. code-block:: python
  88. from wagtail.contrib.modeladmin.options import ModelAdmin
  89. class PersonAdmin(ModelAdmin):
  90. empty_value_display = 'N/A'
  91. ...
  92. Or, if you'd like to change the value used depending on the field, you can
  93. override ``ModelAdmin``'s ``get_empty_value_display()`` method, like so:
  94. .. code-block:: python
  95. from django.db import models
  96. from wagtail.contrib.modeladmin.options import ModelAdmin
  97. class Person(models.Model):
  98. name = models.CharField(max_length=100)
  99. nickname = models.CharField(blank=True, max_length=100)
  100. likes_cat_gifs = models.NullBooleanField()
  101. class PersonAdmin(ModelAdmin):
  102. model = Person
  103. list_display = ('name', 'nickname', 'likes_cat_gifs')
  104. def get_empty_value_display(self, field_name=None):
  105. if field_name == 'nickname':
  106. return 'None given'
  107. if field_name == 'likes_cat_gifs':
  108. return 'Unanswered'
  109. return super(self, PersonAdmin).get_empty_value_display(field_name)
  110. The ``__str__()`` (``__unicode__()`` on Python 2) method is just as valid
  111. in ``list_display`` as any other model method, so it’s perfectly OK to do
  112. this:
  113. .. code-block:: python
  114. list_display = ('__str__', 'some_other_field')
  115. By default, the ability to sort results by an item in ``list_display`` is
  116. only offered when it's a field that has an actual database value (because
  117. sorting is done at the database level). However, if the output of the
  118. method is representative of a database field, you can indicate this fact by
  119. setting the ``admin_order_field`` attribute on that method, like so:
  120. .. code-block:: python
  121. from django.db import models
  122. from django.utils.html import format_html
  123. from wagtail.contrib.modeladmin.options import ModelAdmin
  124. class Person(models.Model):
  125. first_name = models.CharField(max_length=50)
  126. last_name = models.CharField(max_length=50)
  127. color_code = models.CharField(max_length=6)
  128. def colored_first_name(self):
  129. return format_html(
  130. '<span style="color: #{};">{}</span>',
  131. self.color_code,
  132. self.first_name,
  133. )
  134. colored_first_name.admin_order_field = 'first_name'
  135. class PersonAdmin(ModelAdmin):
  136. model = Person
  137. list_display = ('first_name', 'colored_name')
  138. The above will tell Wagtail to order by the ``first_name`` field when
  139. trying to sort by ``colored_first_name`` in the index view.
  140. To indicate descending order with ``admin_order_field`` you can use a
  141. hyphen prefix on the field name. Using the above example, this would look
  142. like:
  143. .. code-block:: python
  144. colored_first_name.admin_order_field = '-first_name'
  145. ``admin_order_field`` supports query lookups to sort by values on related
  146. models, too. This example includes an “author first name” column in the
  147. list display and allows sorting it by first name:
  148. .. code-block:: python
  149. from django.db import models
  150. class Blog(models.Model):
  151. title = models.CharField(max_length=255)
  152. author = models.ForeignKey(Person, on_delete=models.CASCADE)
  153. def author_first_name(self, obj):
  154. return obj.author.first_name
  155. author_first_name.admin_order_field = 'author__first_name'
  156. - Elements of ``list_display`` can also be properties. Please note however,
  157. that due to the way properties work in Python, setting
  158. ``short_description`` on a property is only possible when using the
  159. ``property()`` function and **not** with the ``@property`` decorator.
  160. For example:
  161. .. code-block:: python
  162. from django.db import models
  163. from wagtail.contrib.modeladmin.options import ModelAdmin
  164. class Person(models.Model):
  165. first_name = models.CharField(max_length=50)
  166. last_name = models.CharField(max_length=50)
  167. def full_name_property(self):
  168. return self.first_name + ' ' + self.last_name
  169. full_name_property.short_description = "Full name of the person"
  170. full_name = property(full_name_property)
  171. class PersonAdmin(ModelAdmin):
  172. list_display = ('full_name',)
  173. .. _modeladmin_list_filter:
  174. ---------------------------
  175. ``ModelAdmin.list_filter``
  176. ---------------------------
  177. **Expected value**: A list or tuple, where each item is the name of model field
  178. of type ``BooleanField``, ``CharField``, ``DateField``, ``DateTimeField``,
  179. ``IntegerField`` or ``ForeignKey``.
  180. Set ``list_filter`` to activate filters in the right sidebar of the list page
  181. for your model. For example:
  182. .. code-block:: python
  183. class PersonAdmin(ModelAdmin):
  184. list_filter = ('is_staff', 'company')
  185. .. _modeladmin_search_fields:
  186. ----------------------------
  187. ``ModelAdmin.search_fields``
  188. ----------------------------
  189. **Expected value**: A list or tuple, where each item is the name of a model field
  190. of type ``CharField``, ``TextField``, ``RichTextField`` or ``StreamField``.
  191. Set ``search_fields`` to enable a search box at the top of the index page
  192. for your model. You should add names of any fields on the model that should
  193. be searched whenever somebody submits a search query using the search box.
  194. Searching is all handled via Django's queryset API, rather than using Wagtail's
  195. search backend. This means it will work for all models, whatever search backend
  196. your project is using, and without any additional setup or configuration.
  197. .. _modeladmin_ordering:
  198. ---------------------------
  199. ``ModelAdmin.ordering``
  200. ---------------------------
  201. **Expected value**: A list or tuple in the same format as a model’s
  202. [``ordering``](https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) parameter.
  203. Set ``ordering`` to specify the default ordering of objects when listed by
  204. IndexView. If not provided, the model’s default ordering will be respected.
  205. If you need to specify a dynamic order (for example, depending on user or
  206. language) you can override the ``get_ordering()`` method instead.
  207. .. _modeladmin_list_per_page:
  208. ----------------------------
  209. ``ModelAdmin.list_per_page``
  210. ----------------------------
  211. **Expected value**: A positive integer
  212. Set ``list_per_page`` to control how many items appear on each paginated page
  213. of the index view. By default, this is set to ``100``.
  214. .. _modeladmin_get_queryset:
  215. -----------------------------
  216. ``ModelAdmin.get_queryset()``
  217. -----------------------------
  218. **Must return**: A QuerySet
  219. The ``get_queryset`` method returns the 'base' queryset for your model, to
  220. which any filters and search queries are applied. By default, the ``all()``
  221. method of your model's default manager is used. But, if for any reason you
  222. only want a certain sub-set of objects to appear in the IndexView listing,
  223. overriding the ``get_queryset`` method on your ``ModelAdmin`` class can help
  224. you with that. The method takes an ``HttpRequest`` object as a parameter, so
  225. limiting objects by the current logged-in user is possible.
  226. For example:
  227. .. code-block:: python
  228. from django.db import models
  229. from wagtail.contrib.modeladmin.options import ModelAdmin
  230. class Person(models.Model):
  231. first_name = models.CharField(max_length=50)
  232. last_name = models.CharField(max_length=50)
  233. managed_by = models.ForeignKey(`auth.User`, on_delete=models.CASCADE)
  234. class PersonAdmin(ModelAdmin):
  235. list_display = ('first_name', 'last_name')
  236. def get_queryset(self, request):
  237. qs = super(PersonAdmin, self).get_queryset(request)
  238. # Only show people managed by the current user
  239. return qs.filter(managed_by=request.user)
  240. .. _modeladmin_get_extra_attrs_for_row:
  241. ----------------------------------------------------
  242. ``ModelAdmin.get_extra_attrs_for_row()``
  243. ----------------------------------------------------
  244. **Must return**: A dictionary
  245. The `get_extra_attrs_for_row` method allows you to add html attributes to
  246. the opening `<tr>` tag for each result, in addition to the `data-object_pk` and
  247. `class` attributes already added by the `result_row_display` tag.
  248. If you want to add additional CSS classes, simply provide those class names
  249. as a string value using the `class` key, and the `odd`/`even` will be appended
  250. to your custom class names when rendering.
  251. For example, if you wanted to add some additional class names based on field
  252. values, you could do something like:
  253. .. code-block:: python
  254. from decimal import Decimal
  255. from django.db import models
  256. from wagtail.contrib.modeladmin.options import ModelAdmin
  257. class BankAccount(models.Model):
  258. name = models.CharField(max_length=50)
  259. account_number = models.CharField(max_length=50)
  260. balance = models.DecimalField(max_digits=5, num_places=2)
  261. class BankAccountAdmin(ModelAdmin):
  262. list_display = ('name', 'account_number', 'balance')
  263. def get_extra_attrs_for_row(self, obj, context):
  264. if obj.balance < Decimal('0.00'):
  265. classname = 'balance-negative'
  266. else:
  267. classname = 'balance-positive'
  268. return {
  269. 'class': classname,
  270. }
  271. .. _modeladmin_get_extra_class_names_for_field_col:
  272. ----------------------------------------------------
  273. ``ModelAdmin.get_extra_class_names_for_field_col()``
  274. ----------------------------------------------------
  275. **Must return**: A list
  276. The ``get_extra_class_names_for_field_col`` method allows you to add additional
  277. CSS class names to any of the columns defined by ``list_display`` for your
  278. model. The method takes two parameters:
  279. - ``obj``: the object being represented by the current row
  280. - ``field_name``: the item from ``list_display`` being represented by the
  281. current column
  282. For example, if you'd like to apply some conditional formatting to a cell
  283. depending on the row's value, you could do something like:
  284. .. code-block:: python
  285. from decimal import Decimal
  286. from django.db import models
  287. from wagtail.contrib.modeladmin.options import ModelAdmin
  288. class BankAccount(models.Model):
  289. name = models.CharField(max_length=50)
  290. account_number = models.CharField(max_length=50)
  291. balance = models.DecimalField(max_digits=5, num_places=2)
  292. class BankAccountAdmin(ModelAdmin):
  293. list_display = ('name', 'account_number', 'balance')
  294. def get_extra_class_names_for_field_col(self, obj, field_name):
  295. field_name == 'balance':
  296. if balance <= Decimal('-100.00'):
  297. return ['brand-danger']
  298. if balance <= Decimal('-0.00'):
  299. return ['brand-warning']
  300. if balance <= Decimal('-50.00'):
  301. return ['brand-info']
  302. else:
  303. return ['brand-success']
  304. return []
  305. .. _modeladmin_get_extra_attrs_for_field_col:
  306. ----------------------------------------------------
  307. ``ModelAdmin.get_extra_attrs_for_field_col()``
  308. ----------------------------------------------------
  309. **Must return**: A dictionary
  310. The ``get_extra_attrs_for_field_col`` method allows you to add additional HTML
  311. attributes to any of the columns defined in ``list_display``. Like the
  312. ``get_extra_class_names_for_field_col`` method above, this method takes two
  313. parameters:
  314. - ``obj``: the object being represented by the current row
  315. - ``field_name``: the item from ``list_display`` being represented by the
  316. current column
  317. For example, you might like to add some tooltip text to a certain column, to
  318. help give the value more context:
  319. .. code-block:: python
  320. from django.db import models
  321. from wagtail.contrib.modeladmin.options import ModelAdmin
  322. class Person(models.Model):
  323. name = models.CharField(max_length=100)
  324. likes_cat_gifs = models.NullBooleanField()
  325. class PersonAdmin(ModelAdmin):
  326. model = Person
  327. list_display = ('name', 'likes_cat_gifs')
  328. def get_extra_attrs_for_field_col(self, obj, field_name=None):
  329. attrs = super(PersonAdmin, self).get_extra_attrs_for_field_col(obj, field_name)
  330. if field_name == 'likes_cat_gifs' and obj.likes_cat_gifs is None:
  331. attrs.update({
  332. 'title': (
  333. 'The person was shown several cat gifs, but failed to '
  334. 'indicate a preference.'
  335. ),
  336. })
  337. return attrs
  338. Or you might like to add one or more data attributes to help implement some
  339. kind of interactivity using javascript:
  340. .. code-block:: python
  341. from django.db import models
  342. from wagtail.contrib.modeladmin.options import ModelAdmin
  343. class Event(models.Model):
  344. title = models.CharField(max_length=255)
  345. start_date = models.DateField()
  346. end_date = models.DateField()
  347. start_time = models.TimeField()
  348. end_time = models.TimeField()
  349. class EventAdmin(ModelAdmin):
  350. model = Event
  351. list_display = ('title', 'start_date', 'end_date')
  352. def get_extra_attrs_for_field_col(self, obj, field_name=None):
  353. attrs = super(EventAdmin, self).get_extra_attrs_for_field_col(obj, field_name)
  354. if field_name == 'start_date':
  355. # Add the start time as data to the 'start_date' cell
  356. attrs.update({ 'data-time': obj.start_time.strftime('%H:%M') })
  357. elif field_name == 'end_date':
  358. # Add the end time as data to the 'end_date' cell
  359. attrs.update({ 'data-time': obj.end_time.strftime('%H:%M') })
  360. return attrs
  361. .. _modeladmin_thumbnailmixin:
  362. ----------------------------------------------------
  363. ``wagtail.contrib.modeladmin.mixins.ThumbnailMixin``
  364. ----------------------------------------------------
  365. If you're using ``wagtailimages.Image`` to define an image for each item in
  366. your model, ``ThumbnailMixin`` can help you add thumbnail versions of that
  367. image to each row in ``IndexView``. To use it, simply extend ``ThumbnailMixin``
  368. as well as ``ModelAdmin`` when defining your ``ModelAdmin`` class, and
  369. change a few attributes to change the thumbnail to your liking, like so:
  370. .. code-block:: python
  371. from django.db import models
  372. from wagtail.contrib.modeladmin.mixins import ThumbnailMixin
  373. from wagtail.contrib.modeladmin.options import ModelAdmin
  374. class Person(models.Model):
  375. name = models.CharField(max_length=255)
  376. avatar = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, null=True)
  377. likes_cat_gifs = models.NullBooleanField()
  378. class PersonAdmin(ThumbnailMixin, ModelAdmin):
  379. # Add 'admin_thumb' to list_display, where you want the thumbnail to appear
  380. list_display = ('admin_thumb', 'name', 'likes_cat_gifs')
  381. # Optionally tell IndexView to add buttons to a different column (if the
  382. # first column contains the thumbnail, the buttons are likely better off
  383. # displayed elsewhere)
  384. list_display_add_buttons = 'name'
  385. """
  386. Set 'thumb_image_field_name' to the name of the ForeignKey field that
  387. links to 'wagtailimages.Image'
  388. """
  389. thumb_image_field_name = 'avatar'
  390. # Optionally override the filter spec used to create each thumb
  391. thumb_image_filter_spec = 'fill-100x100' # this is the default
  392. # Optionally override the 'width' attribute value added to each img tag
  393. thumb_image_width = 50 # this is the default
  394. # Optionally override the class name added to each img tag
  395. thumb_classname = 'admin-thumb' # this is the default
  396. # Optionally override the text that appears in the column header
  397. thumb_col_header_text = 'image' # this is the default
  398. # Optionally specify a fallback image to be used when the object doesn't
  399. # have an image set, or the image has been deleted. It can an image from
  400. # your static files folder, or an external URL.
  401. thumb_default = 'http://lorempixel.com/100/100'
  402. .. _modeladmin_list_display_add_buttons:
  403. ---------------------------------------
  404. ``ModelAdmin.list_display_add_buttons``
  405. ---------------------------------------
  406. **Expected value**: A string matching one of the items in ``list_display``.
  407. If for any reason you'd like to change which column the action buttons appear
  408. in for each row, you can specify a different column using
  409. ``list_display_add_buttons`` on your ``ModelAdmin`` class. The value must
  410. match one of the items your class's ``list_display`` attribute. By default,
  411. buttons are added to the first column of each row.
  412. See the ``ThumbnailMixin`` example above to see how
  413. ``list_display_add_buttons`` can be used.
  414. .. _modeladmin_index_view_extra_css:
  415. -----------------------------------
  416. ``ModelAdmin.index_view_extra_css``
  417. -----------------------------------
  418. **Expected value**: A list of path names of additional stylesheets to be added
  419. to the ``IndexView``
  420. See the following part of the docs to find out more:
  421. :ref:`modeladmin_adding_css_and_js`
  422. .. _modeladmin_index_view_extra_js:
  423. -----------------------------------
  424. ``ModelAdmin.index_view_extra_js``
  425. -----------------------------------
  426. **Expected value**: A list of path names of additional js files to be added
  427. to the ``IndexView``
  428. See the following part of the docs to find out more:
  429. :ref:`modeladmin_adding_css_and_js`
  430. .. _modeladmin_index_template_name:
  431. ---------------------------------------
  432. ``ModelAdmin.index_template_name``
  433. ---------------------------------------
  434. **Expected value**: The path to a custom template to use for ``IndexView``
  435. See the following part of the docs to find out more:
  436. :ref:`modeladmin_overriding_templates`
  437. .. _modeladmin_index_view_class:
  438. ---------------------------------------
  439. ``ModelAdmin.index_view_class``
  440. ---------------------------------------
  441. **Expected value**: A custom ``view`` class to replace
  442. ``modeladmin.views.IndexView``
  443. See the following part of the docs to find out more:
  444. :ref:`modeladmin_overriding_views`