Django: forms.ChoiceField, overriding forms __init__

2.8k Views Asked by At

I am trying to implement a forms.ChoiceField() with values from a view. I already can do it if I declare the choices in the forms.py, but that's not what I need.

views.py:

def add_crime(request):
        values = [('1','um'),('2','dois'),('3','tres')]
        if request.method == 'POST':
            form = AddCrimeForm(request.POST, values)
            if form.is_valid():
                # do stuff
                return redirect('show_crime')
        else:
            form = AddCrimeForm(request.GET)
        return render(request, 'add_crime.html', {'form': form})

forms.py:

class AddCrimeForm(forms.Form):
    tests = forms.ChoiceField()
    def __init__(self, testList, args, **kwargs):
        self.testList = testList
        super(AddCrimeForm,self).__init__(*args, **kwargs)  # testList not in args!
        self.fields['tests'].widget = forms.CheckboxSelectMultiple()
        self.fields['tests'].choices = self.testList

Error:

AttributeError: 'tuple' object has no attribute 'get'

From the feedback, do I have to implement another __init__ with one argument in forms.py? That's what I would try in Java.

My final goal is to implement two ChoiceField and the second one would depend from the first one. Is there a better way?

1

There are 1 best solutions below

0
On BEST ANSWER

There are two mistakes here: the first one is in the AddCrimeForm. Here *args in the __init__ header needs an asterisk (*):

class AddCrimeForm(forms.Form):

    tests = forms.MultipleChoiceField()

    def __init__(self, testList, *args, **kwargs):
        self.testList = testList
        super(AddCrimeForm,self).__init__(*args, **kwargs)  # testList not in args!
        self.fields['tests'].widget = forms.CheckboxSelectMultiple()
        self.fields['tests'].choices = self.testList

You also probably want to make forms a MultipleChoiceField [Django-doc] if you want to select multiple choices.

In your view, you should construct an AddCrimeForm, with the list of values as first element, since it is defined as first element in your AddCrimeForm:

def add_crime(request):
        values = [('1','um'),('2','dois'),('3','tres')]
        if request.method == 'POST':
            form = AddCrimeForm(values, request.POST)
            if form.is_valid():
                # do stuff
                return redirect('show_crime')
        else:
            form = AddCrimeForm(values)
        return render(request, 'add_crime.html', {'form': form})

Unless you want to handle the querystring in your form, one typically does not pass request.GET to the form, since otherwise it will take values that are defined in the querystring.