from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.template.defaultfilters import title from perfieldperms.models import PerFieldPermission def get_m2m_remote_fields(src_model, tgt_model): """ Get a list of fields on `tgt_model` that are part of a m2m relationship from `src_model`. """ return [ field for field in src_model._meta.get_fields() if (field.is_relation and field.many_to_many and field.related_model == tgt_model) ] def get_manager_name(src_model, tgt_model): """ Get the name of the manager for an m2m relationship from `src_model` to `tgt_model`. Return `None` if there is no relationship. """ fields = get_m2m_remote_fields(src_model, tgt_model) if fields: return fields[0].name return None def get_non_pfp_perms(content_type): """Get all model level Permission objects for `content_type`.""" return Permission.objects.filter( content_type=content_type, perfieldpermission__isnull=True, ) def list_fields_for_model(model): """ Get a list of (name, verbose_name) tuples of concrete fields for `model` which is a subclass of django.db.models.Model. """ return [ (field.name, title(field.verbose_name)) for field in model._meta.get_fields() if not field.auto_created # or field.concrete ] def list_fields_for_ctype(content_type): """ Get a list of (name, verbose_name) tuples of concrete fields from a model, via its ContentType. """ if not isinstance(content_type, ContentType): raise TypeError("""content_type must be an instance of ContentType.""") model = content_type.model_class() return list_fields_for_model(model) def disable_fields(request, model, obj, form, perm=None): """ Disable the fields in `form` which the user from `request` doesn't have permission to access. Use `perm` to determine applicable model level permission, or state of `obj` if `perm` is not set. """ # Superusers get all the fields if request.user.is_superuser: return form # If a permission was passed in use that. Expect a model level # permission. If obj is None assume we're adding, otherwise we're editing c_type = ContentType.objects.get_for_model(model) if perm is None: if obj is None: perm = Permission.objects.get( content_type=c_type, codename='add_{}'.format(c_type.model)) else: perm = Permission.objects.get( content_type=c_type, codename='change_{}'.format(c_type.model)) # If the user has any PFPS allocated we apply them, otherwise let model # level permissions apply user_query = get_manager_name(Permission, get_user_model()) pfps = PerFieldPermission.objects.filter(**{ 'model_permission': perm, user_query: request.user, }).values_list('field_name', flat=True) if pfps: for fname, field in form.base_fields.items(): if fname not in pfps: field.disabled = True field.help_text = ('This field is disabled as you lack required permissions.') return form