首页 > 解决方案 > 如何通过 DRF 中相关模型中的字段过滤复杂数据库中的记录?

问题描述

我们希望能够通过相关表中的字段在所有表中找到记录。实际上,我们想搜索所有这些。

例如代码:

/models.py

class Tourney(models.Model):
    tourney = models.CharField()    
    tourney_1 = models.CharField() 
    tourney_2 = models.CharField()    

class Stage(models.Model):
    tourney = models.ForeignKey(Tourney, on_delete=models.CASCADE)
    stage = models.CharField()
    stage_1 = models.CharField()
    stage_2 = models.CharField()

class Group(models.Model):
    stage = models.ForeignKey(Stage, on_delete=models.CASCADE)
    group = models.CharField()
    group_1 = models.CharField()
    group_2 = models.CharField()

Group有关系上Stage有关系上Tourney

所以现在我们要为它们设置 API。想象一下,我们为它们提供了简单的序列化器,其中包括它们的所有字段并调用TourneySerializer,StageSerializerGroupSerializer.

现在让我们尝试过滤Group模型。

/views.py

class GroupViewSet(viewsets.ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = serializers.GroupSerializer

    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()

        if 'tourney' in request.GET:
            queryset = queryset.filter(stage__tourney__tourney=request.GET['tourney'])
        if 'tourney_1' in request.GET:
            queryset = queryset.filter(stage__tourney__tourney_1=request.GET['tourney_1'])
        if 'tourney_2' in request.GET:
            queryset = queryset.filter(stage__tourney__tourney_2=request.GET['tourney_2'])
        if 'stage' in request.GET:
            queryset = queryset.filter(stage__stage=request.GET['stage'])
        if 'stage_1' in request.GET:
            queryset = queryset.filter(stage__stage_1=request.GET['stage_1'])
        if 'stage_2' in request.GET:
            queryset = queryset.filter(stage__stage_2=request.GET['stage_2'])
        if 'group' in request.GET:
            queryset = queryset.filter(group=request.GET['group'])
        if 'group_1' in request.GET:
            queryset = queryset.filter(group_1=request.GET['group_1'])
        if 'group_2' in request.GET:
            queryset = queryset.filter(group_2=request.GET['group_2'])

        serializer = self.get_serializer_class()(
            queryset,
            many=True)

        return Response(serializer.data)

这里我们有一个 ViewSet 有一堆明显的代码,如果有更多的表和表中的更多字段,就会有更多。我有多达 20 个表,并且这个相关链中的最后一个表可以过滤大约 40 个字段。因此,所有模型都可能有大约 400 条过滤规则,因此仅最后一个模型就有大约800行愚蠢的代码。一点都不好。

那么有什么正确的已知方法可以做到这一点吗?这个问题看起来很常见,所以也许有来自 Django 或它的任何库的嵌入式解决方案?

标签: djangodjango-rest-frameworkdjango-filterdjango-serializer

解决方案


最快的方法是更改​​传递给该视图的参数名称 GET['tourney_2'] 应该作为 GET['stage__tourney__tourney_2'] 可用。整个过滤将是:

queryset = self.get_queryset().filter(**request.GET)

当然,您应该检查参数名称以避免像 request.GET['deleted'] = True 这样的 SQL 注入

在实际项目中,您不能使用 **request.GET。您应该将其转换为普通的 Python dict filters = dict(request.GET) 并检查过滤器以防止 sql 注入攻击。

queryset = self.get_queryset().filter(**filters)

请查看如何动态构建查询。

如何在 Django 查询中动态提供查找字段名称?


推荐阅读