0

I was looking through this article to figure out how to set a field's value after a form is initialized. I don't see this in Django's docs, or maybe I'm putting in the wrong query, but is there a way to set the 'min' attribute value of a field in views.py? I'm asking because the min value can change constantly since it's a bid amount that is set each time a user bids above the highest_bid variable value in the view_listing function.

models.py

class Bid(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE, null=True)
    bid_amount = models.DecimalField(decimal_places=2, max_digits=10)

    def __str__(self):
        return f"{self.user}'s bid on {self.listing} is {self.bid_amount}"

forms.py

class BidForm(ModelForm):
    class Meta:
        model = Bid
        fields = ('bid_amount',)
        labels = { 'bid_amount': ('Your Bid'), }

        widgets = {
            'bid_amount': forms.NumberInput(attrs={'step': 00.50, 'class': 'form-control', 'style': 'width:50%', 'min': 0, 'title': '', 'placeholder': '' })
        }

views.py

@login_required
def view_listing(request, listing_id):
    listing = get_object_or_404(Listing, pk=listing_id)
    bids = Bid.objects.filter(listing=listing)

    all_bids = [] # Set an empty bid list to get all bids if any exist
    highest_bid = listing.starting_price # Set highest bid to 0 in case there are no bids

    if bids:
        bid_list = list(bids) # Convert Queryset to a list
        for index in bid_list:
            all_bids.append(index)
        highest_bid = all_bids[-1].bid_amount # Get the highest bid from all_bids
        listing.starting_price = highest_bid
    else:
        highest_bid # Else highest bid is 0

    if request.method == "GET":
        bid_form = BidForm()
        # Bid must be at least equal to highest bid
        bid_form.initial['bid_amount'] = highest_bid
        print(bid_form.initial['bid_amount'])

        try: # Check if the item is in the user's watchlist and pass the user's wishlist item name if it does
            Watchlist.objects.get(user=request.user, listing=listing)
            list_item = Watchlist.objects.get(user=request.user, listing_id=listing_id)
            return render(request, "auctions/viewlisting.html" , { "listing": listing, "item": list_item, "bids": bids, "bid_form": bid_form, "highest_bid": highest_bid  })
        except: # else return None
            None
        return render(request, "auctions/viewlisting.html" , { "listing": listing, "bids": bids, "bid_form": bid_form, "highest_bid": highest_bid })

    else: # On POST, allow a user to bid on an item
        try:

            bid_form = BidForm(request.POST)
            newbid = bid_form.save(commit=False)
            newbid.user = request.user

            if bid_form.is_valid():
                bid_amount = request.POST.get("bid_amount")
                # Got bid value from form. Left off here
                bid_value = bid_form.cleaned_data['bid_amount']

                if bid_value <= highest_bid:
                    messages.error(request, f"Your bid must be greater than ${highest_bid}")
                    return HttpResponseRedirect(listing.get_absolute_url())

                bid = Bid.objects.create(
                    listing=listing, user=request.user, bid_amount=bid_amount
                )
                bid.save()
                messages.success(request, "Your bid was saved.")
                return HttpResponseRedirect(listing.get_absolute_url())
            return redirect("auctions:index")

        except ValueError:
            messages.error(request, "Your bid was not accepted.")
            return render(request, "auctions/viewlisting.html", { "listing": listing, "error": ValueError })

viewlisting.html

some html code...
<!-- my bid form --!>
<form action="" method="POST">
   {% csrf_token %} {{ bid_form.as_p }}
   <input type="submit" value="Submit Your Bid" class="btn btn-danger">
</form>

In the "GET" section of my views.py file, where bid_form.initial['bid_amount'] = highest_bid is set, I also want to set the bid_form['bid_amount'] min value to highest_bid value each time it changes so that the user cannot enter anything lower than highest_bid in the form field. Is this at all possible to set in the views.py file? I've tried bid_form['bid_amount'].min but this shows up in the console <built-in method min of decimal.Decimal object at 0x7f99172a82d0>

JackJack
  • 181
  • 1
  • 1
  • 20

1 Answers1

1

You could make a custom function that checks all the other bid amounts, and returns a validation error if new bidding is less than any previous biddings

def validate_highest_bid(value):
    bids = Bid.objects.all()
    for bid in bids:
        if value <= bid.bid_amout:
            raise ValidationError(
            f"{value} must be higher than previous value",
            params={'value': value},
        )
class Bid(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE, null=True)
    bid_amount = models.DecimalField(decimal_places=2, max_digits=10, validators=[validate_highest_bid])

    def __str__(self):
        return f"{self.user}'s bid on {self.listing} is {self.bid_amount}"

You can then add something on save that handles the error

Jjkivai
  • 157
  • 1
  • 10