Reputation: 193
I want to make shure that the current value of the bid field
is not less than current biggest bid
. This is my form with a custom clean
method.
Form:
class Place_A_Bid_Form(forms.Form):
listing = forms.CharField(widget=forms.TextInput(attrs={"type":"hidden"}))
bid = forms.IntegerField(widget=forms.NumberInput(attrs={"class":"form-control"}), min_value=1)
def clean_bid(self, biggestBid):
bid = self.cleaned_data["bid"]
if bid < biggestBid:
raise ValidationError("""New bid shouldn't be less than starting bid, or
if any bids have been placed then new bid
should be greater than current biggest bid""")
return bid
View:
def place_a_bid(request):
if request.method == "POST":
form = Place_A_Bid_Form(request.POST)
user = User.objects.get(username=request.user.username)
biggest_bid = Bid.objects.filter(user=user).aggregate(Max("amount"))
if form.is_valid():
data = form.cleaned_data
user_obj = User.objects.get(username=request.user.username)
listing_obj = Listing.objects.get(title=data["listing"])
Bid.objects.update_or_create(
user=user_obj,
listing=listing_obj,
amount=data["bid"]
)
return redirect(listing_obj)
In view I am extracting current value that I am going to compare to, and I can't figure out how to pass this value to my form field's clean
method. Or maybe I'm doing this wrong? So how to do properly this sort of validation?
Upvotes: 2
Views: 2419
Reputation: 476614
Django's IntegerField
[Django-doc] can validate a minimum value without any extra logic, you can set the .min_value
and add a validator with:
from django.core.validators import MinValueValidator
class Place_A_Bid_Form(forms.Form):
listing = forms.CharField(widget=forms.TextInput(attrs={'type': 'hidden'}))
bid = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control'}))
def __init__(self, *args, **kwargs, min_bid=0):
super().__init__(*args, **kwargs)
bid = self.fields['bid']
min_bid += 1
bid.min_value = min_bid
bid.validators.add(MinValidator(min_bid))
so then you only need to pass the min_bid
to the form:
from django.contrib.auth.decorators import login_required
@login_required
def place_a_bid(request):
biggest_bid = Bid.objects.filter(user=request.user).aggregate(
min_bid=Max('amount')
)['min_bid'] or 0
if request.method == 'POST':
form = Place_A_Bid_Form(request.POST, min_bid=biggest_bid)
if form.is_valid():
data = form.cleaned_data
listing_obj = Listing.objects.get(title=data['listing'])
Bid.objects.update_or_create(
user=request.user,
listing=listing_obj,
amount=data['bid']
)
return redirect(listing_obj)
else:
form = Place_A_Bid_Form(min_bid=biggest_bid)
return render(request, 'app_name/name-of-template.html', {'form': form})
This also construct a form in case of a GET request with the biggest bidding as minimum bid. This will also add this as min="biggest-bid"
in the HTML so the browser can validate this. This can thus also validate the bid already at client side before submitting the form, but will also properly validate the form at the server side.
It might however be better to pass the listing not through the form, but as a URL parameter.
Note: You can limit views to a view to authenticated users with the
@login_required
decorator [Django-doc].
Upvotes: 0
Reputation: 401
class Place_A_Bid_Form(forms.Form):
listing = forms.CharField(widget=forms.TextInput(attrs={"type":"hidden"}))
bid = forms.IntegerField(widget=forms.NumberInput(attrs={"class":"form-control"}),
min_value=1)
def __init__(self,biggestBid=0 *args, **kwargs):
super().__init__(*args, **kwargs)
self.biggestBid = biggestBid
def clean_bid(self):
bid = self.cleaned_data["bid"]
if bid < self.biggestBid:
raise ValidationError("""New bid shouldn't be less than starting bid, or
if any bids have been placed then new bid
should be greater than current biggest bid""")
return bid
and then in views.py:
def place_a_bid(request):
if request.method == "POST":
dict = Bid.objects.filter(user=user).aggregate(Max("amount"))
form = Place_A_Bid_Form(biggestBid=dict['amount__max'], data=request.POST)
user = User.objects.get(username=request.user.username)
if form.is_valid():
data = form.cleaned_data
user_obj = User.objects.get(username=request.user.username)
listing_obj = Listing.objects.get(title=data["listing"])
Bid.objects.update_or_create(
user=user_obj,
listing=listing_obj,
amount=data["bid"]
)
return redirect(listing_obj)
Upvotes: 4