【问题标题】:using method 'build_standard_field(self, field_name, model_field)' to overcame the error [<class 'decimal.InvalidOperation'>]使用方法 'build_standard_field(self, field_name, model_field)' 来克服错误 [<class 'decimal.InvalidOperation'>]
【发布时间】:2022-01-03 09:57:46
【问题描述】:

我正在编写一个 Django Rest 框架,我想使用 generics.CreateAPIView 在我的数据库中创建一个操作。
我的 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 操作时,视图中的字段 'amount' 出现错误 [&lt;class 'decimal.InvalidOperation'&gt;]。 我认为我使用的逻辑是:

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) 我不知道如何使用此方法以及为什么在尝试创建实例时出现错误。

编辑
这是我得到的错误:

Internal Server Error: /v1/api/transfert/
Traceback (most recent call last):
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/rest_framework/generics.py", line 190, in post
    return self.create(request, *args, **kwargs)
  File "/home/mohammed/Projects/PaymentSystem/payment_api/account/views.py", line 30, in create
    serializer.save(sender=User.objects.get(uuid=serializer.validated_data.get('sender_uuid')), receiver=User.objects.get(uuid=serializer.validated_data.get('receiver_uuid')), amount=decimal.Decimal(serializer.validated_data.get('amount')))
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/models/query.py", line 431, in get
    num = len(clone)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/models/query.py", line 262, in __len__
    self._fetch_all()
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/models/query.py", line 1324, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/models/query.py", line 68, in __iter__
    for row in compiler.results_iter(results):
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1122, in apply_converters
    value = converter(value, expression, connection)
  File "/home/mohammed/Projects/PaymentSystem/paymentsys/lib/python3.7/site-packages/django/db/backends/sqlite3/operations.py", line 313, in converter
    return create_decimal(value).quantize(quantize_value, context=expression.output_field.context)
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
[03/Jan/2022 15:58:16] "POST /v1/api/transfert/ HTTP/1.1" 500 145513

【问题讨论】:

  • 你能分享你的错误日志吗?
  • @P.Ezzati 我添加了错误日志。

标签: python django django-rest-framework django-views django-serializer


【解决方案1】:

第一个可能导致错误的原因是您的DecimalFields 配置中的default=0.0。必须是default=Decimal(0);在某些版本的 Django 中,它可能会引起麻烦。

此外,当您的数据无法转换为Decimal 时,通常会发生此错误,尤其是当max_digits 数字不足以满足您的值时。

来自Django docs

DecimalField.max_digits 数字中允许的最大位数。请注意,此数字必须大于或等于 decimal_places

DecimalField.decimal_places 与数字一起存储的小数位数。

所以如果你需要在DecimalField中存储1_000_000_000.00,你必须配置它max_digits=11, decimal_places=2,而不是max_digits=9, decimal_places=2

【讨论】:

  • 即使在模型字段中使用十进制转换器作为默认值广告也在视图中转换 amount 在将其保存到实例之前我得到相同的错误。我将整个错误消息添加到我的问题中
  • 而我只用了少量的5位数
  • 嗯,奇怪。您能否指定serializer.validated_data.get('amount') 中的值?您可以在payment_api/account/views.py 处调试输出。
猜你喜欢
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 2019-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多