【问题标题】:Avoid repeated entries from same user避免来自同一用户的重复输入
【发布时间】:2020-10-09 11:02:53
【问题描述】:

我正在尝试建立一个拍卖网站。我已经设置了我的 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 (£)'})
        }

【问题讨论】:

  • 也显示您的投标表格
  • 我现在已将 BidForm 添加到我的问题中

标签: python django django-models django-views


【解决方案1】:

首先我不喜欢你的观点是你的表单验证分布在表单和视图之间。首先,您检查该表单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 视图中你有listinglisting_bid 他们应该是一样的,为什么你需要他们两个?如果您想在找不到对象时抛出异常,则保留listing,否则保留listing_bid
  2. 要更新用户之前输入的数据而不是创建新数据,您需要向包含用户之前输入的数据的modelform 提供instance 属性。
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,)))

【讨论】:

  • 谢谢,我正在尝试将您的更改应用到我的 forms.py 和 views.py 但我在 self.form_bidder=kwargs.pop("form_bidder") 遇到了关键错误,我'不知道为什么
  • 我的错误,在else 案例中更改了答案。替换bidform = BidForm()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-05
  • 2013-09-05
  • 2017-02-12
  • 1970-01-01
  • 1970-01-01
  • 2015-04-25
相关资源
最近更新 更多