Skip to content

Instantly share code, notes, and snippets.

@niccolomineo
Last active September 10, 2020 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niccolomineo/7a572081798b90906cc01da7fd311d2b to your computer and use it in GitHub Desktop.
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
class FieldPermissionsMixin:
"""
Define a mixin handling read-only fields per group, admin model and form type.
!!!THIS IS JUST A STUB, 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