如何通过DRF中相关模型中的字段过滤复杂数据库中的记录?

How to filter records in complex DB by the fields in related models in DRF?

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

例如代码:

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

GroupStage 有关系,StageTourney.

有关系

所以现在我们要为他们设置API。想象一下,我们为他们提供了简单的序列化程序,其中包括他们所有的字段,并称为 TourneySerializerStageSerializerGroupSerializer.

现在让我们尝试过滤 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,如果 tables 和 tables 中的字段更多,还会有更多。我最多有 20 个 table,并且此相关链中的最后一个 table 可以过滤大约 40 个字段。因此,可能在所有模型上都有大约 400 条过滤规则,因此仅针对最后一个模型,大约有 800 行愚蠢的代码。一点都不好

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

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

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

当然你应该检查参数名称以避免 SQL 像 request.GET['deleted'] = True

这样的注入

在实际项目中不能使用**request.GET。您应该将其转换为正常的 Python dict filters = dict(request.GET) 并检查过滤器是否受到 sql 注入攻击。

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

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

How to dynamically provide lookup field name in Django query?