Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
dj-kaos / utils / admin / mixins.py
Size: Mime:
from __future__ import annotations

from typing import Sequence

from django.contrib import admin
from django.contrib.admin.options import BaseModelAdmin
from django.contrib.auth import get_user_model
from django_object_actions import BaseDjangoObjectActions


class AreYouSureActionsAdminMixin(BaseDjangoObjectActions):
    """
    Add a confirmation prompt to the certain object actions defined in :attr:`are_you_sure_actions`.

    :param are_you_sure_actions: sequence of object actions that require confirmation upon clicking.
    :param are_you_sure_prompt_f: the template of the message shown in the confirmation prompt.

    Example:
        >>> class MyModelAdmin(AreYouSureActionsAdminMixin, admin.ModelAdmin):
        >>>     are_you_sure_actions = ('archive',)
    """
    are_you_sure_actions = ()
    are_you_sure_prompt_f = "Are you sure you want to {label} this object?"

    def __init__(self, *args, **kwargs):
        super(AreYouSureActionsAdminMixin, self).__init__(*args, **kwargs)
        for action in self.are_you_sure_actions:
            tool = getattr(self, action)
            label = getattr(tool, 'label', action).lower()
            are_you_sure_prompt = self.are_you_sure_prompt_f.format(tool=tool, label=label)
            tool.__dict__.setdefault('attrs', {})
            tool.__dict__['attrs'].setdefault('onclick', f"""return confirm("{are_you_sure_prompt}");""")


class ExcludeFromNonSuperusersMixin(BaseModelAdmin):
    """
    Admin mixin to make some fields hidden to non-superusers. Define such fields using
    :attr:`.exclude_from_non_superusers`, or dynamically by overriding :attr:`.get_exclude_from_non_superusers()`.

    :param exclude_from_non_superusers: sequence of field names that are not shown to non-superusers

    Example:
        >>> class MyModelAdmin(ExcludeFromNonSuperusersMixin, admin.ModelAdmin):
        >>>     exclude_from_non_superusers = ('is_superuser',)
    """
    exclude_from_non_superusers = ()

    def get_exclude_from_non_superusers(self, request, obj=None):
        """
        Return a sequence of field names that are not shown to non-superusers.
        By default, return `self.exclude_from_non_superusers`.
        """
        return self.exclude_from_non_superusers

    def get_exclude(self, request, obj=None):
        exclude = super(ExcludeFromNonSuperusersMixin, self).get_exclude(request, obj) or ()
        if request.user.is_superuser:
            return exclude
        return (
            *exclude,
            *self.get_exclude_from_non_superusers(request, obj),
        )


class FilterOutSuperusersAdminMixin(BaseModelAdmin):
    """
    A mixin for filtering out superusers from the foreign key and many-to-many fields in the admin interface.

    :param user_fields: List of field names with fk or m2m to User.
    """
    user_fields: Sequence[str] = ()

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if not request.user.is_superuser and db_field.name in self.user_fields:
            kwargs['queryset'] = get_user_model().objects.filter(is_superuser=False)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if not request.user.is_superuser and db_field.name in self.user_fields:
            kwargs['queryset'] = get_user_model().objects.filter(is_superuser=False)
        return super().formfield_for_manytomany(db_field, request, **kwargs)


class AutoUserAdminMixin(BaseModelAdmin):
    """
    Mixin to automatically set the owner field to the current user as the initial value of the form.

    :param user_field: The name of the field with the foreign key to user
    """
    user_field = 'user'

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        field = super().formfield_for_foreignkey(db_field, request, **kwargs)
        if db_field.name == self.user_field and field.initial is None:
            field.initial = request.user
        return field


class EditReadonlyAdminMixin(BaseModelAdmin):
    """
    Fields defined in :attr:`edit_readonly_fields` are editable upon creation, but are readonly on an existing object.
    Set :attr:`allow_superusers` to True to allow superusers to edit such fields even in existing objects.

    :param edit_readonly_fields: sequence of field names that can be entered during creation, but are readonly on an
        existing object.
    :param allow_superusers: Whether to allow superusers to edit `edit_readonly_fields` even on existing object. False
        by default.

    Example:
        >>> class MyModelAdmin(EditReadonlyAdminMixin, admin.ModelAdmin):
        >>>     edit_readonly_fields = ('slug',)
    """
    edit_readonly_fields = ()
    allow_superusers = False

    def get_edit_readonly_fields(self, request, obj=None):
        return self.edit_readonly_fields

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = super().get_readonly_fields(request, obj)
        if not obj:  # is in add form not edit
            return readonly_fields
        if self.allow_superusers and request.user.is_superuser:
            return readonly_fields
        return readonly_fields + self.get_edit_readonly_fields(request, obj)


class ExcludeFromFieldsetsMixin(BaseModelAdmin):
    """
    Admin mixin to make sure fields that are in :attr:`exclude` are removed from the :attr:`fieldsets` definition.
    By default, without this mixin, if a field defined in :attr:`fieldsets` is in :attr:`exclude`, Django throws an
    error complaining about a missing value for the field.

    Example:
        >>> class MyModelAdmin(ExcludeFromFieldsetsMixin, admin.ModelAdmin):
        >>>     pass
    """

    def get_fieldsets(self, request, obj=None):
        exclude = self.get_exclude(request, obj)
        fieldsets = super().get_fieldsets(request, obj) or ()
        if not exclude:
            return fieldsets
        return [
            (fieldset_name,
             {
                 **fieldset_dict,
                 'fields': [field for field in fieldset_dict['fields'] if field not in exclude]
             })
            for fieldset_name, fieldset_dict in fieldsets
        ]


__all__ = (
    'AreYouSureActionsAdminMixin',
    'ExcludeFromNonSuperusersMixin',
    'FilterOutSuperusersAdminMixin',
    'AutoUserAdminMixin',
    'EditReadonlyAdminMixin',
    'ExcludeFromFieldsetsMixin',
)