Mixin for views that need superadmin access to all resources.
Provides FOUR key features (all bundled - no separate mixins needed):
Inherits from:
Config attributes (inherited from ObjectOwnershipMixin):
Config attributes (superadmin-only fields):
superadmin_only_fields: List of field names that only superadmins can modify (default: []) On CREATE: Fields are stripped from non-superadmin requests (model defaults apply) On UPDATE: Non-superadmins trying to change these fields get PermissionDenied
superadmin_lock_field: Field name that locks the entire object for non-superadmins (default: None) When this field is truthy on the instance, non-superadmins cannot modify ANY field. Common use case: is_managed=True means Keywords AI manages this resource, users can’t edit it.
Note: Writing to global objects (ownership field is None) always requires superadmin.
Usage (detail view):
class MyDetailView(SuperAdminMixin, JWTAndAPIKeyAuthenticationViewMixin, RetrieveUpdateDestroyAPIView):
is_allowing_global_object_read = True
def get_regular_user_queryset(self): return MyModel.objects.filter(organization_id=self.request.user.curr_org_id)
def get_superadmin_queryset(self): return MyModel.objects.all()
Usage (superadmin-only fields):
class IntegrationView(SuperAdminMixin, JWTAndAPIKeyAuthenticationViewMixin, RetrieveUpdateDestroyAPIView): superadmin_only_fields = [‘is_managed’] # Only superadmins can modify is_managed superadmin_lock_field = ‘is_managed’ # When is_managed=True, object is locked for non-superadmins
def get_regular_user_queryset(self): return Integration.objects.filter(organization_id=self.request.user.curr_org_id)
def get_superadmin_queryset(self): return Integration.objects.all()
Usage (related-object org pattern like PromptVersion - PREFERRED: annotate queryset):
from django.db.models import F
class PromptVersionView(SuperAdminMixin, JWTAndAPIKeyAuthenticationViewMixin, RetrieveUpdateDestroyAPIView): def get_regular_user_queryset(self):
return PromptVersion.objects.filter( prompt__organization_id=self.request.user.curr_org_id ).annotate(organization_id=F(“prompt__organization_id”))
def get_superadmin_queryset(self): return PromptVersion.objects.annotate(organization_id=F(“prompt__organization_id”))
Alternative (override method - only if annotation not possible):
class PromptVersionView(SuperAdminMixin, JWTAndAPIKeyAuthenticationViewMixin, RetrieveUpdateDestroyAPIView): def get_affiliated_object_organization_id(self, instance): return instance.prompt.organization_id # Org is on parent object
DO NOT use inline checks like this:
def get_queryset(self): if has_staff_role(self.request.user): return MyModel.objects.all() return MyModel.objects.filter(…)