I have these two Django Models:
class Listing(models.Model):
...
price = models.DecimalField(
decimal_places=2,
max_digits=20,
default=0,
validators=[MinValueValidator(Decimal('0.01'))]
)
class Bid(models.Model):
...
listing = models.ForeignKey(Listing, default=None, on_delete=models.CASCADE)
amount = models.DecimalField(
decimal_places=2,
max_digits=20,
default=0
validators=[MinValueValidator(Decimal(listing.price))]
)
I want to validate that Bid.amount > Bid.listing.price
.
When I try to do it the with the help of MinValueValidator
from django.core.validators
I get this error:
validators=[MinValueValidator(Decimal(listing.price))])
^^^^^^^^^^^^^
AttributeError: 'ForeignKey' object has no attribute 'price'
I also tried referencing listing.price
with a self
attribute and got the following error:
validators=[MinValueValidator(Decimal(self.listing.price))])
^^^^
NameError: name 'self' is not defined
I know I can check the above condition in "views.py" once the Model.Form is submitted, but I wonder if there is a way to validate it with Django Validators.
Any help would be appreciated!
I figured it out by creating a custom validator in forms.py
:
from .models import Listing, Bid
from django.forms import ModelForm
from decimal import Decimal
from django.core.exceptions import ValidationError
class BidForm(ModelForm):
class Meta():
model = Bid
fields = ["amount"]
def clean_amount(self):
amount = self.cleaned_data["amount"]
if Decimal(amount) < Decimal("0.01"):
raise ValidationError(
"Ensure this value is greater than or equal to 0.01."
)
elif Decimal(amount) <= Decimal(self.instance.listing.price):
raise ValidationError("Ensure this value exceeds the price.")
return amount
Then, in views.py
:
from django.http import HttpResponseRedirect
from .models import Listing
from .forms import BidForm
from decimal import Decimal
def listing(request, listing_id):
listing = Listing.objects.get(pk=listing_id)
if request.method == "POST":
form = BidForm(request.POST)
form.instance.holder = request.user
form.instance.listing = listing
if form.is_valid():
form.save()
Listing.objects.filter(pk=listing_id).update(
price=Decimal(request.POST["amount"])
)
return HttpResponseRedirect(
f"/listing/{listing_id}",
{"form":form, "listing":listing}
)
else:
form = BidForm()
return render(request, "auctions/listing.html", {"form": form, "listing": listing})
Instead of validating Django Model's fields, I validate ModelForm submission with the help of clean_amount()
method. Per Django's Documentation:
This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is.
I hope this answer helps someone.