Kazi380
Kazi380

Reputation: 85

How to dynamically set the limit_value of the build-in MinValueValidator inside a Django 3.1 ModelForm

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

Answers (1)

Kazi380
Kazi380

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

Related Questions