Django ChoiceField. How to pass initial value to the form - django

Sorry, I am a beginner. How to pass the variable / value x = "ABCDE" to the form?
#views.py
...
x = "ABCDE"
form1 = KSS_Form1(initial={'x':x})
context = {'form':form1, **context}
return render(request, 'af/size/01_kss_size2.html', context)
#forms.py
class KSS_Form1(forms.Form):
mat_geh_innen = forms.ChoiceField(choices=[], widget=forms.Select())
def __init__(self, *args, **kwargs):
super(KSS_Form1, self).__init__(*args, **kwargs)
self.initial['mat_geh_innen'] = x
self.fields['mat_geh_innen'].choices = \
[(i.id, "Housing: " + i.mat_housing.descr) \
for i in afc_select_housing_innenteile.objects.filter(Q(series__valuefg__exact=x) & Q(anw_01=True))]
as for now I get an error message
Exception Value: name 'x' is not defined
How shall I pass 2, 3, or more values if I have a number of different ChoiceFields in the Form?
Thank you

You can obtain this from the self.initial dictionary:
#forms.py
class KSS_Form1(forms.Form):
mat_geh_innen = forms.ChoiceField(choices=[], widget=forms.Select())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['mat_geh_innen'].initial = self.initial['x']
self.fields['mat_geh_innen'].choices = [
(i.id, 'Housing: ' + i.mat_housing.descr)
for i in
afc_select_housing_innenteile.objects.filter(series__valuefg=x, anw_01=True)
]
You however might want to look to a ModelChoiceField [Django-doc] that makes it more convenient to select model objects.

Willem Van Onsem, thank you for the hint.
For me this was the solution.
class KSS_Form1(forms.Form):
mat_geh_innen = forms.ChoiceField(choices=[], widget=forms.Select())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
x = self.initial['x']
self.fields['mat_geh_innen'].choices = [
(i.id, 'Housing: ' + i.mat_housing.descr)
for i in
afc_select_housing_innenteile.objects.filter(series__valuefg=x, anw_01=True)
]

Related

Render django.forms.fields.ChoiceField object

In my Django Project I have the following Problem:
I would like to have a dynamic Django form. In the first step the user is asked something by the first form. When I get the postmethod the variables should be used for genereating a new form
my views.py
def calc(request):
if request.method =="POST":
get_form = CalculationForm(request.POST)
if get_form.is_valid():
op = get_form.cleaned_data['op']
ab = get_form.cleaned_data['ab']
alternative = AlternativForm(optype = op, wsgroup = ab)
return render(request, 'calculated_lensar.html', {"alternativ" : alternativ})
else:
form = CalculationForm()
return render(request, 'calc.html', {'form': form})
The secondform (postmethod) looks like
class AlternativForm(forms.Form):
praep_button = ((3, 'hallo'), (4, 'tschüss'))
def __init__(self, optype, wsgroup, *args, **kwargs):
super(AlternativForm, self).__init__(*args, **kwargs) #dont know for what this is standing
self.optype = optype
self.wsgroup = wsgroup
self.values = self.read_db()
self.praep_button = self.buttons()
self.felder = self.blub()
self.neu2 = self.myfield_choices()
def read_db(self):
import sqlite3
....
return result #tuple with 15x5 elements
def buttons(self):
praep_button = []
for i in self.values:
praep_button.append((i[4], i[1]))
return praep_button #Just formating result from read_db in tuple(15x2)
def blub(self):
return forms.ChoiceField(widget=forms.RadioSelect, choices=self.praep_button)
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button) #print --><django.forms.fields.ChoiceField object at 0x751f9b90>
def myfield_choices(self):
field = self['myfield']
"""i think here is the problem.
Above 'myfield' is a django.forms.fields.ChoiceField object, but here it is rendered to html (like it should be). I have the code from https://stackoverflow.com/questions/6766994/in-a-django-form-how-do-i-render-a-radio-button-so-that-the-choices-are-separat.
But instead i should use field = self.felder (radioselect woth tuple of the db)"""
widget = field.field.widget
attrs = {}
auto_id = field.auto_id
if auto_id and 'id' not in widget.attrs:
attrs['id'] = auto_id
name = field.html_name
return widget.render(name, field.value(), attrs=attrs)
#return widget.get_renderer(name, field.value(), attrs=attrs)
So all in all I hope the problem is clear.
If i am using AlternativForm() i get the constant form. Instead i would like to get a dynamic form. If I access in views.py:
alternative = AlternativForm(optype = op, wsgroup = ab)
alternative = alternativ.felder
than I get . Can I render that to html?
If I set in forms.py:
field = self.felder
than I get the error that it is a field and not a widget
Thank you for reading!
You just need to assign the choices in the form's __init__() method. Almost what you're doing, but instead of defining self.felder to be a field, you need to use the already initialised form's fields:
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button)
def __init__(self, optype, wsgroup, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myfield'].choices = self.get_choices(optype, wsgroup) # create your choices in this method
def get_choices(optype, wsgroup):
# call your other methods here
return praep_button

How to set Django form initial value only one field?

forms.py
class ExForm(forms.Form):
a = forms.CharField(max_length=100)
b = forms.ChoiceField(choices=SOME_CHOICES)
c = forms.ChoiceField(choices=SOME_CHOICES)
def __init__(self, request, *args, **kwargs):
super(ExForm, self).__init__(*args, **kwargs)
self.initial['a'] = 'something value'
views.py
def view(request):
form = ExForm(request.GET or None)
return render(request, 'a.html', {'form': form})
I want to set an initial value to 'a' field only.
When I submit this form, b and c fields values not set in the form from request.GET.
It works.
def __init__(self, request, *args, **kwargs):
super(ExForm, self).__init__(*args, **kwargs)
self.initial['a'] = 'something value'
self.initial['b'] = request.b
self.initial['c'] = request.c
I want to know how to set initial value only one field.
Do I set all field initial values?
You can use initial field's argument:
class ExForm(forms.Form):
a = forms.CharField(max_length=100, initial='Some value')
b = forms.ChoiceField(choices=SOME_CHOICES)
c = forms.ChoiceField(choices=SOME_CHOICES)
Yes, you can specify a default value to a particular FormField, you can specify it using initial:
class ExForm(forms.Form):
a = forms.CharField(max_length=100, initial="Any Value")
b = forms.ChoiceField(choices=SOME_CHOICES)
c = forms.ChoiceField(choices=SOME_CHOICES)
...
Or specifying a default value from choices, use default='Some Value'
SOME_CHOICES = [
('1','Value1')
('2','Value2')
]
class ExForm(forms.Form):
a = forms.CharField(max_length=100, initial="Any Value")
b = forms.ChoiceField(choices=SOME_CHOICES, default='1')
c = forms.ChoiceField(choices=SOME_CHOICES, default='2')
...
Hope it clears your doubt.

Dynamic Multiwidget/MultivalueField from Model

The beginning is simple:
class Question(models.Model):
question_string = models.CharField(max_length=255)
answers = models.CharField(max_length=255)
answers are json of list of strings e.g ['Yes', 'No']. Number of answers is dynamic.
The challenge for me now is to write a form for this model.
Current state is:
class NewQuestionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(NewQuestionForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['answers'] = AnswerField(num_widgets=len(json.loads(self.instance.answers)))
class Meta:
model = Question
fields = ['question']
widgets = {
'question': forms.TextInput(attrs={'class': "form-control"})
}
class AnswerField(forms.MultiValueField):
def __init__(self, num_widgets, *args, **kwargs):
list_fields = []
list_widgets = []
for garb in range(0, num_widgets):
field = forms.CharField()
list_fields.append(field)
list_widgets.append(field.widget)
self.widget = AnswerWidget(widgets=list_widgets)
super(AnswerField, self).__init__(fields=list_fields, *args, **kwargs)
def compress(self, data_list):
return json.dumps(data_list)
class AnswerWidget(forms.MultiWidget):
def decompress(self, value):
return json.loads(value)
The problem is: i get 'the JSON object must be str, not 'NoneType'' in template with '{{ field }}'
What is wrong?
I found the problem. I forgot to add 'answers' to class Meta 'fields'.
So my example of dynamic Multiwidget created from Model is:
class NewQuestionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# need this to create right number of fields from POST
edit_mode = False
if len(args) > 0:
edit_mode = True
answer_fields = 0
for counter in range(0, 20):
answer_key = "answers_" + str(counter)
if args[0].get(answer_key, None) is not None:
answer_fields = counter + 1
else:
break
super(NewQuestionForm, self).__init__(*args, **kwargs)
if edit_mode:
self.fields['answers'] = AnswerField(num_widgets=answer_fields, required=False)
# get number of fields from DB
elif 'instance' in kwargs:
self.fields['answers'] = AnswerField(num_widgets=len(json.loads(self.instance.answers)), required=False)
else:
self.fields['answers'] = AnswerField(num_widgets=1, required=False)
class Meta:
model = Question
fields = ['question', 'answers']
widgets = {
'question': forms.TextInput(attrs={'class': "form-control"})
}
def clean_answers(self):
temp_data = []
for tdata in json.loads(self.cleaned_data['answers']):
if tdata != '':
temp_data.append(tdata)
if not temp_data:
raise forms.ValidationError('Please provide at least 1 answer.')
return json.dumps(temp_data)
'clean_answers' has 2 porposes: 1. Remove empty answers. 2. I failed to set required attribute on first widget. So i check here at least 1 answer exists
class AnswerWidget(forms.MultiWidget):
def decompress(self, value):
if value:
return json.loads(value)
else:
return ['']
class AnswerField(forms.MultiValueField):
def __init__(self, num_widgets, *args, **kwargs):
list_fields = []
list_widgets = []
for loop_counter in range(0, num_widgets):
list_fields.append(forms.CharField())
list_widgets.append(forms.TextInput(attrs={'class': "form-control"}))
self.widget = AnswerWidget(widgets=list_widgets)
super(AnswerField, self).__init__(fields=list_fields, *args, **kwargs)
def compress(self, data_list):
return json.dumps(data_list)

Django unbound and bound forms

I am creating a service where people can create guides including decks for a video game called hearthstone. First one has to select their hero:
class SelectHero(ListView):
template_name = 'hsguides/select_hero.html'
model = Hero
def get_context_data(self, **kwargs):
context = super(SelectHero, self).get_context_data(**kwargs)
context['heroes'] = Hero.objects.all()
return context
And when it is selected I render a template with the deck and the guide form. Now when I use this setup:
view
#login_required(login_url="/accounts/login")
def guide_create_view(request, hero):
print(DeckForm)
return render(request, 'hsguides/guide_create.html', {
'DeckForm': DeckForm(hero),
'GuideForm': GuideForm,
})
form
class DeckForm(ModelForm):
class Meta:
model = Deck
exclude = ('dust', 'hero',)
def __init__(self, hero=None, **kwargs):
super(DeckForm, self).__init__(**kwargs)
if hero:
self.fields['weapon_cards'].queryset = Weapon.objects.filter(Q(card_class='neutral') |
Q(card_class=hero))
self.fields['spell_cards'].queryset = Spell.objects.filter(Q(card_class='neutral') |
Q(card_class=hero))
self.fields['minion_cards'].queryset = Minion.objects.filter(Q(card_class='neutral') |
Q(card_class=hero))
I see that this form is unbound and it is not valid when I want to use it in my save view
#login_required(login_url="/accounts/login")
def guide_save(request):
if request.method == "POST":
deck_form = DeckForm(request.POST)
guide_form = GuideForm(request.POST)
print(guide_form.is_bound) # printed value, True
print(deck_form.is_bound) # printed value, False
if guide_form.is_valid() and deck_form.is_valid():
new_deck = deck_form.save(commit=False)
new_deck.dust = 0 #TODO create a count method for the dust field!
new_deck.save()
new_guide = guide_form.save(commit=False)
new_guide.author = Account.objects.get(id=request.user.id)
new_guide.deck = Deck.objects.get(id=new_deck.id)
new_guide.save()
else:
print(guide_form.errors)
print(deck_form.errors)
else:
deck_form = DeckForm()
guide_form = GuideForm()
return HttpResponseRedirect('/guides/search-guide/')
Now I am really dependent on this part:
def __init__(self, hero=None, **kwargs):
super(DeckForm, self).__init__(**kwargs)
if hero:
self.fields['weapon_cards'].queryset = Weapon.objects.filter(Q(card_class='neutral') |
Q(card_class=hero))
self.fields['spell_cards'].queryset = Spell.objects.filter(Q(card_class='neutral') |
Q(card_class=hero))
self.fields['minion_cards'].queryset = Minion.objects.filter(Q(card_class='neutral') |
Q(card_class=hero))
But I don't know how to validate the deck form and save it in the best way. How can I approach this situation the best with regards to best practices?
You've redefined the signature of your form so that the first argument is hero, but then you instantiate it with just request.POST.
Instead of doing that, get hero from the kwargs, and always make sure you accept both args and kwargs.
def __init__(self, *args, **kwargs):
hero = kwargs.pop('hero', None)
super(DeckForm, self).__init__(*args, **kwargs)
if hero:
...
Remember to pass the hero argument in by keyword:
return render(request, 'hsguides/guide_create.html', {
'DeckForm': DeckForm(hero=hero),
'GuideForm': GuideForm,
})

How can I pass a value to Django Form's init method from a view?

forms.py
class AddDuration(forms.Form):
def __init__(self, *args, **kwargs):
super(AddDuration, self).__init__(*args, **kwargs)
// set value to relates_to_choices
relates_to_choices = ????????????? // Something like self.choices
self.fields['duration'].choices = relates_to_choices
duration = forms.ChoiceField(required=True)
Now, I have a views.py file that has a class
class AddDurationView(FormView):
template_name = 'physician/add_duration.html'
form_class = AddDurationForm
Override the get_form_kwargs() method on the view.
views.py
class AddDurationView(FormView):
template_name = 'physician/add_duration.html'
form_class = AddDurationForm
def get_form_kwargs(self):
kwargs = super(AddDurationView, self).get_form_kwargs()
kwargs['duration_choices'] = (
('key1', 'display value 1'),
('key2', 'display value 2'),
)
return kwargs
forms.py
class AddDurationForm(forms.Form):
duration = forms.ChoiceField(required=True)
def __init__(self, duration_choices, *args, **kwargs):
super(AddDurationForm, self).__init__(*args, **kwargs)
// set value to duration_choices
self.fields['duration'].choices = duration_choices