Используя метод 'build_standard_field(self, field_name, model_field)' для преодоления ошибки [<class 'decimal.InvalidOperation'>].

Я пишу Django Rest Framework, и я хочу использовать generics.CreateAPIView для создания операции в моей базе данных.
my models.py:


class User(AbstractBaseUser, PermissionsMixin):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField("Email Address", unique=True)
    first_name = models.CharField("First Name", max_length=150)
    last_name = models.CharField("Last Name", max_length=150)
    mobile = models.CharField("Mobile Number", max_length=150, blank=True)
    balance = models.DecimalField(max_digits=9, decimal_places=2, default=0.0, verbose_name=("Balance"))
    created_at = models.DateTimeField("Created at", auto_now_add=True, editable=False)


class Operation(models.Model):
    sender = models.ForeignKey("User", related_name=("Sender"), on_delete=models.CASCADE)
    receiver = models.ForeignKey("User", related_name=("Receiver"), on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=9, decimal_places=2, default=0.0, verbose_name=("Amount"))
    created_at = models.DateTimeField("Created at", auto_now_add=True, editable=False)

Я хочу отправлять значения uuid при создании операции и обрабатывать фильтр пользователей в бэкенде с помощью представления. Поэтому я добавил дополнительные поля в сериализаторы следующим образом:

class OperationCreateSerializer(serializers.ModelSerializer):
    sender_uuid = serializers.UUIDField(format='hex_verbose')
    receiver_uuid = serializers.UUIDField(format='hex_verbose')
    amount = serializers.DecimalField(max_digits=9, decimal_places=2, coerce_to_string=False)

    class Meta:
        model = Operation
        fields = ["sender_uuid", "receiver_uuid", "amount"]
        write_only_fields = ["sender_uuid", "receiver_uuid"]


class OperationListSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Operation
        fields = ["sender", "receiver", "amount"]

Проблема заключается в том, что когда я пытаюсь создать операцию transfert, я получаю ошибку [<class 'decimal.InvalidOperation'>] для поля 'amount' в представлении. Логика, которую я использую в представлении, следующая:

class TransferView(generics.CreateAPIView):
    serializer_class = OperationCreateSerializer
    queryset = Operation.objects.all()

    def create(self, request, *args, **kwargs):
        serializer = OperationCreateSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            print(serializer.validated_data.get('amount'))
        if serializer.is_valid(raise_exception=True):
            with transaction.atomic():
                sender = User.objects.filter(uuid=serializer.validated_data.get('sender_uuid'))
                if sender and sender[0].balance >= serializer.validated_data.get('amount'):
                    User.objects.filter(uuid=serializer.validated_data.get('sender_uuid')).update(balance=F('balance') - serializer.validated_data.get('amount', 0))
                    User.objects.filter(uuid=serializer.validated_data.get('receiver_uuid')).update(balance=F('balance') + serializer.validated_data.get('amount', None))
                    serializer.save(sender=User.objects.get(uuid=serializer.validated_data.get('sender_uuid')), receiver=User.objects.get(uuid=serializer.validated_data.get('receiver_uuid')))
                    return Response({'message': 'transfer done successfully'}, status=status.HTTP_201_OK)
                else:
                    return Response({'message': 'insufficient balance'}, status=status.HTTP_400_BAD_REQUEST)
        return Response(status=status.HTTP_400_BAD_REQUEST)


class TransactionViewSet(generics.ListAPIView):
    permission_classes = (IsAuthenticated,)
    serializer_class = OperationListSerializer
    queryset = Operation.objects.all()

    def get_queryset(self, **kwargs):
        """
        Restricting the returned purchased Operations to a given a date range query 
        parameter in the URL.
        """
        start = self.request.query_params.get('start_date')
        end = self.request.query_params.get('end_date')
        try:
            queryset = self.queryset.filter(Q(created_at__range=[start, end]))
            return queryset
        except Operation.DoesNotExist:
            raise Http404

Я не знаю, в чем проблема со значением поля Decimale, я нашел, что есть способ сопоставить дополнительные поля fields = ["sender_uuid", "receiver_uuid"], которые я использую для отправки значения uuid в бэкенд с соответствующим полем в моделях fields = ["sender", "receiver"] с помощью метода

> build_standard_field(self, field_name, model_field). Я не знаю, как использовать этот метод и почему я получаю ошибку при попытке создать мой экземпляр.

Первая причина, которая может вызвать ошибку, - это default=0.0 внутри вашей конфигурации ваших DecimalField. Это должно быть default=Decimal(0); в некоторых версиях Django это может вызвать проблемы.

Кроме того, эта ошибка обычно возникает, когда ваши данные не могут быть преобразованы в Decimal, особенно когда max_digits цифр недостаточно для вашего значения.

Из Django docs:

DecimalField.max_digits Максимальное количество цифр, допустимое в числе. Обратите внимание, что это число должно быть больше или равно decimal_places.

DecimalField.decimal_places. Количество десятичных знаков, которые следует хранить в числе.

Поэтому если вам нужно хранить 1_000_000_000.00 в DecimalField, вы должны настроить его max_digits=11, decimal_places=2, а не max_digits=9, decimal_places=2.

Вернуться на верх