Skip to content

Allow custom serializer class for request and response #8347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

khamaileon
Copy link

Following this commit, this PR add the possibility to specify classes that will be used when generating the input/output specs in the OpenAPI documentation.

@khamaileon khamaileon force-pushed the request-response-serialization branch 3 times, most recently from 788f74d to b22d728 Compare January 30, 2022 19:42
@stale
Copy link

stale bot commented Apr 3, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Apr 3, 2022
@khamaileon khamaileon force-pushed the request-response-serialization branch from b22d728 to dab91c3 Compare April 18, 2022 07:28
@stale stale bot removed the stale label Apr 18, 2022
@Smixi
Copy link

Smixi commented May 5, 2022

Hello, do you need anything so it pass and can be merged I'm interested in this feature.

@khamaileon khamaileon force-pushed the request-response-serialization branch from dab91c3 to ff075ef Compare June 24, 2022 07:08
@khamaileon
Copy link
Author

@tomchristie hi! would you mind taking a look at this?

@tomchristie
Copy link
Member

I don't really understand this, since it adds methods on the view sets, but doesn't actually plug the view sets behaviour into those methods.

We've pushed back on differing requests/response serialisers (with the small exception of allowing a way for those to be defined by the schema) and I don't think that's going to change at this point in the project's lifespan.

@khamaileon
Copy link
Author

khamaileon commented Jun 25, 2022

@tomchristie Thanks for answering! To be able to define serializers in the schema as proposed here (#7424), we need the methods get_request_serializer and get_response_serializer. If they are there by default, you just have to fill in request_serializer_response and response_serializer_response to get the schema generation.

As far as separating the request and response serializers, that would have been the ultimate goal indeed. I like the way it is handled in flask-smorest and we wouldn't be far off to do something like that in DRF. I know DRF is getting old but many of us still use it and it's far from being dead to us :)

@Smixi
Copy link

Smixi commented Jul 13, 2022

@tomchristie @khamaileon, Hi, thank you for your time :).

For my point of view, allowing to dissociate request/response serializer is crucial, because it allows to have schema generation libraries work out of the box. My pipeline using drf-spectacular for schema generation and openapi-generators for frontend service libraries can't work without a new generic view or a lot of decorator.

My main issue is that I wanted to have this behavior in ModelViewset, so I could have a view like this :

class WeaponViewSet(LendModelViewSet):
    queryset = Weapon.objects.all()
    serializer_class = WeaponSerializer
    output_serializer_class = WeaponSerializer
    filterset_class = WeaponFilter

    @action(detail=True, methods=['post'])
    def return_weapon(self, request, pk=None):
        """Return the weapon"""
        loan = Loan.objects.get(weapon_lent=pk, date_end=None)
        if loan.date_end is not None:
            return Response({'detail':"Cannot complete an already completed loan"}, HTTP_400_BAD_REQUEST)
        with transaction.atomic():
            loan.date_end = timezone.now()
            loan.weapon_lent.is_available = True
            loan.weapon_lent.save()
            loan.save()
        serializer = self.serializer_class(loan.weapon_lent)
        return Response(data=serializer.data)

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return InputWeaponSerializer
        if self.action in ['return_weapon']:
            return EmptySerializer
        return super().get_serializer_class()

This view will always use WeaponSerializer for response, but have different request serializer (Empty and InputWeapon), because some fields (like id, created_at, etc ...) must not be in the request schema.

To do that, I obviously get the same behavior as a Modelviewset, but modified all mixins to incorporate input/ouput logic:

class LendUpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}
        output_serializer = self.get_output_serializer(serializer.instance)
        return Response(output_serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

Without modifying the generics/mixins, I can't use ModelViewset without specifying in/out request by overiding methods on each of my viewset, which is kinda counterproductive ...

I guess this behavior might be outside the basic behavior of drf, so I could make an external modules to do that.
If i understand, this PR will only work for schema generation, if you want also to be plug with views, I will be pleased to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants