Можно ли отфильтровать некоторые строки базы данных в промежуточном программном обеспечении? - PullRequest
1 голос
/ 17 октября 2019

Извините за длину этого вопроса, я не смог сделать его короче и по-прежнему значимым.

У нас очень простое приложение с двумя простыми моделями Company и Buildingс отношениями многих ко многим. У каждого есть атрибут restricted. User - это обычный класс Django User, за исключением того, что мы добавляем атрибут show.

# models.py

class User(AbstractUser):
    show = models.BooleanField(default=True)


class Company(models.Model):
    name = models.CharField(max_length=100)
    restricted = models.BooleanField(default=False)


class Building(models.Model):
    name = models.CharField(max_length=100)
    restricted = models.BooleanField(default=False)
    companies = models.ManyToManyField(Company, related_name='buildings')

Представления - это обычные наборы представлений Django REST Framework, а сериализаторы настолько просты, насколько это возможно:

# views.py

class CompanyViewSet(ModelViewSet):
    queryset = Company.objects.all()
    serializer_class = CompanySerializer


class BuildingViewSet(ModelViewSet):
    queryset = Building.objects.all()
    serializer_class = BuildingSerializer


# serializers.py

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = '__all__'


class BuildingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Building
        fields = '__all__'

Теперь мы хотим реализовать это поведение : если user.show равно False, пользователь должен не быть в состоянии видеть (в представлениях)restricted Company и Building.

Другими словами, если john является User и john.show is False, john может видеть (в представлениях) normal_company иnormal_building, но не restricted_company или restricted_building.

Для этого мы не хотим редактировать представления / сериализаторы , если этовозможно, потому что их много (это упрощенная версия реального проекта, который намного больше).

Моя команда думала об использовании промежуточного программного обеспечения. Мы попытались динамически изменить Company.objects и Building.objects:

# middleware.py

class FilterMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        user = get_user()  # Get the user somehow.

        if not user.show:
            # Replace objects.
            for model in (Company, Building):
                model.objects = model.objects.filter(restricted=False)

        response = self.get_response(request)
        return response

Но это, по какой-то неизвестной причине, не работает: john все еще может видеть компании с ограниченным доступом. Затем мы попытались динамически обновить метод django.db.models.Manager.get_queryset:

# middleware.py
class FilterMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        user = get_user()

        if not user.show:
            models.Manager.get_queryset = get_restricted_queryset

        response = self.get_response(request)
        return response


def get_restricted_queryset(self, *args, **kwargs):
    # Conditions for the filter later.
    hiding_conditions = {
        "Company": Q(restricted=True),
        "Building": Q(companies__restricted=True) | Q(restricted=True),
    }

    model_name = self.model.__name__

    if model_name in hiding_conditions:
        # We must filter the model out, so apply the hiding conditions.
        hiding_condition = hiding_conditions[model_name]

        return self._queryset_class(
            model=self.model, using=self._db, hints=self._hints
        ).exclude(hiding_condition)
    else:
        return self._queryset_class(model=self.model, using=self._db, hints=self._hints)

Но это не работает - и это странно: когда я выбираю компании, на самом деле это только User модель, которая называется get_queryset, поэтому get_restricted_queryset не имеет никакого эффекта.

Теперь мы действительно застряли. У кого-нибудь есть идея, которая может нам помочь? Или просто промежуточное программное обеспечение не должно делать таких вещей?

1 Ответ

2 голосов
/ 17 октября 2019

Вам не нужно промежуточное программное обеспечение (поскольку промежуточное программное обеспечение обрабатывает только запросы и ответы, которые на уровне абстракции ниже QuerySets). Вы можете сделать это в DRF с помощью пользовательского FilterBackend, например: Обновление: фильтруйте и вложенные компании!

from rest_framework import filters

class IsRestrictedFilterBackend(filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        if request.user and user.is_authenticated and not user.show:
            if queryset.model and queryset.model in [Company, Building]:
                queryset = queryset.filter(restricted=False)
                if queryset.model == Building:
                    return queryset.filter(companies__restricted=False)
        return queryset

, затем вы добавите этот фильтр бэкэнда в свои настройки:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['yourapp.filter_backends.IsRestrictedFilterBackend']
}

... или вы можете использовать его на основе ViewSet:

class BuildingViewSet(ModelViewSet):
    queryset = Building.objects.all()
    serializer_class = BuildingSerializer
    filter_backends = [yourapp.filter_backends.IsRestrictedFilterBackend]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...