I would like to write simple code to filter records in view based on request information (eg. organization the user belongs to).
I started to implemented it as Mixin for Admin views.
class OrganizationPermissionMixin:
def get_queryset(self, request):
query = super().get_queryset(request)
if request.user.is_superuser:
return query
return query.filter(
organization__in=request.user.organization_set.all()
)
This works fine but when I tried to apply this Mixin on Generic views, I have a signature error as there is no request parameter passed to the get_queryset
method:
TypeError: OrganizationPermissionMixin.get_queryset() missing 1 required positional argument: 'request'
If I adapt the Mixin to:
class OrganizationPermissionMixin:
def get_queryset(self):
query = super().get_queryset()
if self.request.user.is_superuser:
return query
return query.filter(
organization__in=self.request.user.organization_set.all()
)
It works for generic views such as ListView
but now it indeed breaks for ModelAdmin
view:
OrganizationPermissionMixin.get_queryset() takes 1 positional argument but 2 were given
This inconsistency in signature is somehow very frustrating because it requires to duplicate code simply because request passing mechanism is different between Generic and Admin views.
My question is: how can I make this Mixin works both for Generic and Admin views. Is there something ready for that in Django? Is it normal it behaves like this or is it an unfortunate design choice?
CodePudding user response:
The request
is not passed as a queryset in a class-based view. It is however in a ModelAdmin
, you can harmonize the two with:
class OrganizationPermissionMixin:
def get_queryset(self, *args, **kwargs):
request = args[0] if args else kwargs.get('request') or self.request
query = super().get_queryset(*args, **kwargs)
if request.user.is_superuser:
return query
return query.filter(organization__user=request.user)
It will thus first try to obtain the request from the kwargs
. If that returns None
, it will "fallback" on self.request
, which is the case for a class-based view.
The modeling of the ModelAdmin
is quite bad. For example it reuses the same object, and therefore has no request encoded in the object. It also groups all sorts of views together. There have been proposals to redesign the modeladmin, but unfortunately, these are currently not implemented due to backwards compatibility.
CodePudding user response:
Maybe sth like this:
...
def get_queryset(self, *args, **kwargs):
request = kwargs.get("request", None)
if request:
query = super().get_queryset(request)
else:
query = super().get_queryset()
...