Flask Wtf Form no longer validates on submit after I add form.field.default = value and then form.process()
For example my form class,
class SelectFoo(FlaskForm):
var1 = SelectField('Var 1')
var2 = SelectField('Var 2')
var3 = SelectField('Var 3')
My Route,
#route.route('/foo-input', methods=['GET', 'POST'])
def foo_route():
form = SelectFoo()
df = pd.Dataframe({'var_1': np.random.rand(10), 'var_2': np.random.rand(10), 'var_3': np.random.rand(10)})
choices = [(s, s.replace('_', ' ').title()) for s in df.columns]
fuzzy_lookup = compare_lists(target, cols)
print(fuzzy_lookup) # for this test use {i:i for i in df.columns}
print(choices)
form.val1.choices = choices
form.val2.choices = choices
form.val2.choices = choices
if form.validate_on_submit():
dict_ = {
'var 1': form.var1.data,
'var 2': form.var2.data,
'var 3': form.var2.data,
}
return jsonify(dict_)
return render_template('footemplate.html', form=form)
If I add,
#route.route('/foo-input', methods=['GET', 'POST'])
def foo_route():
...
form.coupon.choices = choices
form.val1.default = fuzzy_lookup['val1']
form.val2.default = fuzzy_lookup['val2']
form.val2.default = fuzzy_lookup['val2']
form.process()
...
return jsonify(dict_)
return render_template('footemplate.html', form=form)
The form will render with the selected value that is with default. However, my form no longer submits, best I can tell is that it adds aselectedkeyword to an optionVal 1` when I add the default option in my html form. I did a complete diff of the html and that is all I found.
It looks like you need to process the form in the 'GET' request.
if request.method == 'GET':
form.process()
Then everything works fine.
Related
I have the following view:
#login_required
def my_view(request):
instance = my_model(user=request.user)
form = my_model_form(request.POST,instance = instance)
if request.method == "POST":
if form.is_valid():
form.save(commit=False)
#Field1 and field2 is already in the form (its the input)
# Do some back-end operations to get values for the remaining fields
df = some_util_function()
form.field3 = df["field3"]
form.field4 = df["field4"]
form.field5= df["field5"]
form.save()
return redirect("my_html")
else:
form = my_model_form()
context = {
"form":form
}
return render(request, "discounttracker/my_html.html",context=context)
and the problem is that field3,field4,field5 are not changed. I have even tried to hard-code them to 1000, 2000,3000 (they are FloatField(default=0)) but they remain at their default value when written to the DB.
What am I doing wrong here?
You are setting the attributes of the form, not of the instance wrapped in the form. You should alter this to:
#login_required
def my_view(request):
instance = my_model(user=request.user)
if request.method == 'POST':
form = my_model_form(request.POST, instance = instance)
if form.is_valid():
# Field1 and field2 is already in the form (its the input)
# Do some back-end operations to get values for the remaining fields
df = some_util_function()
# ↓ the instance of the form
form.instance.field3 = df['field3']
form.instance.field4 = df['field4']
form.instance.field5 = df['field5']
form.save()
return redirect("my_html")
else:
form = my_model_form()
context = {
'form': form
}
return render(request, 'discounttracker/my_html.html', context=context)
in view.py:
#require_POST
#csrf_exempt
def ipn(request):
transactions_logger = logging.getLogger("django")
processor = Ipn(request.POST, logger=transactions_logger)
verification_success = processor.verify_ipn()
encoding = request.POST.get('ok_charset', None)
data = QueryDict(request.body, encoding=encoding)
if verification_success:
form = OkpayIpnForm(data)
if form.is_valid():
print("ALL FINE!!")
form.save()
return HttpResponse("")
In forms.py:
class OkpayIpnForm(forms.ModelForm):
class Meta:
model = OkpayIpn
exclude = []
Code for IPN Checkprocessor = Ipn(request.POST, logger=transactions_logger:
class Ipn(object):
OKPAY_VERIFICATION_URL = 'https://checkout.okpay.com/ipn-verify'
OKPAY_IPN_INVALID = b'INVALID'
OKPAY_IPN_VERIFIED = b'VERIFIED'
OKPAY_IPN_TEST = b'TEST'
OKPAY_STATUS_COMPLETED = 'completed'
__verification_result = False
def __init__(self, request_data, logger):
if 'ok_verify' in request_data:
raise Exception("ok_verify must not be present in initial request data for {}".format(
self.__class__.__name__
))
self._request_data = request_data
self.logger = logger
return
def verify_ipn(self):
self.__verification_result = False
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
verify_request_payload = {
'ok_verify': 'true',
}
verify_request_payload.update(self._request_data)
resp = requests.post(self.OKPAY_VERIFICATION_URL, data=verify_request_payload, headers=headers)
if resp.content == self.OKPAY_IPN_VERIFIED or resp.content == self.OKPAY_IPN_TEST:
self.__verification_result = True
# if resp.content == self.OKPAY_IPN_VERIFIED: # anyway disable test on production.
# self.__verification_result = True
return self.__verification_result
All is ok, I revice IPN and validate it, then I try to validate form and save it to Database.
But form doesn't pass validation and doesn't save to database.
Thank You for help
Problem was that 1 CharField of Model for saving IPN has maxlength=20, but recieved 40 symbols.
Thx jape he advised to add in form validation else statement and print form.errors
the error of of form validation was :
<li>ok_item_1_type<ul class="errorlist"><li>Ensure this value has at most 20 characters (it has 40).</li></ul></li>
I have view to submit data. From the web browser, i can submit data and redirected nicely. But on the test, i got the error: didn't return an HttpResponse object. It returned None instead.
How can i pass my test?
The view code is:
def insert_barang_po(request, po_id=None):
"submit barang2 dalam PO, ditamilkan setelah insert PO"
po = get_object_or_404(PurchaseOrder, id=po_id)
context = {'menu_sales': True,}
context['po'] = {'id': po.id,}
if request.method == 'POST':
form = InsertBarangPOForm(request.POST)
if form.is_valid():
barang_po = BarangPurchaseOrder.objects.create(
po=form.cleaned_data['po'],
finish_good=form.cleaned_data['finish_good'],
jumlah_barang=form.cleaned_data['jumlah_barang'])
return redirect(reverse('sales:insert_barang_po', args=[po.id]))
else:
form = InsertBarangPOForm(initial={'po':po,})
context['form'] = form
return render(request, 'sales/insert_barang_po.html', context)
The model code is:
class BarangPurchaseOrder(models.Model):
"Menyimpan isi dari setiap PO yg diterima"
po = models.ForeignKey(PurchaseOrder)
finish_good = models.ForeignKey(FinishGood)
jumlah_barang = models.IntegerField()
def __unicode__(self):
return self.finish_good.nama
The test code is
def test_insert_barang_po_valid_po_id(self):
po = PurchaseOrder.objects.create(nomor_po='zaneti')
fg = FinishGood.objects.create(nama='selesai', code='sls')
c = Client()
response = c.post(reverse('sales:insert_barang_po', args=[po.id]),
{'po': po,
'finish_good': fg.id,
'jumlah_barang': '35',},
follow=True)
bpo = BarangPurchaseOrder.objects.filter(finish_good=fg) \
.order_by('id').reverse()
self.assertEqual(bpo[0].jumlah_barang, 35)
Your views.py doesn't cover all the cases. If the form is invalid, your method simply returns nothing, that's why you have this error message. You should have an else statement corresponding to if form.is_valid() statement.
I was trying to dynamically generate fields as shown in http://jacobian.org/writing/dynamic-form-generation/. My case slightly differs in that I am looking to use multiplechoicefield that is dynamically created. This is what I came up with...
views.py
def browseget(request):
success = False
if request.method == 'POST':
list_form = ListForm(request.POST)
if list_form.is_valid():
success = True
path = list_form.cleaned_data['path']
minimum_size = list_form.cleaned_data['minimum_size']
follow_link = list_form.cleaned_data['follow_link']
checkboxes = list_form.cleaned_data['checkboxes']
....do something
else:
list_form = ListForm(name_list)
ctx = {'success': success, 'list_form': list_form, 'path': path, 'minimum_size': minimum_size}
return render_to_response('photoget/browseget.html', ctx, context_instance=RequestContext(request))
forms.py
class ListForm(forms.Form):
path = forms.CharField(required=False)
minimum_size = forms.ChoiceField(choices=size_choices)
follow_link = forms.BooleanField(required=False, initial=True)
def __init__(self, *args, **kwargs):
name_list = kwargs.pop('name_list', None)
super(ListForm, self).__init__(*args, **kwargs)
print 'Received data:', self.data
if name_list:
name_choices = [(u, u) for u in name_list]
self.fields['checkboxes'] = forms.MultipleChoiceField(required=False, label='Select Name(s):', widget=forms.CheckboxSelectMultiple(), choices=name_choices)
def clean_path(self):
cd = self.cleaned_data
path = cd.get('path')
if path == '': path = None
return path
def clean_minimum_size(self):
cd = self.cleaned_data
minimum_size = cd.get('minimum_size')
if minimum_size is None: minimum_size = 0
return int(minimum_size)
The form generates and displays perfectly... until I post some data. The 'checkboxes' field doesn't show up in list_form.cleaned_data.items() while it shows in self.data. As it is the form breaks with a KeyError exception. So Im asking, how do i access the checkboxes data?
You're not passing in the name_list parameter when you re-instantiate the form on POST, so the field is not created because if name_list is False.
I have my form clean method return two values. How do I distinguish the two variables in my view. Basically, I want to use the form data to check the database and return an object if it exists so that I can pass it to a new view. My goal is to not hit the database twice, once to see if the object exists and another time to retrieve it to display to the user.
Forms.py
class DocumentCodeLookup(forms.Form):
code = forms.CharField(max_length=15, error_messages={'required': 'Whoops! Please enter the Document Code from your ticket.'})
def clean_code(self):
code = self.cleaned_data['code'].upper()
if (re.match(r'^[A-Z0-9]{4,8}[-][A-Z0-9]{6}$',code)):
code_parts = code.split('-')
try:
q = Code.objects.get( user_defined_code__name=code_parts[0], document_code=code_parts[1] )
except Code.DoesNotExist:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
else:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
return code, q
Views.py
def index(request):
code_lookup_form = DocumentCodeLookup()
if request.method == 'POST':
code_lookup_form = DocumentCodeLookup(request.POST)
if code_lookup_form.is_valid:
redirect('document', x = q) # I want to pass the returned object to the view
return render_to_response('base/splash_page.html' ,{
'code_lookup_form' : code_lookup_form
}, context_instance=RequestContext(request))
Will clean_field even work like that?
http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
Note the comment.
You can put the attr on the form with self.
class DocumentCodeLookup(forms.Form):
code = forms.CharField(max_length=15, error_messages={'required': 'Whoops! Please enter the Document Code from your ticket.'})
def clean_code(self):
code = self.cleaned_data['code'].upper()
if (re.match(r'^[A-Z0-9]{4,8}[-][A-Z0-9]{6}$',code)):
code_parts = code.split('-')
self.q = None
try:
self.q = Code.objects.get( user_defined_code__name=code_parts[0], document_code=code_parts[1] )
except Code.DoesNotExist:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
else:
raise forms.ValidationError("Hmmm, we couldn't find that document.")
return code
q is on the form.
def index(request):
code_lookup_form = DocumentCodeLookup()
if request.method == 'POST':
code_lookup_form = DocumentCodeLookup(request.POST)
if code_lookup_form.is_valid():
redirect('document', x = code_lookup_form.q) # <---
return render_to_response('base/splash_page.html' ,{
'code_lookup_form' : code_lookup_form
}, context_instance=RequestContext(request))