""" backend/apps/core/mixins.py """ from urllib.parse import parse_qsl from django.contrib.admin.widgets import RelatedFieldWidgetWrapper from django.db import models from django.utils.safestring import mark_safe from guardian.admin import GuardedModelAdmin from import_export.admin import ImportExportModelAdmin from simple_history.admin import SimpleHistoryAdmin from unfold.admin import ModelAdmin, StackedInline, TabularInline from unfold.contrib.import_export.forms import ImportForm, SelectableFieldsExportForm from unfold.decorators import display from unfold.widgets import UnfoldAdminSingleDateWidget from .widgets import WysiwygWidget class SoeAdminMixin(ModelAdmin, SimpleHistoryAdmin, GuardedModelAdmin, ImportExportModelAdmin): """ This class represents a base admin interface mixin. """ list_per_page = 200 import_form_class = ImportForm export_form_class = SelectableFieldsExportForm list_fullwidth = True warn_unsaved_form = True formfield_overrides = { models.TextField: { "widget": WysiwygWidget, }, models.DateField: { "widget": UnfoldAdminSingleDateWidget( # format="%m-%d-%Y", attrs={"placeholder": "MM-DD-YYYY"}, ), # "input_formats": ["%m-%d-%Y"], }, } @display(description="Notes", ordering="notes") def display_notes(self, obj): """Display notes as HTML in list view.""" if obj.notes: return mark_safe(obj.notes) return "-" @display(description="Address", ordering="address") def display_address(self, obj): """Display address as HTML in list view.""" if obj.address: return mark_safe(obj.address) return "-" def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) for _field_name, form_field in form.base_fields.items(): widget = form_field.widget # Check if the widget is a RelatedFieldWidgetWrapper instance if isinstance(widget, RelatedFieldWidgetWrapper): widget.can_add_related = False widget.can_change_related = False widget.can_delete_related = False widget.can_view_related = False return form def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} obj = self.get_object(request, object_id) # Get the base queryset qs = self.get_queryset(request) # Get and parse filters from the request changelist_filters = request.GET.get("_changelist_filters", "") if changelist_filters: try: # Parse the URL-encoded filter string filter_dict = dict(parse_qsl(changelist_filters)) # Apply each filter to the queryset qs = qs.filter(**filter_dict) except Exception as e: # Log error but continue with unfiltered queryset print(f"Error applying filters: {e}") # Get the ordering from the request ordering = request.GET.get("o") # Django's default ordering parameter if ordering: try: # Convert ordering parameter to field names order_fields = [] for field_index in ordering.split("."): try: index = int(field_index) # Get the actual field from list_display field = self.get_list_display(request)[abs(index)] # If index is negative, it's descending order if index < 0: field = "-" + field order_fields.append(field) except (ValueError, IndexError): continue if order_fields: qs = qs.order_by(*order_fields) except Exception as e: print(f"Error applying ordering: {e}") else: # Use default ordering from model or admin ordering = self.get_ordering(request) or () qs = qs.order_by(*ordering) if ordering else qs.order_by("name") # Get the list of IDs in the correct order ordered_ids = list(qs.values_list("id", flat=True)) try: # Find current position current_position = ordered_ids.index(obj.pk) # Get prev/next objects prev_obj = None if current_position == 0 else self.model.objects.get(pk=ordered_ids[current_position - 1]) next_obj = None if current_position == len(ordered_ids) - 1 else self.model.objects.get(pk=ordered_ids[current_position + 1]) except (ValueError, IndexError): prev_obj = next_obj = None # Add navigation context extra_context.update({ "prev_obj": prev_obj, "next_obj": next_obj, "has_prev": bool(prev_obj), "has_next": bool(next_obj), "opts": self.model._meta, "changelist_filters": changelist_filters, }) return super().change_view(request, object_id, form_url, extra_context=extra_context)