Home > Software engineering >  Model filed values are not updating in django
Model filed values are not updating in django

Time:09-24

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):>
  • Related