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, но, к сожалению, в настоящее время они не реализованы из-за обратной совместимости.