Keine Beschreibung

utils.py 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from django.shortcuts import render
  2. from django.contrib import messages
  3. # Create your views here.
  4. from django_filters.views import FilterView
  5. from django.urls import reverse_lazy, reverse
  6. from django.views.generic import (
  7. ListView,
  8. DetailView,
  9. CreateView,
  10. UpdateView,
  11. DeleteView,
  12. )
  13. from django.core.paginator import Paginator
  14. class ConfigurableCRUDView:
  15. model = None
  16. list_template_name = None
  17. detail_template_name = None
  18. form_template_name = None
  19. confirm_delete_template_name = None
  20. fields = '__all__' # Default to all fields
  21. paginate_by = 10
  22. filterset_class = None
  23. page_title = "Page Title"
  24. create_url_name = None
  25. update_url_name = None
  26. delete_url_name = None
  27. list_url_name = None
  28. config_fields = "all" # "all" or a list of field names to display
  29. config_field_orders = [] # Fields to display first
  30. config_excludes = [] # Fields to exclude
  31. config_readonly_fields = [] # Fields that should be read-only in update view
  32. config_edit_fields = "__all__" # "all" or a list of field names to display
  33. ordering = None
  34. def get_fields(self):
  35. """
  36. Dynamically generate the fields to be displayed in the list view.
  37. - Includes all fields if `config_fields` is set to "all".
  38. - Respects `config_field_orders` and `config_excludes`.
  39. Returns field instances instead of field names.
  40. """
  41. model_fields = {f.name: f for f in self.model._meta.get_fields()}
  42. # Filter based on `config_fields` configuration
  43. fields = []
  44. if self.config_fields != "all":
  45. fields = [model_fields[f] for f in self.config_fields if f in model_fields]
  46. else:
  47. fields = list(model_fields.values())
  48. # pprint("------------------------")
  49. # pprint(f"fields = {fields}")
  50. # Exclude fields specified in `config_excludes`
  51. fields = [f for f in fields if f.name not in self.config_excludes]
  52. # Order fields based on `config_field_orders`
  53. ordered_fields = [f for f in fields if f.name in self.config_field_orders]
  54. remaining_fields = [f for f in fields if f.name not in self.config_field_orders]
  55. # Reorder fields to match the order specified in `config_field_orders`
  56. ordered_field_names = set(self.config_field_orders)
  57. ordered_fields.sort(key=lambda f: self.config_field_orders.index(f.name) if f.name in ordered_field_names else len(ordered_field_names))
  58. return ordered_fields + remaining_fields
  59. def get_breadcrumbs(self, view_type, obj=None):
  60. """
  61. Generate breadcrumbs dynamically based on the view type.
  62. :param view_type: The type of view (list, create, update, detail, delete)
  63. :param obj: The current object (if applicable)
  64. :return: A list of breadcrumb dictionaries
  65. """
  66. breadcrumbs = [
  67. {'label': 'Dashboard', 'url': reverse('dashboard:index')},
  68. {'label': self.page_title, 'url': reverse(self.list_url_name)} if self.list_url_name else None,
  69. ]
  70. if view_type == 'create':
  71. breadcrumbs.append({'label': f'Create {self.model._meta.verbose_name}', 'url': None})
  72. elif view_type == 'update' and obj:
  73. breadcrumbs.append({'label': obj, 'url': reverse(self.update_url_name, kwargs={'pk': obj.pk})})
  74. elif view_type == 'detail' and obj:
  75. breadcrumbs.append({'label': f'Detail of {obj}', 'url': None})
  76. elif view_type == 'delete' and obj:
  77. breadcrumbs.append({'label': f'Delete {obj}', 'url': None})
  78. return [b for b in breadcrumbs if b]
  79. def get_list_view(self):
  80. class ListViewClass(FilterView, ListView):
  81. model = self.model
  82. template_name = self.list_template_name
  83. paginate_by = self.paginate_by
  84. filterset_class = self.filterset_class
  85. ordering = self.ordering
  86. def get_context_data(inner_self, **kwargs):
  87. context = super().get_context_data(**kwargs)
  88. fields = self.get_fields()
  89. context.update({
  90. 'fields': [f for f in fields],
  91. # 'fields': [field for field in self.model._meta.get_fields()],
  92. 'page_title': self.page_title,
  93. 'list_url': self.list_url_name,
  94. 'create_url': self.create_url_name,
  95. 'update_url': self.update_url_name,
  96. 'delete_url': self.delete_url_name,
  97. 'bs': self.get_breadcrumbs('list'),
  98. })
  99. return context
  100. return ListViewClass
  101. def get_detail_view(self):
  102. class DetailViewClass(DetailView):
  103. model = self.model
  104. template_name = self.detail_template_name
  105. def get_context_data(inner_self, **kwargs):
  106. context = super().get_context_data(**kwargs)
  107. context.update({
  108. 'page_title': self.page_title,
  109. 'bs': self.get_breadcrumbs('detail'),
  110. })
  111. return context
  112. return DetailViewClass
  113. def get_create_view(self):
  114. class CreateViewClass(CreateView):
  115. model = self.model
  116. template_name = self.form_template_name
  117. fields = self.config_edit_fields
  118. def form_valid(inner_self, form):
  119. response = super().form_valid(form)
  120. messages.success(inner_self.request, f"{self.model._meta.verbose_name} created successfully!")
  121. return response
  122. def get_success_url(inner_self):
  123. return reverse(self.update_url_name, kwargs={'pk': inner_self.object.pk})
  124. def get_context_data(inner_self, **kwargs):
  125. context = super().get_context_data(**kwargs)
  126. context.update({
  127. 'list_url_name': self.list_url_name,
  128. 'page_title': f"Create {self.model._meta.verbose_name}",
  129. 'bs': self.get_breadcrumbs('update', obj=inner_self.object),
  130. })
  131. return context
  132. return CreateViewClass
  133. def get_update_view(self):
  134. class UpdateViewClass(UpdateView):
  135. model = self.model
  136. template_name = self.form_template_name
  137. fields = self.config_edit_fields
  138. def form_valid(inner_self, form):
  139. response = super().form_valid(form)
  140. messages.success(inner_self.request, f"{self.model._meta.verbose_name} updated successfully!")
  141. return response
  142. def get_form(inner_self, *args, **kwargs):
  143. """
  144. Customize the form to make specified fields read-only.
  145. """
  146. form = super().get_form(*args, **kwargs)
  147. for field_name in self.config_readonly_fields:
  148. if field_name in form.fields:
  149. form.fields[field_name].widget.attrs['readonly'] = True
  150. form.fields[field_name].widget.attrs['style'] = 'background-color: #f9f9f9; cursor: not-allowed;'
  151. form.fields[field_name].disabled = True # Optional: Completely disable the field
  152. return form
  153. def get_success_url(inner_self):
  154. return reverse(self.update_url_name, kwargs={'pk': inner_self.object.pk})
  155. def get_context_data(inner_self, **kwargs):
  156. context = super().get_context_data(**kwargs)
  157. context.update({
  158. 'list_url_name': self.list_url_name,
  159. 'page_title': f"Update {self.model._meta.verbose_name}",
  160. 'bs': self.get_breadcrumbs('update', obj=inner_self.object),
  161. })
  162. return context
  163. return UpdateViewClass
  164. def get_delete_view(self):
  165. class DeleteViewClass(DeleteView):
  166. model = self.model
  167. template_name = self.confirm_delete_template_name
  168. success_url = reverse_lazy(self.delete_url_name)
  169. def get_context_data(inner_self, **kwargs):
  170. context = super().get_context_data(**kwargs)
  171. context.update({
  172. 'list_url_name': self.list_url_name,
  173. 'page_title': f"Delete {self.model._meta.verbose_name}",
  174. 'bs': self.get_breadcrumbs('delete', obj=inner_self.object),
  175. })
  176. return context
  177. return DeleteViewClass