I’m trying to build an auction site. I’ve set up my placebid view and I’m quite happy with how it functions but the problem is the same user is able to bid on the same listing multiple times. This is okay but instead of it being stored in the database as several separate bids all by the same user, I want the initial bid to be updated or initial bid removed and new bid created.
I did try UniqueConstraint and added to my Bid model (fields: bidder and bid_input) but it made no difference plus when I deleted it from my Bid model and makemigrations there were no changes detected so it seems the database was dismissing it anyway.
I tried to do a .filter().exists() check before saving the form in my placebid view and now the form lets me place an initial bid but when I do a second bid it returns a Page Not Found error:
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.
This is my functional views.py with no form validation checking:
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,)))
I understand that I am doing my form validation in views.py and it’s best to do it in forms.py using clean() but I don’t know how to because I’m depending on the highest_bid variable to validate my forms and that needs the id that is passed through with the request.
This is the .filter().exists() checking that I added:
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,)))
Bid model and Bid form
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}"
class BidForm(forms.ModelForm):
class Meta:
model = Bid
fields = ["bid_input"]
labels = {"bid_input": ""}
widgets = {
"bid_input": forms.NumberInput(attrs={'placeholder': 'Enter bid (£)'})
}