python - 是否可以在中间件中过滤掉一些数据库行?

标签 python django django-rest-framework

很抱歉这个问题太长,我无法使其更短但仍然有意义。

我们有一个非常简单的应用程序,其中包含两个简单的模型 CompanyBuilding,它们之间具有多对多关系。每个都有一个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')

View 是常规的 Django REST Framework View 集,序列化器尽可能简单:

# 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.showFalse,则用户必须能够(在 View 中)看到受限公司建筑物

换句话说,如果john是一个User并且john.show是Falsejohn可以看到(在 View 中)normal_companynormal_building,但不是 restricted_companyrestricted_building

为了实现这一点,如果可能的话,我们不想编辑 View /序列化器,因为它们有很多(这是一个真实项目的简化版本,它要大得多) )。

我的团队考虑过使用中间件。我们尝试动态更改 Company.objectsBuilding.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)

但这不起作用 - 这很奇怪:当我获取公司时,它实际上get_queryset调用的User模型code>,因此 get_restricted_queryset 无效。

现在,我们真的陷入困境了。有人有什么想法可以帮助我们吗?或者只是中间件不应该做这样的事情?

最佳答案

您不需要中间件(因为中间件仅处理请求和响应,这是查询集以下的抽象级别)。您可以使用自定义 FilterBackend 在 DRF 中执行此操作,如下所示: 更新:也过滤嵌套公司!

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]

关于python - 是否可以在中间件中过滤掉一些数据库行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58425038/

相关文章:

python - Django:维护跨多个域的 session

python - 我如何创建 turtle 克隆并将它们附加到列表中?

python - 根据时间条件在MongoDB集合中查找文档并写回时间戳?

python - 当值超出范围时,在二维数组中显示错误的轴

Django REST框架-反向ForeignKey关系

python - Django:尝试选择随机结果,但获取对象没有 len() 错误

python - Django REST Framework - 来自外部提供商的 OAuth2 消费者 API

python - Django 应用程序在内存中有一个在所有请求之间共享的 Pandas 对象?

python - 500错误: null value in column "timeline_id" violates not-null constraint

javascript - 如何在django中提交表单而不刷新?