Reputation: 85
I'm trying to dynamically set the limit_value of the build-in MinValueValidator inside a Django 3.1 ModelForm. The below code works for a fixed limit_value of 10 (see line 21 in views.py).
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="bids")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="bids")
bid = models.DecimalField(decimal_places=2, max_digits=9)
views.py
from django.contrib.auth import authenticate, login, logout
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django import forms
from .models import User, Listing, Category, Bid
from django.db.models import Max
from decimal import Decimal, DecimalException
from django.core.validators import MaxValueValidator, MinValueValidator
from django.core.exceptions import ValidationError
class NewBidForm(forms.ModelForm):
class Meta:
model = Bid
fields = '__all__'
widgets = {
'user': forms.HiddenInput(),
'listing': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
super(NewBidForm, self).__init__(*args, **kwargs)
self.fields['user'].show_hidden_initial=True
self.fields['listing'].show_hidden_initial=True
self.fields['bid'].validators=[MinValueValidator(10)]
def clean(self):
if 'user' in self.changed_data or 'listing' in self.changed_data:
raise forms.ValidationError('Non editable field have changed!')
return self.cleaned_data
def index(request):
listings = Listing.objects.all()
return render(request, "auctions/index.html", {
"listings" : listings,
})
def listing(request, listing_id):
if request.method == 'POST':
data = request.POST
form = NewBidForm(data)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("index"))
else:
listing = Listing.objects.get(pk=listing_id)
bids = Bid.objects.filter(listing=listing)
if bids:
highest_bid = bids.aggregate(Max('bid'))['bid__max']
else:
highest_bid = listing.starting_bid
return render(request, "auctions/listing.html", {
"listing" : listing,
"highest_bid" : highest_bid,
"form" : form
})
else:
listing = Listing.objects.get(pk=listing_id)
bids = Bid.objects.filter(listing=listing)
if bids:
highest_bid = bids.aggregate(Max('bid'))['bid__max']
else:
highest_bid = listing.starting_bid
form = NewBidForm(initial={'listing':listing,'user':request.user})
return render(request, "auctions/listing.html", {
"listing" : listing,
"highest_bid" : highest_bid,
"form" : form
})
However when I try passing a variable via 'my_arg' to the NewBidForm's init method during instantiation of the ModelForm, I get the following error messages:
Below is the modified code in views.py
views.py
class NewBidForm(forms.ModelForm):
class Meta:
model = Bid
fields = '__all__'
widgets = {
'user': forms.HiddenInput(),
'listing': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
my_arg = kwargs.pop('my_arg')
super(NewBidForm, self).__init__(*args, **kwargs)
self.fields['user'].show_hidden_initial=True
self.fields['listing'].show_hidden_initial=True
self.fields['bid'].validators=[MinValueValidator(my_arg)]
def clean(self):
if 'user' in self.changed_data or 'listing' in self.changed_data:
raise forms.ValidationError('Non editable field have changed!')
return self.cleaned_data
def index(request):
listings = Listing.objects.all()
return render(request, "auctions/index.html", {
"listings" : listings,
})
def listing(request, listing_id):
if request.method == 'POST':
data = request.POST
form = NewBidForm(data)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("index"))
else:
listing = Listing.objects.get(pk=listing_id)
bids = Bid.objects.filter(listing=listing)
if bids:
highest_bid = bids.aggregate(Max('bid'))['bid__max']
else:
highest_bid = listing.starting_bid
return render(request, "auctions/listing.html", {
"listing" : listing,
"highest_bid" : highest_bid,
"form" : form
})
else:
listing = Listing.objects.get(pk=listing_id)
bids = Bid.objects.filter(listing=listing)
if bids:
highest_bid = bids.aggregate(Max('bid'))['bid__max']
else:
highest_bid = listing.starting_bid
form = NewBidForm(initial={'listing':listing,'user':request.user}, my_arg=12)
return render(request, "auctions/listing.html", {
"listing" : listing,
"highest_bid" : highest_bid,
"form" : form
})
Can anyone tell me how to pass a variable to the init method inside the ModelForm during instantiation? An alternative solution for changing the limit_value of the build in MinValueValidator at runtime would also be acceptable. However, i don't like to redefine fromfields in the ModelForm.
BR, Konrad
Upvotes: 1
Views: 716
Reputation: 85
Below code examples show the answer to my question.
Model form class
class NewBidForm(forms.ModelForm):
class Meta:
model = Bid
fields = '__all__'
def __init__(self, *args, **kwargs):
my_arg = kwargs.pop('my_arg')
super(NewBidForm, self).__init__(*args, **kwargs)
self.fields['bid'].validators=[MinValueValidator(my_arg)]
Then every time a form object is instantiated make sure to pass in the my_variable
like so:
form = NewBidForm(my_arg=my_variable)
My mistake was to instantiate the form at two locations in my code but only passing the argument in one of the instances. I
Upvotes: 1