在get_queryset()中使用序列化器验证的数据

时间:2018-10-16 13:39:34

标签: django django-rest-framework

我想基于查询参数为序列化程序编写自定义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不喜欢这种解决方案

2 个答案:

答案 0 :(得分:3)

我对您误用了Serializer印象很深。快速分析您的问题后,我认为您需要DRF filtering

Serializers处理request.data,实际上是Django request.POSTrequest.FILES,但是在您的get_queryset实现中,您在request.query_params中进行查找,以Django的术语是request.GET。查看有关此内容的DRF文档。

为了实现序列化器所需的功能,您将不得不滥用视图,序列化器字段和序列化器本身。这根本不是它应该做的。 另外,我认为您不需要在搜索中进行太多验证。

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

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
  

注意我没有在上面测试代码,但是它应该向您展示如何解决此问题。

答案 1 :(得分:0)

user = get_object_or_404(queryset, id=ModelA_id)的情况如何。对我来说看起来更好。 get_object_or_404将捕获您需要的对象,或引发Not Found响应。