简体   繁体   中英

Use serializer validated data in get_queryset()

I want to write custom get_queryset() method for serializer based on query params.

Here is my serializer:

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'))

If object of modelA exists - validation will return an instance. But I don't want to perform the same query in get_queryset() in my if branch.

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

I found only one solution - add the following line in my GET method:

self.serializer = self.get_serializer()

Than it will be possible to get validated values in my get_queryset() method. But PyCharm don't like this solution

I'm under strong impression that you are misusing the Serializer. After quick analysis of your issue i think you need DRF filtering

Serializers process request.data which under the hood is just Django request.POST and request.FILES yet in your get_queryset implementation you make lookups in request.query_params which in Django terms is request.GET . Check the DRF docs on this.

In order to achieve what you need with Serializers you would have to abuse the Views, Serializer Fields and Serializer itself. It is simply not what its supposed to do. Additionaly I don't think that you need so much validation on search.

Customization and use of Django Filter should solve your problem.

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

NOTE I did not test code above but it should show you how to tackle this problem.

How is about user = get_object_or_404(queryset, id=ModelA_id) . It looks better as for me. get_object_or_404 will catch an object you need, or will raise Not Found responce.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM