How to propagate error on create method in DRF serializer?

I'm trying to raise some error based on some business logic as in below

class ConsultationViewset(BaseModelViewset, ListModelViewsetMixin, RetrieveModelViewsetMixin, CreateModelViewsetMixin, UpdateModelViewsetMixin):
    serializer_class = ConsultationSerializer
    ....
class ConsultationSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        consultation = ConsultationService.create_consultation(validated_data.pop('customer', None), validated_data)
        return consultation
def create_consultation(customer: CustomerFactory.Models.CUSTOMER, validated_data):
    if ticket is None:
            raise exceptions.PermissionDenied("No active ticket found for this customer, request is forbidden")

My aim is to send the raised message in create_consultation in the response. Yet I keep getting AssertionError: create() did not return an object instance.instead. I could send a custom message if I re-raise the error in the viewset like below, but it felt wrong as the error is AssertionError.

class ConsultationViewset(...):
    def perform_create(self, serializer):
        try:
            serializer.save()
        except AssertionError as e:
            raise exceptions.PermissionDenied('custom message')

How to properly raise a PermissionDenied error?

When you use Django REST Framework (DRF), the serializer's create() method will create and return a new object. The error happens as when the serializer calls create_consultation function but there's no ticket and raises an error. Because of this error, nothing gets returned from the create() method. That's why you get AssertionError: create() did not return an object instance

You can try to catch the error in the serializer

class ConsultationSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        try:
            consultation = ConsultationService.create_consultation(validated_data.pop('customer', None), validated_data)
            return consultation
        except exceptions.PermissionDenied as e:
            raise e
        except Exception as e:
            # Convert other exceptions to DRF exceptions if needed
            raise exceptions.ValidationError(str(e))

You should not raise permissionDenied in create method. it's handled in the view before accessing the serializer. So per the doc permissions, you need to implement a custom-permissions and add it to the permission classes in the view.

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'No active ticket found for this customer, request is forbidden.'

    def has_permission(self, request, view) -> bool:
         return request.user.has_active_ticket()  # example
class ExampleView(APIView):
    def get_permissions(self) -> list[BasePermission]:
        permission_classes = self.permission_classes
        if self.request.method == "POST":
            permission_classes = (
                *permission_classes,
                CustomerAccessPermission,
            )
        return [permission() for permission in permission_classes]
Back to Top