Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Django get_queryset has different signature for Admin and Generic views, how to prevent code duplication

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading