首页 > 解决方案 > 在 get_queryset() 中使用经过序列化程序验证的数据

问题描述

我想根据查询参数为序列化程序编写自定义 get_queryset() 方法。

这是我的序列化程序:

class SearchRequestSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=255, required=False)
    nickname = serializers.RegexField(
        r'^(?!(.*?\_){2})(?!(.*?\.){2})[A-Za-z0-9\._]{3,24}$',
        max_length=24,
        min_length=3,
        required=False,
    )
    modelA_id = serializers.CharField(max_length=11, min_length=11,
                                      required=False)

    def validate_modelA_id(self, value):
        queryset = modelA.objects.filter(id=value)
        if queryset.exists():
            return queryset.first()
        else:
            raise serializers.ValidationError(_('Not found'))

如果 modelA 的对象存在 - 验证将返回一个实例。但我不想在我的 if 分支的 get_queryset() 中执行相同的查询。

def get_queryset(self):
    name = self.request.query_params.get('name', None)
    nickname = self.request.query_params.get('nickname', None)
    queryset = User.objects.filter(Q(name__contains=name)|Q(nickname__contains=nickname))
    if 'modelA_id' in self.request.query_params:
       # in this case will be annotated extra field to queryset
       # extra field will be based on 'modelA' instance which should be returned by serializer
    return queryset

我只找到了一种解决方案 - 在我的 GET 方法中添加以下行:

self.serializer = self.get_serializer()

比在我的 get_queryset() 方法中获得验证值是可能的。但是 PyCharm 不喜欢这种解决方案

标签: djangodjango-rest-framework

解决方案


我对您滥用序列化程序有强烈的印象。在快速分析您的问题后,我认为您需要DRF 过滤

序列化程序进程request.data在底层只是 Django request.POSTrequest.FILES但在您的get_queryset实现中,您进行查找request.query_params,在 Django 术语中是request.GET. 检查有关此的 DRF 文档。

为了用序列化器实现你需要的东西,你必须滥用视图、序列化器字段和序列化器本身。这根本不是它应该做的。另外,我认为您不需要对搜索进行太多验证。

自定义和使用Django 过滤器应该可以解决您的问题。

class UserFilter(filters.FilterSet):
    class Meta:
        model = User
        fields = ['name', 'nickname', 'modelA_id']

    def filter_queryset(self, queryset):
        name = self.form.cleaned_data.get('name', None)
        nickname = self.form.cleaned_data.get('nickname', None)
        queryset = queryset.filter(Q(name__contains=name)|Q(nickname__contains=nickname))
        if 'modelA_id' in self.form.cleaned_data:
            # in this case will be annotated extra field to queryset
            # extra field will be based on 'modelA' instance which should be returned by serializer
        return queryset


class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
    filterset_class = UserFilter

注意我没有测试上面的代码,但它应该告诉你如何解决这个问题。


推荐阅读