README.rst 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. ========================
  2. django-changelist-inline
  3. ========================
  4. Inline Changelists for Django
  5. Overview
  6. ========
  7. Django Admin comes with inlines_ which allows you to display forms to edit
  8. related objects from the parent object's change form. While it's technically
  9. possible to use this infrastructure to display list of related objects, it
  10. gets hacky pretty fast.
  11. Here's where *django-changelist-inline* comes into play. It allows you to embed
  12. the standard list of objects as an inline on the parent object's change form.
  13. Internally, it uses a subclass of ``ModelAdmin``, which means you can customize
  14. the list as you'd normally do. Additionally, you can display links below the
  15. list of objects.
  16. Usage Example
  17. =============
  18. Let's assume you have a Django project with two models - ``Thing`` and
  19. ``RelatedThing`` and that you want to display list of ``RelatedThing`` objects
  20. as an inline in ``Thing`` change form. The follwing snippet provides an example
  21. of how to implement that using *django-changelist-inline*:
  22. .. code-block:: python
  23. # -*- coding: utf-8 -*-
  24. from urllib.parse import urlencode
  25. from django.contrib import admin
  26. from django.urls import reverse
  27. from django.utils.safestring import mark_safe
  28. from django_changelist_inline import (
  29. ChangelistInline,
  30. ChangelistInlineAdmin,
  31. ChangelistInlineModelAdmin,
  32. )
  33. from testing.models import RelatedThing, Thing
  34. @admin.register(RelatedThing)
  35. class RelatedThingModelAdmin(admin.ModelAdmin):
  36. list_display = ('pk', 'name')
  37. class RelatedThingChangelistInline(ChangelistInline):
  38. model = RelatedThing
  39. class ChangelistModelAdmin(ChangelistInlineModelAdmin):
  40. list_display = ('name', 'format_actions')
  41. list_display_links = None
  42. list_per_page = 5
  43. def get_queryset(self, request):
  44. return RelatedThing.objects.filter(thing=self.parent_instance)
  45. @mark_safe
  46. def format_actions(self, obj=None):
  47. if obj is None:
  48. return self.empty_value_display
  49. change_link_url = reverse(
  50. 'admin:{app_label}_{model_name}_change'.format(
  51. app_label=RelatedThing._meta.app_label,
  52. model_name=RelatedThing._meta.model_name,
  53. ),
  54. args=[obj.pk],
  55. )
  56. return (
  57. f'<a class="button" href="{change_link_url}">'
  58. 'Edit'
  59. '</a>'
  60. )
  61. format_actions.short_description = 'Actions'
  62. @property
  63. def title(self):
  64. return 'Linked Related Things'
  65. @property
  66. def no_results_message(self):
  67. return 'No Related Things?'
  68. def get_add_url(self, request):
  69. result = super().get_add_url(request)
  70. if result is not None:
  71. return result + '?' + urlencode({
  72. 'thing': self.parent_instance.pk,
  73. })
  74. return result
  75. def get_show_all_url(self, request):
  76. result = super().get_show_all_url(request)
  77. if result is not None:
  78. return result + '?' + urlencode({
  79. 'thing': self.parent_instance.pk,
  80. })
  81. return result
  82. def get_toolbar_links(self, request):
  83. return (
  84. '<a href="https://www.bthlabs.pl/">'
  85. 'BTHLabs'
  86. '</a>'
  87. )
  88. @admin.register(Thing)
  89. class ThingModelAdmin(ChangelistInlineAdmin):
  90. inlines = (RelatedThingChangelistInline,)
  91. API
  92. ===
  93. ``ChangelistInline`` objects
  94. ----------------------------
  95. The ``ChangelistInline`` class is the center piece of the API. It's
  96. designed to be used in a ``ModelAdmin``'s ``inlines``.
  97. In order for it to work, you'll need to define the ``model`` property and
  98. embed ``ChangelistModelAdmin`` class, which should be a subclass of
  99. ``ChangelistInlineModelAdmin``.
  100. ``ChangelistInlineModelAdmin`` objects
  101. --------------------------------------
  102. The ``ChangelistInlineModelAdmin`` is a customized ``ModelAdmin`` subclass
  103. which provides sane defaults and additional functionality for inline
  104. changelists.
  105. **Changelist sanitization**
  106. This subclass overrides the following attributes and methods of ``ModelAdmin``
  107. to provide sane defaults:
  108. * ``list_editable`` - set to empty tuple to disable editing of the list,
  109. * ``list_filter`` - set to empty tuple to disable filtering of the list,
  110. * ``search_fields`` - set to empty tuple to disable searching,
  111. * ``date_hierarchy`` - set to ``None``,
  112. * ``sortable_by`` - set to empty tuple to disable sorting,
  113. * ``get_actions()`` - returns empty list to disable actions.
  114. **Additional functionality**
  115. To allow customization and to expose additional functionality,
  116. ``ChangelistInlineModelAdmin`` provides the following additional methods:
  117. * ``title`` property - returns the model's *verbose name* by default.
  118. * ``no_results_message`` property - returns text to display in place of the
  119. table if no objects are fetched from the DB.
  120. * ``get_add_url(request)`` - returns URL for the model's add form, if the
  121. user has the add permission. Return ``None`` to hide the add link.
  122. * ``get_show_all_url(request)`` - returns URL for the model's changelist, if
  123. the user has the view permission. Return ``None`` to hide the show all link.
  124. * ``get_toolbar_links(request)`` - returns ``None`` by default. Override this
  125. to return string with additional ``<a/>`` elements to render in the toolbar.
  126. The return value is marked safe in the template.
  127. ``ChangelistInlineAdmin`` objects
  128. ---------------------------------
  129. The ``ChangelistInlineAdmin`` class is a base class for ``ModelAdmin``
  130. subclasses that use inline changelists.
  131. ``ChangelistInlineAdminMixin``
  132. ------------------------------
  133. A mixin class that is used to properly configure changelist inlines in the
  134. parent ``ModelAdmin``. Overrides ``get_inlines(request, obj=None)`` and
  135. ``get_inline_instances(request, obj=None)`` methods.
  136. If you can't use ``ChangelistInlineAdmin`` as you base class, you can use this
  137. mixin to enable inline changelists:
  138. .. code-block:: python
  139. @admin.register(Thing)
  140. class ThingModelAdmin(ChangelistInlineAdminMixin, MyBaseModelAdmin):
  141. ...
  142. Author
  143. ------
  144. *django-changelist-inline* is developed by `Tomek Wójcik`_.
  145. License
  146. -------
  147. *django-changelist-inline* is licensed under the MIT License.
  148. .. _inlines: https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#inlinemodeladmin-objects
  149. .. _Tomek Wójcik: https://www.bthlabs.pl/