I'm currently developing a talent application where participants can receive votes from voters, and each vote incurs a cost. The collected funds from these votes are meant to be credited to the respective contestants. For instance, if someone casts a vote for a contestant with a count of 10 votes, the contestant's total vote count should increase by the number of votes received.
However, I've encountered an issue after a user casts a vote and makes a payment. It appears that the total vote count is incrementing by twice the intended value. For example, if a user submits a vote of 10, the contestant's total vote count is increasing by 20, which is unexpected and seems incorrect. Any assistance in resolving this issue would be greatly appreciated. Please find my code below:
def contestant_detail_payment(request, slug, *args, **kwargs):
contestant = get_object_or_404(Contestant, slug=slug)
vote_charge = contestant.get_vote_per_charge()
if request.method == "POST":
email = request.POST.get("email", "")
vote = request.POST.get("vote", "")
amount = int(request.POST["amount"].replace("₦", ""))
pk = settings.PAYSTACK_PUBLIC_KEY
payment = Payment.objects.create(
contestant=contestant,
amount=amount,
email=email,
vote=vote,
)
payment.save()
context = {
"payment": payment,
"field_values": request.POST,
"paystack_pub_key": pk,
"amount_value": payment.amount_value(),
"contestant": contestant,
}
return render(request, "competitions/make_payment.html", context)
context = {
"contestant": contestant,
"vote_charge": vote_charge,
}
return render(request, "competitions/profile.html", context)
@transaction.atomic
def verify_payment(request, ref):
print(f"verify_payment called for reference: {ref}")
try:
payment = Payment.objects.get(ref=ref)
except Payment.DoesNotExist:
# Handle the case where the payment with the given ref doesn't exist
print("Invalid payment reference:", ref)
return render(
request,
"competitions/error.html",
{"error_message": "Invalid payment reference."},
)
verified = payment.verify_payment()
if verified:
contestant = payment.contestant
profile_obj = payment.contestant.user.profile
profile_obj.totalVote += payment.vote
profile_obj.save()
print(f"Payment verified for with total votes of {contestant.user.profile.totalVote}.")
return render(request, "competitions/success.html")
else:
return render(
request,
"competitions/error.html",
{"error_message": "Payment verification failed."},
)
class Payment(models.Model):
contestant = models.ForeignKey(Contestant, on_delete=models.CASCADE)
email = models.EmailField(max_length=254, blank=True, null=True)
amount = models.PositiveIntegerField()
vote = models.IntegerField()
ref = models.CharField(max_length=200)
verified = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ("-date_created",)
def __str__(self):
return "{} voted for {} with {} votes".format(
self.email, self.contestant.user.first_name, self.vote
)
def save(self, *args, **kwargs):
while not self.ref:
ref = secrets.token_urlsafe(50)
object_with_similar_ref = Payment.objects.filter(ref=ref)
if not object_with_similar_ref:
self.ref = ref
super().save(*args, **kwargs)
def amount_value(self):
return int(self.amount) * 100
def verify_payment(self):
if not self.verified:
paystack = Paystack()
status, result = paystack.verify_payment(self.ref, self.amount)
if status:
if result["amount"] / 100 == self.amount:
self.verified = True
self.save()
return self.verified
I have tried refactoriing where the payment iss been verified and yet no head way. I have also moved tthe totalVote from where it was to the user profile model hoping that could be the issue and that didnt work a well.
Generally, you should avoid mathematical expressions working with model's fields. It may(and will) cause race condition. Use
model.field_name = F("field_name") + somethinginstead ofmodel.field_name += something.More about race conditions: https://bovage.hashnode.dev/how-to-avoid-race-condition-in-django
More about F(): https://docs.djangoproject.com/en/4.2/ref/models/expressions/#f-expressions
But honestly, try to log these additions and see where it works in unexpected way. First things to log are previous state, current state and what you've added. It may help you