Django get_queryset имеет разную сигнатуру для представлений Admin и Generic, как предотвратить дублирование кода

Я хотел бы написать простой код для фильтрации записей в представлении на основе информации о запросе (например, организации, к которой принадлежит пользователь).

Я начал реализовывать его как Mixin для представлений администратора.

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()
        )

Это работает нормально, но когда я попытался применить этот Mixin к Generic views, у меня возникла ошибка подписи, так как нет параметра запроса, переданного методу get_queryset:

TypeError: OrganizationPermissionMixin.get_queryset() missing 1 required positional argument: 'request'

Если я адаптирую Mixin к:

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()
        )

Это работает для общих представлений, таких как ListView, но теперь это действительно ломается для представления ModelAdmin:

OrganizationPermissionMixin.get_queryset() takes 1 positional argument but 2 were given

Это несоответствие в подписи очень расстраивает, поскольку требует дублирования кода просто потому, что механизм передачи запроса отличается между представлениями Generic и Admin.

Мой вопрос: как я могу сделать так, чтобы этот Mixin работал и для Generic и для Admin представлений. Есть ли что-то готовое для этого в Django? Это нормально, что он так себя ведет, или это неудачный выбор дизайна?

Может быть, что-то вроде этого:

...
def get_queryset(self, *args, **kwargs):
    request = kwargs.get("request", None)
    if request:
        query = super().get_queryset(request)
    else:
        query = super().get_queryset()
    ...

В представлении на основе класса request не передается как кверисет. Однако он передается в ModelAdmin, вы можете согласовать эти два параметра с помощью:

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)

Таким образом, сначала он попытается получить запрос от kwargs. Если это вернет None, он "отступит" на self.request, что является случаем для представления на основе класса.

Моделирование ModelAdmin довольно плохое. Например, он повторно использует один и тот же объект и поэтому не имеет запроса, закодированного в объекте. Он также группирует вместе всевозможные представления. Были предложения по переработке modeladmin, но, к сожалению, в настоящее время они не реализованы из-за обратной совместимости.

Вернуться на верх