Am building this voting platform, users will have to but the votes. They will input the number of votes to they want to buy, then make payment. After a successful payment, The number of votes they just bought should be added to their old votes in the database.
For instance, say a user buys 2 votes at a price of 10USD and have 3 votes already, the 2 should should be added to the 3 to make it 5.
My problem is 10USD is rather added to the users votes.
models.py
class Nomination(models.Model):
Fullname = models.CharField(max_length=120)
Nominee_ID = models.CharField(max_length=100)
Category = models.ForeignKey(Category, on_delete=models.CASCADE)
image = models.ImageField(upload_to='nominations_images')
slug = models.SlugField(max_length=150)
votes = models.IntegerField(default=0)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.Fullname
views.py
def nomination_payView(request, slug):
if request.method == 'GET':
model = get_object_or_404(Nomination, slug=slug)
template_name = 'Payment.html'
context = {
'nomination': model
}
return render(request, 'Payment.html', context)
elif request.method == 'POST':
amount = (str(int(request.POST['votes']) * float(request.POST['price'])) '0').replace('.', '')
zeros = '0' * (12 - len(amount))
amount = zeros amount
email = request.POST['email']
url = 'https://test.theteller.net/checkout/initiate'
transaction_id = random.randint(100000000000, 999999999999)
data = {
"merchant_id": "TTM-00000740",
"transaction_id": transaction_id,
"desc": "Payment Using Checkout Page",
"amount": amount,
"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}",
"email": email,
"API_Key": "ZDQ2OGEyZDNjN2YzMDY5ZDVkY2MyM2U5YTRiMGI0N2Q=",
"apiuser": "halotech5d525bfd6ead3",
}
encoded = base64.b64encode(
b'halotech5d525bfd6ead3:ZDQ2OGEyZDNjN2YzMDY5ZDVkY2MyM2U5YTRiMGI0N2Q=')
headers = {
'Content-Type': 'application/json',
'Authorization': f'Basic {encoded.decode("utf-8")}',
'Cache-Control': 'no-cache'
}
res = requests.post(url, data=json.dumps(data),
headers=headers).json()
print(res['checkout_url'])
return redirect(res["checkout_url"])
def process_payment(request, slug, amount):
trans_id = request.GET.get('transaction_id')
status = request.GET.get('status')
reason = request.GET.get('reason')
transaction_id = request.GET.get('transaction_id')
if status == 'Approved':
transaction = SuccessfulTransactionHistory(
nominee_name=slug,
transaction_id=transaction_id,
amount=amount
)
transaction.save()
nomination = Nomination.objects.filter(slug=slug).values('votes')
Nomination.objects.filter(slug=slug).update(votes=int(nomination[0]['votes']) int(amount[:-2]))
return redirect('/success')
else:
context = {
'error': reason
}
transaction = FailedTransactionHistory(
nominee_name=slug,
transaction_id=transaction_id,
amount=amount
)
transaction.save()
return render(request, 'payment_error.html', context=context)
CodePudding user response:
No need to perform separate queries to get the value (first) and then to increment (second). Just use an F expression to access a field from the same model and perform an increment in a single operation:
from django.db.models import F
Nomination.objects.filter(slug=slug).update(votes=F('votes') amount)
No need to cast Nomination.votes
to int
because it already is. But if amount
is an str
(though better if you accept it in your URL as an int
to avoid conversions such as this):
Nomination.objects.filter(slug=slug).update(votes=F('votes') int(amount[:-2])) # The slice [:-2] was used as performed in the question. Adjust accordingly depending on what the amount really contains.
Here, all Nomination
objects with the target slug
would have their votes
incremented by the said amount.
Update
Since we need the count of votes
, we should not just accept the amount
here since it is already the product of votes * price
. What we can do is change the call to this API and include votes
by changing nomination_payView()
specifically this line:
"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}",
To something like:
"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}?votes={request.POST['votes']}",
Or:
"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}/{request.POST['votes']}",
- What you would choose depends on what is possible to be processed by the endpoint
https://test.theteller.net/checkout/initiate
as it will do the redirect.
Then within this process_payment
API, we can simply access that count directly from the query parameters:
Nomination.objects.filter(slug=slug).update(votes=F('votes') int(request.GET['votes']))
Or if you chose the 2nd option from path parameters
Nomination.objects.filter(slug=slug).update(votes=F('votes') votes) # Assuming you defined the URL as something like <path("process-payment/{str:slug}/{int:amount}/{int:votes}", ...> and the view function as <def process_payment(request, slug, amount, votes):>