Используя метод '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
.