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]