首页 > 解决方案 > 模型字段值未在 Django 中更新

问题描述

正在建立这个投票平台,用户将不得不投票。他们将输入他们想要购买的票数,然后付款。支付成功后,他们刚刚购买的票数应该添加到他们在数据库中的旧票数中。

例如,假设用户以 10 美元的价格购买了 2 张选票,并且已经拥有 3 张选票,则应将 2 添加到 3 以使其成为 5。

我的问题是 10 美元被添加到用户投票中。

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)

标签: pythondjangodjango-modelsdjango-views

解决方案


无需执行单独的查询来获取值(第一次)然后递增(第二次)。只需使用F 表达式访问同一模型中的字段并在单个操作中执行增量:

from django.db.models import F

Nomination.objects.filter(slug=slug).update(votes=F('votes') + amount)

无需强制Nomination.votes转换,int因为它已经是。但是 ifamount是一个str(如果您在 URL 中接受它作为int避免这样的转换会更好):

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.

在这里,所有Nomination具有目标的对象slug都将按votes上述数量递增。


更新

既然我们需要 的计数votes,我们不应该只接受amount这里的,因为它已经是 的乘积votes * price。我们可以做的是改变对这个 API 的调用,并votes通过nomination_payView()专门改变这一行来包含:

"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}",

类似于:

"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}?votes={request.POST['votes']}",

或者:

"redirect_url": f"http://127.0.0.1:8000/process-payment/{slug}/{amount}/{request.POST['votes']}",
  • 您将选择什么取决于端点可能处理的内容,https://test.theteller.net/checkout/initiate因为它将执行重定向。

然后在这个process_paymentAPI 中,我们可以直接从查询参数中直接访问该计数:

Nomination.objects.filter(slug=slug).update(votes=F('votes') + int(request.GET['votes']))

或者,如果您从路径参数中选择了第二个选项

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

推荐阅读