diff --git a/jdav_web/contrib/admin.py b/jdav_web/contrib/admin.py index 4346029..fa762d7 100644 --- a/jdav_web/contrib/admin.py +++ b/jdav_web/contrib/admin.py @@ -8,8 +8,10 @@ from django.http import HttpResponse, HttpResponseRedirect from django.urls import path, reverse from django.db import models from django.contrib.admin import helpers, widgets +from django.conf import settings import rules.contrib.admin from rules.permissions import perm_exists +from utils import OrderedSet class FieldPermissionsAdminMixin: @@ -174,6 +176,62 @@ class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, Filtere # For any other type of field, just call its formfield() method. return db_field.formfield(**kwargs) + + @property + def field_key(self): + return f"{self.model._meta.app_label}_{self.model.__name__}".lower() + + def get_excluded_fields(self): + return OrderedSet(settings.CUSTOM_MODEL_FIELDS.get(self.field_key, {}).get('exclude', [])) + + def get_included_fields(self): + return OrderedSet(settings.CUSTOM_MODEL_FIELDS.get(self.field_key, {}).get('fields', [])) + + + def get_fieldsets(self, request, obj=None): + included = self.get_included_fields() + excluded = self.get_excluded_fields() + original_fieldsets = super().get_fieldsets(request, obj) + if original_fieldsets: + print(f"get_fieldsets called for {self.field_key}") + print(f"Original fieldsets: {original_fieldsets}") + new_fieldsets = [] + + for title, attrs in original_fieldsets: + fields = attrs.get("fields", []) + + # Flatten groupings like tuples if needed + filtered_fields = [ + f for f in fields + if ( + (not included or f in included) + and f not in excluded + ) + ] + + if filtered_fields: + new_fieldsets.append((title, dict(attrs, **{"fields": filtered_fields}))) + + if new_fieldsets: + print(f"Filtered fieldsets: {new_fieldsets}") + return new_fieldsets + + + def get_fields(self, request, obj=None): + fields = OrderedSet(super().get_fields(request, obj) or []) + custom_fields = self.get_included_fields() - self.get_excluded_fields() + if custom_fields: + print(f"get_fields called for {self.field_key}, fields: {fields}, custom_fields: {custom_fields}") + return list(custom_fields) + return list(fields) + + def get_exclude(self, request, obj=None): + excluded = OrderedSet(super().get_exclude(request, obj) or []) + custom_excluded = self.get_excluded_fields() - self.get_included_fields() + if custom_excluded: + print(f"get_exclude called for {self.field_key}, excluded: {excluded}, custom_excluded: {custom_excluded}") + return list(excluded | custom_excluded) + return list(excluded) class CommonAdminInlineMixin(CommonAdminMixin): diff --git a/jdav_web/jdav_web/settings/local.py b/jdav_web/jdav_web/settings/local.py index 71e260c..6da4a84 100644 --- a/jdav_web/jdav_web/settings/local.py +++ b/jdav_web/jdav_web/settings/local.py @@ -76,3 +76,8 @@ REPORTS_SECTION = get_var('startpage', 'reports_section', default='reports') # testing TEST_MAIL = get_var('testing', 'mail', default='test@localhost') + + +# excluded and included model fields in admin and admin forms +CUSTOM_MODELS = list(get_var('custom_model_fields', default={}).keys()) +CUSTOM_MODEL_FIELDS = {model.lower(): get_var('model_fields', model, default=[]) for model in CUSTOM_MODELS} \ No newline at end of file diff --git a/jdav_web/utils.py b/jdav_web/utils.py index 129e495..439b0e9 100644 --- a/jdav_web/utils.py +++ b/jdav_web/utils.py @@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from decimal import Decimal, ROUND_HALF_DOWN import unicodedata +from collections import OrderedDict def file_size_validator(max_upload_size): @@ -88,3 +89,28 @@ def coming_midnight(): return timezone.datetime(year=base.year, month=base.month, day=base.day, hour=0, minute=0, second=0, microsecond=0, tzinfo=base.tzinfo) + + +class OrderedSet(OrderedDict): + def __init__(self, iterable=None): + super().__init__() + if iterable: + for item in iterable: + self[item] = None + + def __sub__(self, other): + if not isinstance(other, OrderedSet): + return NotImplemented + return OrderedSet(k for k in self if k not in other) + + def add(self, item): + self[item] = None + + def discard(self, item): + self.pop(item, None) + + def __contains__(self, item): + return item in self.keys() + + def __iter__(self): + return iter(self.keys()) \ No newline at end of file