首页 > 解决方案 > 避免来自同一用户的重复输入

问题描述

我正在尝试建立一个拍卖网站。我已经设置了我的 placebid 视图,我对它的功能非常满意,但问题是同一个用户能够多次对同一个列表出价。这没关系,但我希望更新初始出价或删除初始出价并创建新出价,而不是将其作为多个单独的出价存储在数据库中。

我确实尝试了 UniqueConstraint 并添加到我的 Bid 模型(字段:bidder 和 bid_input),但它没有任何区别,而且当我从我的 Bid 模型中删除它并且 makemigrations 没有检测到任何更改时,似乎数据库无论如何都在解雇它。

在将表单保存在我的 placebid 视图中之前,我尝试进行 .filter().exists() 检查,现在该表单允许我进行初始出价,但是当我进行第二次出价时,它返回一个 Page Not Found 错误:

Page not found (404)
Request Method: POST
Request URL:    http://127.0.0.1:8000/listing/3/bid
Raised by:  auctions.views.placebid
No Bid matches the given query.

这是我的功能views.py,没有表单验证检查:

def placebid(request, id):
    listing_bid = get_object_or_404(Listing, id=id)
    highest_bid = Bid.objects.filter(bid_item_id=id).aggregate(Max('bid_input'))['bid_input__max'] or Decimal('0')
    currentHighest = Bid.objects.filter(bid_item_id=id).aggregate(Max('bid_input'))['bid_input__max']
    listing = Listing.objects.get(pk=id)
    if request.method == "POST":
        bidform = BidForm(request.POST)
        if bidform.is_valid() and highest_bid == 0:
            bid_placed = bidform.cleaned_data['bid_input']
            if bid_placed <= listing.start_price:
                messages.error(request, 'Make sure your bid is greater than the start price')
                return HttpResponseRedirect(reverse("listing", args=(id,)))
            else:
                newbid = bidform.save(commit=False)
                newbid.bidder = request.user
                newbid.bid_input = bidform.cleaned_data['bid_input']
                newbid.bid_item = listing_bid
                newbid.time = timezone.now()
                newbid.save()
                messages.success(request, 'Bid placed succesfully')
                return HttpResponseRedirect(reverse("listing", args=(id,)))
        if bidform.is_valid() and highest_bid > 0:
            bid_placed = bidform.cleaned_data['bid_input']
            if bid_placed <= highest_bid:
                messages.error(request, 'Make sure your bid is greater than the current highest bid')
                return HttpResponseRedirect(reverse("listing", args=(id,)))
            else:
                newbid = bidform.save(commit=False)
                newbid.bidder = request.user
                newbid.bid_input = bidform.cleaned_data['bid_input']
                newbid.bid_item = listing_bid
                newbid.time = timezone.now()
                newbid.save()
                messages.success(request, 'Bid placed succesfully')
                return HttpResponseRedirect(reverse("listing", args=(id,)))
    else:
        bidform = BidForm()
    return HttpResponseRedirect(reverse("listing", args=(id,)))

这是我添加的 .filter().exists() 检查:

                bid = get_object_or_404(Bid, id=id)
                if bid.bid_input.filter(bidder=request.user.id).exists():
                    bid.bid_input.remove(request.user)
                    newbid = bidform.save(commit=False)
                    newbid.bidder = request.user
                    newbid.bid_input = bidform.cleaned_data['bid_input']
                    newbid.bid_item = listing_bid
                    newbid.time = timezone.now()
                    newbid.save()
                    messages.success(request, 'Bid placed succesfully')
                    return HttpResponseRedirect(reverse("listing", args=(id,)))
                else:
                    newbid = bidform.save(commit=False)
                    newbid.bidder = request.user
                    newbid.bid_input = bidform.cleaned_data['bid_input']
                    newbid.bid_item = listing_bid
                    newbid.time = timezone.now()
                    newbid.save()
                    messages.success(request, 'Bid placed succesfully')
                    return HttpResponseRedirect(reverse("listing", args=(id,)))

投标模型

class Bid(models.Model):
    bidder = models.ForeignKey(User, on_delete=models.CASCADE, related_name="bidders")
    bid_item = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="bid_items", default=None)
    bid_input = models.DecimalField(max_digits=9, decimal_places=2, default=None)
    time = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.bidder}, bid amount: {self.bid_input}"

编辑添加 BidForm:

class BidForm(forms.ModelForm):
    class Meta:
        model = Bid
        fields = ["bid_input"]
        labels = {"bid_input": ""}
        widgets = {
            "bid_input": forms.NumberInput(attrs={'placeholder': 'Enter bid (£)'})
        }

标签: pythondjangodjango-modelsdjango-views

解决方案


首先我不喜欢你的观点是你的表单验证分布在表单和视图之间。首先,您检查该表单is_valid,然后添加其他验证。我会将此验证移至ModelForm 您需要提供以形成所有需要的数据,例如form_bidderhidhest_bidbid_item

class BidForm(forms.ModelForm):
    class Meta:
        model = Bid
        fields = ["bid_input"]
        labels = {"bid_input": ""}
        widgets = {
            "bid_input": forms.NumberInput(attrs={'placeholder': 'Enter bid (£)'})
        }

    def __init__(self, *args, **kwargs):
        self.form_bidder = kwargs.pop("form_bidder")
        self.highest_bid = kwargs.pop("hidhest_bid")
        self.bid_item = kwargs.pop("bid_item")
        super().__init__(*args, **kwargs)

    def clean_bid(self):
        bid_input = self.cleaned_data.get("bid_input")
        if bid_input and self.highest_bid == 0 and bid_input < self.bid_item.start_price:
            self.add_errror("bid_input", "Make sure your bid is greater than the start price")
        if bid_input and self.highest_bid > 0 and bid_input < self.highest_bid:
            self.add_errror("bid_input", "Make sure your bid is greater than the current highest bid")
        return bid_input

    def save(self, *args, **kwargs):
        self.instance.bidder = self.form_bidder
        self.instance.bid_item = self.bid_item
        self.instance.time = timezone.now()
        return super().save(*args, **kwargs)
  1. 鉴于placebid您拥有listing并且listing_bid它们应该相同,为什么您需要它们?如果您想在找不到对象时抛出异常,则保留,listing否则保留listing_bid
  2. 要更新用户之前输入的数据而不是创建新数据,您需要提供包含用户之前输入的数据的instance属性。modelform
def placebid(request, id):
    highest_bid = Bid.objects.filter(bid_item_id=id).aggregate(Max('bid_input'))['bid_input__max'] or Decimal('0')
    currentHighest = Bid.objects.filter(bid_item_id=id).aggregate(Max('bid_input'))['bid_input__max']
    # listing_bid = get_object_or_404(Listing, id=id)
    listing = Listing.objects.get(pk=id) 
    form_instance = Bid.objects.filter(bidder=request.user, bid_item=listing).first() 

    if request.method == "POST":
        bidform = BidForm(
            request.POST, 
            instance=form_instance, 
            form_bidder=request.user, 
            hidhest_bid=highest_bid,
            bid_item = listing,
            )
        if bidform.is_valid():
            newbid = bidform.save()
            messages.success(request, 'Bid placed succesfully')
            return HttpResponseRedirect(reverse("listing", args=(id,)))
        else:
            for error in bidform.errors:
                messages.error(request, str(error))
            return HttpResponseRedirect(reverse("listing", args=(id,)))
    else:        
        bidform = BidForm(
            instance=form_instance, 
            form_bidder=request.user, 
            hidhest_bid=highest_bid,
            bid_item = listing,
        )
    return HttpResponseRedirect(reverse("listing", args=(id,)))


推荐阅读