Django Rest Framework - ListCreateAPIView - Как фильтровать + агрегировать + создавать несколько объектов внутри def perform_create
Любая помощь по этому вопросу будет очень признательна!
В общем, у меня есть модель Transaction
, которая связана с моделью User
с помощью ForeignKey.
Я хотел бы filter
все транзакции, сделанные сегодня, для каждого пользователя, а затем я хочу aggregate
их продажи, суммируя транзакции для каждого пользователя, а затем на основе этой суммы я хотел бы рассчитать 10% от нее и create
новую транзакцию (в основном сегодняшние заработанные проценты)
Я пытался заставить его работать внутри serializers.py
class UserTransactionFilterSerializer(serializers.Serializer):
username = serializers.CharField(read_only = True)
id = serializers.IntegerField(read_only = True)
agg_amt_btc = serializers.SerializerMethodField(read_only=True)
trans = serializers.SerializerMethodField(read_only = True)
# Gets transaction data using UserTransactionSerializer
def get_trans(self, obj):
user = obj
print(user)
today = timezone.now().date()
my_trans_qs = user.transaction_set.filter(trans_date__date=today)
return TransactionUserInlineSerializer(my_trans_qs, many=True, context = self.context).data
# Aggregates the trans amount (sum and avg)
def get_agg_amt_btc(self, obj):
user = obj
print(user)
today = timezone.now().date()
my_trans_qs = user.transaction_set.filter(trans_date__date=today)
return my_trans_qs.aggregate(sum_btc = Sum('trans_amt_btc'), avg_btc = Avg('trans_amt_btc'))
def create(self,obj):
today = timezone.now().date()
for user in User.objects.filter(transaction__trans_date__date=today).distinct():
total_transactions = user.transaction_set.aggregate(sum_btc = Sum('trans_amt_btc'))['sum_btc']
print(user)
transaction_amount = total_transactions * Decimal('0.1')
print(transaction_amount)
with transaction.atomic():
Transaction.objects.create(owner=user, trans_amt_btc=transaction_amount)
return user
Который работает! Но он обходит все валидации, когда я делаю:
Transaction.objects.create(owner=user,trans_amt_btc=transaction_amount)
Проблема в том, что в приведенном выше методе objects.create
некоторые поля не упомянуты.
Например, есть поле trans_status
, которое находится в модели Transaction (показано ниже), которое имеет null=False
и blank=False
Но, когда объект создается, он имеет эти поля как null
вместо того, чтобы дать мне ошибку
Есть идеи, как я могу реализовать это внутри представления, где я уверен, что проверка может произойти?
Вот поля моей Transaction
модели, как они выглядят для справки:
owner = models.ForeignKey(User, default = 1, null = True, on_delete = models.SET_NULL) #related_name defaults to transaction_set
trans_date = models.DateTimeField(auto_now_add=True, blank=False)
trans_amt_btc = models.DecimalField(decimal_places=12, max_digits=24,blank=False, null=False)
trans_type = models.CharField(choices= TRANSACTION_TYPE_CHOICES, max_length=24, blank=False, null=False)
trans_reason = models.CharField(choices= TRANSACTION_REASON_CHOICES, max_length=24, blank=False, null=False)
trans_mode = models.CharField(choices= TRANSACTION_MODE_CHOICES, max_length=24, blank=False, null=False)
trans_status = models.CharField(choices= TRANSACTION_STATUS_CHOICES, max_length=24, blank=False, default="APPROVED")
note = models.CharField(max_length=120, blank=False, null=False)
Ниже приведена моя попытка добавить его в view
, где, как я надеюсь, я смогу выполнить необходимые проверки , которые не работают :
class UserTransactionCommissionView(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserTransactionFilterSerializer
def perform_create(self,serializer):
today = timezone.now().date()
for user in list(User.objects.filter(transaction__trans_date__date=today).distinct()):
total_transactions = user.transaction_set.aggregate(sum_btc = Sum('trans_amt_btc'))['sum_btc']
print(total_transactions)
total_transactions = serializer.validated_data.get('trans_amt_btc')
transaction_amount = total_transactions * Decimal('0.1')
print(transaction_amount)
serializer.save(owner=user)
# with transaction.atomic():
# Transaction.objects.create(owner=user, trans_amt_btc=transaction_amount)
return user
Добавить его в представление - это правильный путь, но я думаю, что вам просто не хватает еще одного сериализатора. Вы должны создать следующий сериализатор, чтобы иметь все необходимые поля:
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = Transaction
fields = ['owner', 'trans_amt_btc'] # Add other required fields here
Теперь вам нужно использовать этот сериализатор внутри perform_create следующим образом:
def perform_create(self, serializer):
today = timezone.now().date()
for user in User.objects.filter(transaction__trans_date__date=today).distinct():
total_transactions = user.transaction_set.filter(trans_date__date=today).aggregate(sum_btc=Sum('trans_amt_btc'))['sum_btc']
if total_transactions:
transaction_amount = total_transactions * Decimal('0.1')
transaction_data = {
'owner': user,
'trans_amt_btc': transaction_amount
# Add other fields here
}
transaction_serializer = TransactionSerializer(data=transaction_data) # <= Call the new serializer
if transaction_serializer.is_valid(): # <= Do the validation
transaction_serializer.save()
else:
raise serializers.ValidationError(transaction_serializer.errors)