I need the below DRF API View to list and create Withdraw
records in my Django project.
class WithdrawListCreateAPIView(PartnerAware, WithdrawQuerySetViewMixin, generics.ListCreateAPIView):
permission_classes = (TokenMatchesOASRequirements,)
required_alternate_scopes = {
"GET": [[OTCScopes.WITHDRAW_READ]],
"POST": [[OTCScopes.WITHDRAW_CREATE]],
}
def get_serializer_class(self, *args, **kwargs):
if self.request.method == "POST":
return WithdrawCreateSerializer
return WithdrawSerializer
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
self.get_partner_info()
def perform_create(self, serializer):
serializer.save(partner=self.partner, created_by=self.request.user)
My API construction must be for a single record, but it is possible that this amount may be greater than a specific amount called SETTLEMENT_MAX_AMOUNT
, and I have to break it down to more than one Withdraw
record and also I must create a separate record for each one, and I should return all these records that have been created in one list to the user.Here is the related serializer I've implemented for this case:
class WithdrawCreateSerializer(serializers.ModelSerializer):
target_uuid = serializers.UUIDField(write_only=True)
def validate_target_uuid(self, value):
partner = self.context["view"].partner
try:
target = WithdrawTarget.objects.get(active=True, uuid=value, partner=partner)
except WithdrawTarget.DoesNotExist:
raise serializers.ValidationError("Target does not exist for the current partner.")
return target.uuid
def create(self, validated_data):
target_uuid = validated_data.pop("target_uuid")
partner = self.context["view"].partner
target = WithdrawTarget.objects.get(uuid=target_uuid, partner=partner)
amount = validated_data["amount"]
num_withdrawals = amount // SETTLEMENT_MAX_AMOUNT
remaining_amount = amount % SETTLEMENT_MAX_AMOUNT
withdrawals = []
for _ in range(num_withdrawals):
withdraw_data = {
"target": target,
"amount": SETTLEMENT_MAX_AMOUNT,
"partner": validated_data["partner"],
"created_by": validated_data["created_by"],
"description": validated_data.get("description"),
}
withdrawal = Withdraw.objects.create(**withdraw_data)
withdrawals.append(withdrawal)
if remaining_amount > 0:
withdraw_data = {
"target": target,
"amount": remaining_amount,
"partner": validated_data["partner"],
"created_by": validated_data["created_by"],
"description": validated_data.get("description"),
}
withdrawal = Withdraw.objects.create(**withdraw_data)
withdrawals.append(withdrawal)
return withdrawals
class Meta:
model = Withdraw
fields = ("uuid",
"amount",
"target_uuid",
"description",
"status",
"tracker_id",
"created_at",)
extra_kwargs = {
"amount": {"required": True, "allow_null": False},
"target_uuid": {"required": True, "allow_null": False},
}
read_only_fields = ("state", "tracker_id", "created_at")
Now by sending this request:
curl --location '127.0.0.1:8000/withdraws/' \
--header 'Authorization: Bearer blob' \
--form 'amount="2240"' \
--form 'target_uuid="d4d92a38-4193-443c-b11e-8022e64543a4"'
for example, the value of SETTLEMENT_MAX_AMOUNT
is 1000, and with the above request I should get 3 "Withdraw" records with amount of: 1000, 1000, 240.
I receive the following error in response:
AttributeError at /withdraws/
Got AttributeError when attempting to get a value for field `amount` on serializer `WithdrawCreateSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `list` instance.
The original exception text was: 'list' object has no attribute 'amount'.
How can I change the above view or serializer the system behaves as I want?
by changing my API view to below view I resolve this problem:
class WithdrawListCreateAPIView(PartnerAware, WithdrawQuerySetViewMixin, generics.ListCreateAPIView):
permission_classes = (TokenMatchesOASRequirements,)
required_alternate_scopes = {
"GET": [[OTCScopes.WITHDRAW_READ]],
"POST": [[OTCScopes.WITHDRAW_CREATE]],
}
def get_serializer_class(self, *args, **kwargs):
if self.request.method == "POST":
return WithdrawCreateSerializer
return WithdrawSerializer
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
self.get_partner_info()
def perform_create(self, serializer):
return serializer.save(partner=self.partner, created_by=self.request.user)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
withdraws = self.perform_create(serializer)
return Response(WithdrawSerializer(withdraws, many=True).data, status=status.HTTP_201_CREATED)