Reputation: 246
I'm not sure how to resolve this issue. I tried to convert highest_bid to a float:
highest_bid = float(Bid.objects.filter(id=bid_item.id).aggregate(Max('bid_input')))
but that also produced an error because a dict cannot be converted to a float in that way. So I'm not sure how to resolve this. I'm trying to build an auctions site and I would like the bid that a user places to be rejected if it is less than the listing start price and less than the highest of all the bids that have been placed.
error
Internal Server Error: /listing/2/bid
Traceback (most recent call last):
File "C:\Python\Python385\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "C:\Python\Python385\lib\site-packages\django\core\handlers\base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\sarah\Desktop\commerce\auctions\views.py", line 157, in placebid
if bidform.cleaned_data['bid_input'] < listing.start_price and bidform.cleaned_data['bid_input'] <= highest_bid:
TypeError: '<=' not supported between instances of 'decimal.Decimal' and 'dict'
[08/Oct/2020 13:59:48] "POST /listing/2/bid HTTP/1.1" 500 62598
views.py
def placebid(request, id):
listing_bid = get_object_or_404(Listing, id=id)
highest_bid = Bid.objects.filter(id=id).aggregate(Max('bid_input'))
listing = Listing.objects.get(pk=id)
if request.method == "POST":
bidform = BidForm(request.POST)
if bidform.is_valid():
if bidform.cleaned_data['bid_input'] < listing.start_price and bidform.cleaned_data['bid_input'] <= highest_bid:
return render(request, "auctions/listingPage.html", {
"listing": listing,
"error": "Make sure your bid is greater than the start price and current highest bid"
})
else:
listing_bid.bid_input = bidform.cleaned_data['bid_input']
listing_bid.bidder = request.user
listing_bid.bid_item = listing
listing_bid.time = timezone.now()
listing_bid.save()
messages.success(request, 'Bid placed succesfully')
return HttpResponseRedirect(reverse("listing", args=(id,)))
else:
return render(request, "auctions/listingPage.html", {
"bidform": bidform })
else:
bidform = BidForm()
return render(request, "auctions/listingPage.html", {
"bidform": bidform })
Please let me know if I need to include any more details.
Edited to add: models.py
class Listing(models.Model):
class NewManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='active')
options = (
('active', 'Active'),
('closed', 'Closed'),
)
title = models.CharField(max_length=64)
description = models.TextField(max_length=64)
start_price = models.DecimalField(max_digits=9, decimal_places=2, validators=[MinValueValidator(0.99)])
image = models.URLField(max_length=200, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="listings")
lister = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=None, null=True, blank=True, related_name="lister_user")
date_added = models.DateTimeField(default=timezone.now)
status = models.CharField(max_length=10, choices=options, default="active")
winner = models.ForeignKey('Bid', on_delete=models.CASCADE, null=True, related_name="bid_winner")
favourites = models.ManyToManyField(User, related_name="favourite", default=None, blank=True)
objects = models.Manager()
listingmanager = NewManager()
def __str__(self):
return f"{self.title} ({self.pk}, £{self.start_price}, {self.lister})"
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}"
Edited to add: updated 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')
listing = Listing.objects.get(pk=id)
if request.method == "POST":
bidform = BidForm(request.POST)
if bidform.is_valid():
if bidform.cleaned_data['bid_input'] < listing.start_price and bidform.cleaned_data['bid_input'] <= highest_bid:
return render(request, "auctions/listingPage.html", {
"listing": listing,
"error": "Make sure your bid is greater than the start price and current highest bid"
})
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:
return render(request, "auctions/listingPage.html", {
"bidform": bidform })
else:
bidform = BidForm()
return render(request, "auctions/listingPage.html", {
"bidform": bidform })
Upvotes: 3
Views: 1501
Reputation: 476534
highest_bid
is a dictionary that maps the name of the aggregate to a value, so for example { 'bid_input__max': Decimal('14.25') }
, you can thus unwrap the value from the dictionary by subscripting:
highest_bid = Bid.objects.filter(
id=id
).aggregate(Max('bid_input'))['bid_input__max']
but likely this will not give you the expected value. You filter on id=id
, so that means the queryset spans one records. You probably want to filter over the bid_item_id=id
or something similar:
highest_bid = Bid.objects.filter(
bid_item_id=id
).aggregate(Max('bid_input'))['bid_input__max']
based on your comment, it is possible that there is no bid yet. In that case, it will return a None
. We can use zero instead with:
highest_bid = Bid.objects.filter(
bid_item_id=id
).aggregate(Max('bid_input'))['bid_input__max'] or Decimal('0')
Upvotes: 2