Initialize django CheckboxSelectMultiple with everything unchecked? - django

I have a model form that is rendering a ModelMultipleChoiceField as a CheckboxSelectMultiple
class VisitForm(ModelForm):
def __init__(self, queryset=None, *args, **kwargs):
super(VisitForm, self).__init__(*args, **kwargs)
if queryset:
self.fields['students'] = forms.ModelMultipleChoiceField(
queryset=queryset,
widget=forms.CheckboxSelectMultiple()
)
It's rendered very simply in the template right now:
<div class="form-group">
{{field.label}}
{{field}}
</div>
It initializes okay with the selections I expect to be there - but by default both options are checked
I am trying to figure out how to intialize the boxes so they are unchecked by default.
What is the best way to accomplish this?
It's initialized in the view like this:
visitor = Visitor.objects.get(unique_id=unique_id)
students = Student.objects.filter(parents__unique_id=unique_id)
form = VisitForm(initial={'visitor':visitor, 'students':students}, queryset=students)

You are passing the students queryset as the initial data, so Django selects every student in the queryset.
You could use an empty queryset Student.objects.none(), but it is even easier to remove students from the initial dictionary:
form = VisitForm(initial={'visitor':visitor}, queryset=students)

Related

Prepopulate non-model form attribute when instantiating in Django

I want to set a minimum value for a DecimalField attribute on a form at the time when I instantiate it in the view. And I want to get that minimum value from an object I gather from the database. I made it (sort of) work by manually putting in the html form in the template, but want to refactor it to use the form class because I can do more useful things with data in the view than I can in the template.
Based on my reading of other questions and docs, I can't set attributes with the .initial argument. I thought likely I need to override the __init__ method on the form, but I'm pretty sure I'm not doing this right and it makes no sense syntactically. Here's what I have tried:
class BidForm(forms.Form):
bid = forms.DecimalField(decimal_places=2)
listing_bid = forms.CharField(widget=forms.HiddenInput())
def __init__(self, min_bid, listing_pk, *args, **kwargs):
super(BidForm, self).__init__(*args, **kwargs)
self.fields['bid'].min = min_bid
self.fields['listing_bid'] = listing_pk
My idea is to have this form take a min_bid and a listing_pk, and fill the "min" attribute on the html input with whatever is in the min_bid variable. I want to put the listing_pk that's passed in as the value in a hidden field called "listing_bid". If it helps clarify, I'm trying to generate html equivalent to:
<input type="number" name="bid" min="{{ listing.current_bid }}">
<input type="hidden" name="listing_bid" value="{{ listing.pk }}">
In the view, I'd like to say something like:
form = BidForm(min_bid=foo, listing_bid=bar)
Then pass that into the template context for rendering.
Does this make sense? I've found some discussion of it in the context of ModelForms but can't wrap my head around how to do this with regular forms.
Edit: for future reference, here is what worked for me. I deleted the bid attribute on the form because there's no case where I would want to init it without supplying a min_bid:
class BidForm(forms.Form):
listing_bid = forms.CharField(widget=forms.HiddenInput())
def __init__(self, min_bid, listing_pk, *args, **kwargs):
super(BidForm, self).__init__(*args, **kwargs)
self.fields['bid'] = forms.DecimalField(decimal_places=2, min_value=min_bid)
self.fields['listing_bid'].initial = listing_pk
You can make use of .initial attribute:
class BidForm(forms.Form):
bid = forms.DecimalField(decimal_places=2)
listing_bid = forms.CharField(widget=forms.HiddenInput())
def __init__(self, min_bid, listing_pk, *args, **kwargs):
super(BidForm, self).__init__(*args, **kwargs)
self.fields['bid'].min_value = min_bid
self.fields['listing_bid'].intial = listing_pk

Setting HTML required attribute in Django formsets

To achieve client-side validation making the user to fill out non-null fields before submitting, I use the following code:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
if field.required == True:
field.widget.attrs['required'] = ''
This translates to the following html in the template:
<input class="form-control" ........ required="">
Now, when I use formsets, the required HTML attribute does not appear in the tempalte. The question is, how do I make Django formsets inherit this required attribute from the original forms - if it's possible whatsoever?
MyFormSet = modelformset_factory(MyModel, fields=(...))
formset = MyFormSet(queryset = MyModel.objects.filter(...))
How about creating formset from MyForm?
MyFormSet = forms.formset_factory(MyForm)
After spending three hours, I've solved the issue by setting a custom form in modelformset_factory. Maybe it will be useful for someone else
MyFormSet = modelformset_factory(MyModel, MyForm)
formset = MyFormSet(queryset = MyModel.objects.filter(...))
Specifying MyForm effectively tells Django to inherit all widget attributes that you have once declared in the MyForm definition.
Using formset_factory is for some reasons a headache for me, primarily because it accepts values instead of querysets which means I have to bother about foreign key relationships.

Rendering individual fields in template in a custom form

I am having trouble rendering individual fields in my template. I have a custom form that renders a multichoice widget and Charfield. I want to render the widget and Charfield individually, so I can place the Charfield on the right of the widget rather than on the bottom (which is what Django does by default). Is there a way to call individual fields in my form in the template?
forms.py
class UpdateStateOptionWithOutcomesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
disease=kwargs.pop('disease', None)
super(UpdateStateOptionWithOutcomesForm, self).__init__(*args, **kwargs)
self.fields['relevantoutcome']=forms.ModelMultipleChoiceField(queryset=Outcome.objects.filter(relevantdisease_id=disease),required=True, widget=forms.CheckboxSelectMultiple)
self.fields['relevantoutcome'].label="Treatment Outcomes"
outcome_qs=Outcome.objects.filter(relevantdisease_id=disease)
for outcome in outcome_qs:
self.fields['outcomevalue_%s' % outcome.pk] = forms.CharField(required=False)
self.fields['outcomevalue_%s' % outcome.pk].label = "Outcome Value"
{{ form.fieldname }} will render only the field widget by its name.
Check out Customizing the form template.
The following code could be helpful to someone. Here is a way to get a rendering field with fieldname from a form:
form.fields[fieldname].get_bound_field(form, fieldname)

django dynamic data driven form fields [duplicate]

I'm working on something like an online store. I'm making a form in which the customer buys an item, and she can choose how many of these item she would like to buy. But, on every item that she buys she needs to choose what its color would be. So there's a non-constant number of fields: If the customer buys 3 items, she should get 3 <select> boxes for choosing a color, if she buys 7 items, she should get 7 such <select> boxes.
I'll make the HTML form fields appear and disappear using JavaScript. But how do I deal with this on my Django form class? I see that form fields are class attributes, so I don't know how to deal with the fact that some form instance should have 3 color fields and some 7.
Any clue?
Jacob Kaplan-Moss has an extensive writeup on dynamic form fields:
http://jacobian.org/writing/dynamic-form-generation/
Essentially, you add more items to the form's self.fields dictionary during instantiation.
Here's another option: how about a formset?
Since your fields are all the same, that's precisely what formsets are used for.
The django admin uses FormSets + a bit of javascript to add arbitrary length inlines.
class ColorForm(forms.Form):
color = forms.ChoiceField(choices=(('blue', 'Blue'), ('red', 'Red')))
ColorFormSet = formset_factory(ColorForm, extra=0)
# we'll dynamically create the elements, no need for any forms
def myview(request):
if request.method == "POST":
formset = ColorFormSet(request.POST)
for form in formset.forms:
print "You've picked {0}".format(form.cleaned_data['color'])
else:
formset = ColorFormSet()
return render(request, 'template', {'formset': formset}))
JavaScript
<script>
$(function() {
// this is on click event just to demo.
// You would probably run this at page load or quantity change.
$("#generate_forms").click(function() {
// update total form count
quantity = $("[name=quantity]").val();
$("[name=form-TOTAL_FORMS]").val(quantity);
// copy the template and replace prefixes with the correct index
for (i=0;i<quantity;i++) {
// Note: Must use global replace here
html = $("#form_template").clone().html().replace(/__prefix_/g', i);
$("#forms").append(html);
};
})
})
</script>
Template
<form method="post">
{{ formset.management_form }}
<div style="display:none;" id="form_template">
{{ formset.empty_form.as_p }}
</div><!-- stores empty form for javascript -->
<div id="forms"></div><!-- where the generated forms go -->
</form>
<input type="text" name="quantity" value="6" />
<input type="submit" id="generate_forms" value="Generate Forms" />
you can do it like
def __init__(self, n, *args, **kwargs):
super(your_form, self).__init__(*args, **kwargs)
for i in range(0, n):
self.fields["field_name %d" % i] = forms.CharField()
and when you create form instance, you just do
forms = your_form(n)
it's just the basic idea, you can change the code to whatever your want. :D
The way I would do it is the following:
Create an "empty" class that inherits from froms.Form, like this:
class ItemsForm(forms.Form):
pass
Construct a dictionary of forms objects being the actual forms, whose composition would be dependent on the context (e.g. you can import them from an external module). For example:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
In views, you can use python native "type" function to dynamically generate a Form class with variable number of fields.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
Pass the content to the form and render it in the template:
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
The "content" is a dictionary of field values (e.g. even request.POST would do).
You can see my whole example explained here.
Another approach: Rather than breaking the normal field initialization flow, we can override fields with a mixin, return an OrderedDict of dynamic fields in generate_dynamic_fields which will be added whenever its set.
from collections import OrderedDict
class DynamicFormMixin:
_fields: OrderedDict = None
#property
def fields(self):
return self._fields
#fields.setter
def fields(self, value):
self._fields = value
self._fields.update(self.generate_dynamic_fields())
def generate_dynamic_fields(self):
return OrderedDict()
A simple example:
class ExampleForm(DynamicFormMixin, forms.Form):
instance = None
def __init__(self, instance = None, data=None, files=None, auto_id='id_%s', prefix=None, initial=None,
error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None,
use_required_attribute=None, renderer=None):
self.instance = instance
super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order,
use_required_attribute, renderer)
def generate_dynamic_fields(self):
dynamic_fields = OrderedDict()
instance = self.instance
dynamic_fields["dynamic_choices"] = forms.ChoiceField(label=_("Number of choices"),
choices=[(str(x), str(x)) for x in range(1, instance.number_of_choices + 1)],
initial=instance.initial_choice)
return dynamic_fields

Django: Set field choices from view?

I've got some <selects> that I need to populate with some choices that depend on the currently logged in user. I don't think this is possible (or easy) to do from inside the form class, so can I just leave the choices blank and set them in the view instead? Or what approach should I take?
Not sure if this is the best answer, but in the past I have set the choices of a choice field in the init of the form - you could potentially pass your choices to the constructor of your form...
You could build your form dynamically in you view (well, actually i would rather keep the code outside the view in it's own function and just call it in the view but that's just details)
I did it like this in one project:
user_choices = [(1, 'something'), (2, 'something_else')]
fields['choice'] = forms.ChoiceField(
choices=user_choices,
widget=forms.RadioSelect,
)
MyForm = type('SelectableForm', (forms.BaseForm,), { 'base_fields': fields })
form = MyForm()
Obviously, you will want to create the user_choices depending on current user and add whatever field you need along with the choices, but this is a basic principle, I'll leave the rest as the reader exercise.
Considering that you have included the user as a parameter, I would solve this using a custom tag.
In your app/templatetags/custom_tags.py something like this:
#register.simple_tag
def combo(user, another_param):
objects = get_objects(user, another_param)
str = '<select name="example" id="id_example">'
for object in objects:
str += '<option value="%s">%s</option>' % (object.id, object.name)
str += '</select>'
return mark_safe(str)
Then in your template:
{% load custom_tags %}
{% special_select user another_param %}
More about custom tags http://docs.djangoproject.com/en/dev/howto/custom-template-tags/
Django create dynamic forms - It works !!
Forms.py
class MyForm(forms.Form):
""" Initialize form values from views"""
select=forms.BooleanField(label='',required=False)
field_1=forms.CharField(label='',widget=forms.TextInput(attrs= \
{'size':'20','readonly':'readonly',}))
field_2=forms.ChoiceField(widget=forms.Select(), \
choices=((test.id,test.value) for test in test.objects.all()))
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
input=kwargs.get('initial',{})
get_field_1_initial_input_from_views=re.sub("\[|\]|u'|'","",str (input.values()))
# override field_2 choices based on field_1 input
try:
# filter choices
self.fields[‘field_2'].choices=((test.id,test.value) for test in test.objects.filter ( --------)))
except:
pass
Views.py
def views_function(request,val):
"""Dynamically generate input data for formset."""
Initial_data=[]
initial_data.append({'field_1':data.info})
#Initializing formset
MyFormSet=formset_factory(MyForm,extra=0)
formset=MyFormSet(initial=initial_data)
context={'formset':formset}
if request.method == 'POST':
formset=MyFormSet(request.POST,request.FILES)
if formset.is_valid():
# You can work with the formset dictionary elements in the views function (or) pass it to
#Forms.py script through an instance of MyForm
return HttpResponse(formset.cleaned_data)
return render_to_response(‘test.html', context,context_instance=RequestContext(request))