Set default value for dynamic choice field - django

I have a form that asks the user to enter in their zip code. Once they do it sends them to another form where there is a field called 'pickup_date'. This gets the value of the zip from the previous field and gets all of the available pickup_dates that match that zip code into a ChoiceField. I set all of this within the init of the model form.
def __init__(self,*args,**kwargs):
super(ExternalDonateForm,self).__init__(*args,**kwargs)
if kwargs:
zip = kwargs['initial']['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
elif self.errors:
zip = self.data['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
The problem I have is when there are other errors on the form. I use the elif self.errors to regenerate the possible choices but it doesn't default to the original selected option. It goes back and defaults to the first choice. How can I make it so it's default option on form errors is what was originally posted?

Change self.fields['pickup_date'] to self.fields['pickup_date'].initial and see if that helps.

I got it to work after playing around for a while. Above, I was setting all the dynamic choices with a get_dates() function that returned a tuple. Instead of doing that I returned a field object like this using a customized ModelChoiceField instead of a regular ChoiceField....
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.date.strftime('%a %b %d, %Y')
Dates function
def get_dates(self,zip):
routes = Route.objects.filter(zip=zip).values_list('route',flat=True)
pickups = self.MyModelChoiceField(queryset = PickupSchedule.objects.filter(
current_count__lt=F('specials'),
route__in=routes,
).order_by('date')
)
if not pickups:
pickups = (('----','No Pickups Available At This Time'),)
return pickups
in the init i set the value for self.fields['pickup_date'] like so..
self.fields['pickup_date'] = self.get_dates(zip)

Related

Passing a variable defined in previous form to another form

So I have this flask app I'm making and I need some help with a variable access.
Most of the time, when you define a form in flask, you'll do the following :
class MyForm(Form):
my_field = StringField('I'm a field')
my_submit = SubmitField('Go!')
And when the time comes where you need the form, you'll declare an instance of that class with form = MyForm()
Up to here, it's all good, However :
If you want say, a SelectField (Dropdown) where the choices depend on the answers of a previous form, you need to be able to give the new form those choices. This is what I'm trying to achieve, but I can't get a variable to keep its contents.
Here is my Form code (Above the page code):
class DataMappingForm(Form):
dm_choices = #I need this array !
DMpatient_id = SelectField(u'Select Patient ID Column',
choices=dm_choices, validators=[Required()])
...
Here is my Page code :
#app.route('/upload', methods=['GET','POST'])
def upload():
uform = SomeOtherForm()
if uform.is_submitted() and uform.data['Usubmit']:
#Do stuff from previous form
# and declare array_of_choices
dmform = DataMappingForm() #Needs array_of_choices to work
...
What I've tried so far :
session['dm_choices'] gives me a working outside of request context error
global variables, get reset for some reason
overloading the __init__ of Form by adding the array but i can't access it in the parts above the __init__ function.
I should mention, this all needs to be on the same page.
Is there a way to pass this array_of_choices to my DataMappingForm class ?
EDIT This is what it looked like when I trid the __init__ overload:
class DataMappingForm(Form):
def __init__(self, dm_choices, *args, **kwargs):
self.dm_choices = dm_choices
Form.__init__(self, *args, **kwargs)
DMpatient_id = SelectField(u'Select Patient ID Column',
choices=dm_choices, validators=[Required()])
#I've tried putting it above or below, I get 'dm_choices is not defined'
I've Got it ! Thanks to #synonym for pointing me in the right direction with your last link.
All you need to do is declare a function in which the class is defined. You then pass the variable to the function, and it will be accessible within the class.
Finally, make the function return the form object.
Example :
def makeMyForm(myArray):
def class MyForm(Form):
my_select_field = SelectField(u'I'm a select field', choices=myArray)
my_submit = SubmitField(u'Go!')
return MyForm()
And to make the form, you use :
form = makeMyForm(theArrayYouWant)
And VoilĂ  !
Note : As I've had the problem before, I'll mention that the Array is composed of tuples :
myArray = [('value','What you see'),('value2','What you see again')]
If you want to dynamically change the choices of a SelectField the following should work:
class DataMappingForm(Form):
def __init__(self, choices)
self.DMpatient_id.choices = choices
DMpatient_id = SelectField(u'Select Patient ID Column') #note that choices is absent
If you want fully dynamic fields you can create the class dynamically in a function. From the WTForms Documentation:
def my_view():
class F(MyBaseForm):
pass
F.username = StringField('username')
for name in iterate_some_model_dynamically():
setattr(F, name, StringField(name.title()))
form = F(request.POST, ...)
# do view stuff
In that case you can customize the form as much as you want. Of course in the case you only want to customize the choices the first approach should be enough.

How to dynamically update the 'initial' value in a Form ChoiceField

I have a Form with the following field:
image_choices = []
images = forms.ChoiceField(label=_("Images"), choices=image_choices, initial="")
I need to be able to update the value of the 'initial' attribute, after I learn what that value should be.
Currently, I have this assignment done within the __init__:
def __init__(self, request, image_choices=image_choices,
flavor_choices=flavor_choices, args, *kwargs):
super(UpdateWorkload, self).__init__(request, args, *kwargs)
selected_image = selected_workload['image']
self.fields['images'].initial = selected_image
I do not get any errors, and, when printed, the value is there,
but, in the actual form on the screen,
I still get my default list, and no specific items are selected, as per self.fields['images'].initial = str(selected_image)
How can I fix that?
After all, using this approach:
self.fields['images'].initial = selected_image
self.fields['flavors'].initial = selected_flavor
is working. The only thing I did differently was changing my backend from django restframework to tastypie

submitting form results into db - django

i created a form to save a post into db for my blog project. I've designed index page. now i am tryin to create a form to create new posts. before that i was using ' manage.py shell'
here is my view :
def addpost(request):
form = addForm()
if request.method=="POST":
titleform = request.POST['title']
bodyform = request.POST['body']
checkform = request.POST['isdraft']
if form.is_valid():
n = Post(title = titleform, body = bodyform, isdraft=checkform)
n.save()
return HttpResponseRedirect('/admin/')
else:
pass
return render(request,'userside/add.html',{'form':form,})
my model.py:
class Post(models.Model):
title = models.CharField(max_length = 100)
body = models.TextField()
slug = AutoSlugField(populate_from='title',unique=True)
posted = models.DateField(auto_now_add=True)
isdraft = models.BooleanField()
def __unicode__(self):
return self.title
#permalink
def get_absolute_url(self):
return ('view_blog_post',None, {'postslug':self.slug})
class addForm(forms.Form):
title = forms.CharField(max_length=100)
body = forms.CharField(widget=forms.Textarea)
isdraft = forms.BooleanField()
if i submit form as 'isdraft' field is False(unchecked) ; it gives error like:
MultiValueDictKeyError at /admin/addpost/
"Key 'isdraft' not found in "
and if i submit the form as 'isdraft' field is True(checked) ; it gives nothing. just refreshing form. no adding data into db.
i am doing sth wrong..
thank you
edit : Dmitry Beransky's answer worked for checkbox error. but it still doesnt add any data into db. just refreshes the form.
The whole point of using a form is that it takes care of validation and cleaning, that is converting values to the proper data types. That's why you should be accessing form.cleaned_data rather than reques.POST, and you should be doing it inside the if form.is_valid() check.
Edit
I've just noticed that you're never passing request.POST to the form. So form.is_valid() will never be true.
Please go back and read the documentation about using a form in a view.
If a checkbox is not checked in your HTML form, it's name/value is not going to be included in the data that the browser sends to your server. Which meanst that the request.POST dictionary is not going to contain an entry for 'isdraft' which in turn will cause a key error when you try to read the isdraft value. A solution is to change the way you read the value from the posted data to:
checkform = request.POST.get('isdraft', False)
rather than throw an error if isdraft isn't found in the dictionary, this will set checkform to False (the default value in case of a missing key)
Maybe your form does not validate at all. Have you checked if your code even reaches those lines after the if form.is_valid() statement ? If they do, what you've done there is right and should create the db row for your new entry, though you could have used
Post.objects.create(....) , and that would have taken away the need for calling the method save().
Some points though:
instead of checking for request.POST , check for request.method == 'POST' , cause there might be a post which has an empty POST dict ( in case no arguments have been submitted ), in that case request.POST fails to provide the right check .
see the docs for more info : request.POST
instead of using request.POST['var_name'] , use request.POST.get('var_name', 'default_value') , cause doing this like request.POST['var_name'] might result in some exceptions ( in case for example the argument is not provided , like what happened for your checkform variable )
Try accessing those variables through form.cleaned_data
and finally , you don't need the else statement in the end , just use the indentation :)

Best way to handle request variables in Django

I have a form 'in the wild' that takes many different variables - which may or may not be populated.
try:
app_version = request.REQUEST["appVersion"]
except:
app_version = ''
try:
app_name = request.REQUEST["appName"]
except:
app_name = ''
try:
app_code_name = request.REQUEST["appCodeName"]
except:
app_code_name = ''
Is there a tighter way to accomplish this?
app_version = request.REQUEST.get("appVersion", "")
get(key, default) is a method implemented on Python dicts. If the key exists in the dictionary, its value is returned; if the key does not exist, the specified default value is returned. In Django, request objects are dictionary-like objects, so get is also defined for them in the same manner.
If these variables are intended to populate a form, then you can safely pass the request.POST object directly into the form constructor.
if request.method == 'POST':
form = MyForm(request.POST)
The form will automatically pass the correct values to the correct form fields and use defaults for keys that don't exist and will still create blank fields for missing keys (see addendum).
If you are trying to process a form, it is still better to create a form object as above, and read out the values from that object.
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
# You may process these variables here
print form.appVersion
print form.appName
print form.appCodeName
Remember, validation code is best placed in the form class as well. That way, if form.is_valid() returns True, then you know you have a clean dataset to work with.
Note: Django docs recommend using request.POST or request.GET directly rather than the amalgamated variable request.REQUEST, as it is more explicit.
Addendum:
It is important to understand the difference between bound and unbound forms in this case. If you create an unbound form with form = MyForm(), then when the form is instantiated, it will fill in all fields with the initial property of each field (if it exists). For example, with this code:
from django import forms
class MyForm(forms.Form):
appVersion = forms.CharField(initial='1.0')
appName = forms.CharField()
appCodeName = forms.CharField()
the form will be initialized with appVersion having a value of '1.0'. However, if you bind a POST request to a form like this: form = MyForm(request.POST), then the initial properties are ignored. That means if the POST dict does not include an appVersion key, then that field will be left blank. As long as the field is not required, your form will still validate, and you can modify form.appVersion in the view after validation.
If you have many fields, a more compact version might be:
defaults = { 'field1' : 'val1', 'field2' : 'val2', ...}
defaults.update(request.POST)

Setting the selected value on a Django forms.ChoiceField

Here is the field declaration in a form:
max_number = forms.ChoiceField(widget = forms.Select(),
choices = ([('1','1'), ('2','2'),('3','3'), ]), initial='3', required = True,)
I would like to set the initial value to be 3 and this doesn't seem to work. I have played about with the param, quotes/no quotes, etc... but no change.
Could anyone give me a definitive answer if it is possible? And/or the necessary tweak in my code snippet?
I am using Django 1.0
Try setting the initial value when you instantiate the form:
form = MyForm(initial={'max_number': '3'})
This doesn't touch on the immediate question at hand, but this Q/A comes up for searches related to trying to assign the selected value to a ChoiceField.
If you have already called super().__init__ in your Form class, you should update the form.initial dictionary, not the field.initial property. If you study form.initial (e.g. print self.initial after the call to super().__init__), it will contain values for all the fields. Having a value of None in that dict will override the field.initial value.
e.g.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
# assign a (computed, I assume) default value to the choice field
self.initial['choices_field_name'] = 'default value'
# you should NOT do this:
self.fields['choices_field_name'].initial = 'default value'
You can also do the following. in your form class def:
max_number = forms.ChoiceField(widget = forms.Select(),
choices = ([('1','1'), ('2','2'),('3','3'), ]), initial='3', required = True,)
then when calling the form in your view you can dynamically set both initial choices and choice list.
yourFormInstance = YourFormClass()
yourFormInstance.fields['max_number'].choices = [(1,1),(2,2),(3,3)]
yourFormInstance.fields['max_number'].initial = [1]
Note: the initial values has to be a list and the choices has to be 2-tuples, in my example above i have a list of 2-tuples. Hope this helps.
I ran into this problem as well, and figured out that the problem is in the browser. When you refresh the browser is re-populating the form with the same values as before, ignoring the checked field. If you view source, you'll see the checked value is correct. Or put your cursor in your browser's URL field and hit enter. That will re-load the form from scratch.
Both Tom and Burton's answers work for me eventually, but I had a little trouble figuring out how to apply them to a ModelChoiceField.
The only trick to it is that the choices are stored as tuples of (<model's ID>, <model's unicode repr>), so if you want to set the initial model selection, you pass the model's ID as the initial value, not the object itself or it's name or anything else. Then it's as simple as:
form = EmployeeForm(initial={'manager': manager_employee_id})
Alternatively the initial argument can be ignored in place of an extra line with:
form.fields['manager'].initial = manager_employee_id
Dave - any luck finding a solution to the browser problem? Is there a way to force a refresh?
As for the original problem, try the following when initializing the form:
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.base_fields['MyChoiceField'].initial = initial_value
To be sure I need to see how you're rendering the form. The initial value is only used in a unbound form, if it's bound and a value for that field is not included nothing will be selected.