Last active
August 27, 2024 11:51
-
-
Save niccolomineo/7a572081798b90906cc01da7fd311d2b to your computer and use it in GitHub Desktop.
A mixin handling read-only fields per group, admin model and form type in Django
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class FieldPermissionsMixin: | |
""" | |
Define a mixin handling read-only fields per group, admin model and form type. | |
!!!THIS IS JUST A DRAFT, AWAITING COMPLETION!!! | |
Read-only fields can be specified in a setting exemplified below. | |
For permission names, codenames without the model name are considered well-formed. | |
GROUPS = { | |
"{{group_name}}" : { | |
"{{model_name}}" : { | |
"permissions" : ("{{permission_name_1}}", "{{permission_name_2}}"), | |
"readonly_fields": { | |
"{{field_name}}": { | |
"list_display": "{{readonly_field_name}}", | |
"change_form": "{{readonly_field_name}}" | |
} | |
}, | |
... | |
}, | |
... | |
}, | |
.. | |
} | |
""" | |
def _get_readonly_fields_for_groups(self, group_names, form_type): | |
"""Return readonly fields for input groups.""" | |
model_label = self.model.__name__.lower() | |
readonly_fields = {} | |
for g in group_names: | |
dg = settings.GROUPS[g] | |
default_readonly_fields = dg.get(model_label, {}).get("readonly_fields", []) | |
for f in default_readonly_fields: | |
readonly_field_name = default_readonly_fields[f].get(form_type) | |
if readonly_field_name: | |
readonly_fields.update({f: readonly_field_name}) | |
return readonly_fields | |
def _replace_fields_in_collection(self, data, readonly_fields): | |
"""Walk through fields or fieldsets and replace field with readonly variant.""" | |
def _traverse_and_replace_data(data, readonly_fields): | |
"""Traverse and replace data.""" | |
if isinstance(data, list): | |
for d in data: | |
if isinstance(d, str): | |
if d in readonly_fields.keys(): | |
idx = data.index(d) | |
data[idx] = readonly_fields[d] | |
else: | |
_traverse_and_replace_data(d, readonly_fields) | |
if isinstance(data, dict): | |
for _k, v in data.items(): | |
_traverse_and_replace_data(v, readonly_fields) | |
_traverse_and_replace_data(data, readonly_fields) | |
return data | |
def get_fields(self, request, obj=None): | |
"""Return field list with additional readonly fields.""" | |
form_type = "change_form" | |
fields = json.loads(json.dumps(super().get_fields(request, obj))) | |
if fields: | |
group_names = request.user.groups.all().values_list("name", flat=True) | |
additional_readonly_fields = self._get_readonly_fields_for_groups( | |
group_names, form_type | |
) | |
if additional_readonly_fields: | |
fields = self._replace_fields_in_collection( | |
fields, additional_readonly_fields | |
) | |
return fields | |
def get_fieldsets(self, request, obj=None): | |
"""Return fieldset data structure with additional readonly fields.""" | |
form_type = "change_form" | |
fieldsets = json.loads(json.dumps(super().get_fieldsets(request, obj))) | |
if fieldsets: | |
group_names = request.user.groups.all().values_list("name", flat=True) | |
additional_readonly_fields = self._get_readonly_fields_for_groups( | |
group_names, form_type | |
) | |
if additional_readonly_fields: | |
fieldsets = self._replace_fields_in_collection( | |
fieldsets, additional_readonly_fields | |
) | |
return fieldsets | |
def get_readonly_fields(self, request, obj=None): | |
"""Return readonly field list with additional readonly fields.""" | |
form_type = "change_form" | |
readonly_fields = super().get_readonly_fields(request, obj) | |
group_names = request.user.groups.all().values_list("name", flat=True) | |
additional_readonly_fields = self._get_readonly_fields_for_groups( | |
group_names, form_type | |
) | |
if additional_readonly_fields: | |
for _k, v in additional_readonly_fields: | |
readonly_fields += v |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment