| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- from django.shortcuts import render
- from django.contrib import messages
- # Create your views here.
- from django_filters.views import FilterView
- from django.urls import reverse_lazy, reverse
- from django.views.generic import (
- ListView,
- DetailView,
- CreateView,
- UpdateView,
- DeleteView,
- )
- from django.core.paginator import Paginator
- class ConfigurableCRUDView:
- model = None
- list_template_name = None
- detail_template_name = None
- form_template_name = None
- confirm_delete_template_name = None
- fields = '__all__' # Default to all fields
- paginate_by = 10
- filterset_class = None
- page_title = "Page Title"
- create_url_name = None
- update_url_name = None
- delete_url_name = None
- list_url_name = None
- config_fields = "all" # "all" or a list of field names to display
- config_field_orders = [] # Fields to display first
- config_excludes = [] # Fields to exclude
- config_readonly_fields = [] # Fields that should be read-only in update view
- config_edit_fields = "__all__" # "all" or a list of field names to display
- ordering = None
- def get_fields(self):
- """
- Dynamically generate the fields to be displayed in the list view.
- - Includes all fields if `config_fields` is set to "all".
- - Respects `config_field_orders` and `config_excludes`.
- Returns field instances instead of field names.
- """
- model_fields = {f.name: f for f in self.model._meta.get_fields()}
- # Filter based on `config_fields` configuration
- fields = []
- if self.config_fields != "all":
- fields = [model_fields[f] for f in self.config_fields if f in model_fields]
- else:
- fields = list(model_fields.values())
- # pprint("------------------------")
- # pprint(f"fields = {fields}")
- # Exclude fields specified in `config_excludes`
- fields = [f for f in fields if f.name not in self.config_excludes]
- # Order fields based on `config_field_orders`
- ordered_fields = [f for f in fields if f.name in self.config_field_orders]
- remaining_fields = [f for f in fields if f.name not in self.config_field_orders]
- # Reorder fields to match the order specified in `config_field_orders`
- ordered_field_names = set(self.config_field_orders)
- 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))
- return ordered_fields + remaining_fields
- def get_breadcrumbs(self, view_type, obj=None):
- """
- Generate breadcrumbs dynamically based on the view type.
- :param view_type: The type of view (list, create, update, detail, delete)
- :param obj: The current object (if applicable)
- :return: A list of breadcrumb dictionaries
- """
- breadcrumbs = [
- {'label': 'Dashboard', 'url': reverse('dashboard:index')},
- {'label': self.page_title, 'url': reverse(self.list_url_name)} if self.list_url_name else None,
- ]
- if view_type == 'create':
- breadcrumbs.append({'label': f'Create {self.model._meta.verbose_name}', 'url': None})
- elif view_type == 'update' and obj:
- breadcrumbs.append({'label': obj, 'url': reverse(self.update_url_name, kwargs={'pk': obj.pk})})
- elif view_type == 'detail' and obj:
- breadcrumbs.append({'label': f'Detail of {obj}', 'url': None})
- elif view_type == 'delete' and obj:
- breadcrumbs.append({'label': f'Delete {obj}', 'url': None})
- return [b for b in breadcrumbs if b]
- def get_list_view(self):
- class ListViewClass(FilterView, ListView):
- model = self.model
- template_name = self.list_template_name
- paginate_by = self.paginate_by
- filterset_class = self.filterset_class
- ordering = self.ordering
- def get_context_data(inner_self, **kwargs):
- context = super().get_context_data(**kwargs)
- fields = self.get_fields()
- context.update({
- 'fields': [f for f in fields],
- # 'fields': [field for field in self.model._meta.get_fields()],
- 'page_title': self.page_title,
- 'list_url': self.list_url_name,
- 'create_url': self.create_url_name,
- 'update_url': self.update_url_name,
- 'delete_url': self.delete_url_name,
- 'bs': self.get_breadcrumbs('list'),
- })
- return context
- return ListViewClass
- def get_detail_view(self):
- class DetailViewClass(DetailView):
- model = self.model
- template_name = self.detail_template_name
- def get_context_data(inner_self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'page_title': self.page_title,
- 'bs': self.get_breadcrumbs('detail'),
- })
- return context
- return DetailViewClass
- def get_create_view(self):
- class CreateViewClass(CreateView):
- model = self.model
- template_name = self.form_template_name
- fields = self.config_edit_fields
- def form_valid(inner_self, form):
- response = super().form_valid(form)
- messages.success(inner_self.request, f"{self.model._meta.verbose_name} created successfully!")
- return response
- def get_success_url(inner_self):
- return reverse(self.update_url_name, kwargs={'pk': inner_self.object.pk})
- def get_context_data(inner_self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'list_url_name': self.list_url_name,
- 'page_title': f"Create {self.model._meta.verbose_name}",
- 'bs': self.get_breadcrumbs('update', obj=inner_self.object),
- })
- return context
- return CreateViewClass
- def get_update_view(self):
- class UpdateViewClass(UpdateView):
- model = self.model
- template_name = self.form_template_name
- fields = self.config_edit_fields
- def form_valid(inner_self, form):
- response = super().form_valid(form)
- messages.success(inner_self.request, f"{self.model._meta.verbose_name} updated successfully!")
- return response
- def get_form(inner_self, *args, **kwargs):
- """
- Customize the form to make specified fields read-only.
- """
- form = super().get_form(*args, **kwargs)
- for field_name in self.config_readonly_fields:
- if field_name in form.fields:
- form.fields[field_name].widget.attrs['readonly'] = True
- form.fields[field_name].widget.attrs['style'] = 'background-color: #f9f9f9; cursor: not-allowed;'
- form.fields[field_name].disabled = True # Optional: Completely disable the field
- return form
- def get_success_url(inner_self):
- return reverse(self.update_url_name, kwargs={'pk': inner_self.object.pk})
- def get_context_data(inner_self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'list_url_name': self.list_url_name,
- 'page_title': f"Update {self.model._meta.verbose_name}",
- 'bs': self.get_breadcrumbs('update', obj=inner_self.object),
- })
- return context
- return UpdateViewClass
- def get_delete_view(self):
- class DeleteViewClass(DeleteView):
- model = self.model
- template_name = self.confirm_delete_template_name
- success_url = reverse_lazy(self.delete_url_name)
- def get_context_data(inner_self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'list_url_name': self.list_url_name,
- 'page_title': f"Delete {self.model._meta.verbose_name}",
- 'bs': self.get_breadcrumbs('delete', obj=inner_self.object),
- })
- return context
- return DeleteViewClass
|