validating a form dynamically built in flask - python

I have a problem sending data in a form, which will not be the case. In the view, I build a dynamically form of agreement has some more complex models in the code is much simpler but the idea is the same.
When I complete the form, this is never processed. I really do not know what may be the problem.
code:
from wtforms import StringField,SubmitField
from flask.ext.wtf import Form
#preguntas.route('/pregunta/encuesta/', methods=['GET','POST'])
def view():
class F(Form):
pass
#building form
setattr(F,'funcion1', StringField(label='fun1'))
setattr(F,'funcion2', StringField(label='fun2'))
setattr(F,'enviar', SubmitField('enviar formulario'))
formulario = F()
if formulario.validate_on_submit():
#never comes to this part of the code
print "saving data..."
return render_template('pregunta/page.html',form = formulario)
thanks for your answers

I think there are a few things you need to check. What is it in the form that you are trying to validate? In you code sample I don't see the use of validator. for example,
setattr(F,'funcion1', StringField(label='fun1', [validators.Length(max=10)]))
This should add a validators that validate that no more than 10 characters were entered. You can add to the list of validators any validator you would like
In addition if you are using CSRF protection and POST you should include the {{hidden_tag}}or{{formulario.csrf_token}}` as the first element in your form
your form in your template should look like this:
<form action="{{url_for('some_view'}}">
{{hidden_tag}}
<!-- rest of form elements -->
</form>
And try to see what errors you get from your forms bu doing this:
formulario = F()
print formulario.errors
if formulario.validate_on_submit():
# some code

Related

getting data from wtforms FormField in a Flask route

I have a problem getting data from a form using it in a route
forms.py
class Calculator(Form):
amount = IntegerField('Amount')
weight = IntegerField('Weight')
class Program(Form):
cycles = IntegerField('Cycles')
volume = FormField(Calculator)
app.py
#app.route('/', methods=('GET', 'POST'))
def index():
form = forms.Program()
if form.validate_on_submit():
values = models.Progression(
cycles=form.cycles.data,
amount=form.amount.data,
weight=form.weight.data
)
return render_template('index.html', form=form, values=values)
The data for cycles comes through just fine but I am unsure of the syntax for how to access the encapsulated form within my route. The docs say FormField will return the data dict of the enclosed form but I can't seem to figure out how to grab that and put it in a variable.
I was able to grab the data I needed using
amount=form.volume.amount.data,
weight=form.volume.weight.data
The real issue came from the fact that the form was not validating when I was using FormField. A rookie error I should have checked earlier.
I had to enable CSRF protection by importing it from flask_wtf and using CsrfProtect(app)
The problem is that the form data does not come through as the attributes of the Calculator class. The data is sent as a dictionary from the volume attribute.
Test it out with: print form.volume.data
(I suggest commenting out your values object and just use print statements)
The output should be: {'amount': foo, 'weight': bar}
Thanks for teaching me something! I never knew of FormField.

How to pre-populate Django form with part of URL?

I'm completely new to programming (Django), and I'm trying to pre-populate a django_messages form with a snippet of the URL.
For example, for a compose form at www.mywebsite.com/compose_root/Chris88, I want the "Recipient" field to be pre-populated with "Chris88".
Is there any way to do this? In urls.py, I have:
url(r'^compose_root/(<recipient>[\w.#+-]+)/$', compose, name='messages_compose_to'),
I already tried plugging in recipient as an initial in the "Recipient" form field, but it didn't work, so it might be easier just to pre-populate with an excerpt of the URL.
Help is much appreciated.
Assuming you have a form that looks something like:
class New_form(Form.form):
... FormStuff
recipient = Some Field
Add a view that looks like:
def compose_root(request,recipient):
...# View Stuff
form = New_form(initial={"recipient": recipient})
return render_to_response('form-template.html', {'form':form})
And in your form-template
{{form}}

flask-wtf editing a model using wtform Form constructor: pre-filling the form

I am reading the Flask Web Development book and came across this:
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.location = form.location.data
current_user.about_me = form.about_me.data
db.session.add(user)
flash('Your profile has been updated.')
return redirect(url_for('.user', username=current_user.username))
form.name.data = current_user.name
form.location.data = current_user.location
form.about_me.data = current_user.about_me
return render_template('edit_profile.html', form=form)
Basically, when the form is not posted or doesn't validate, this copies over the data from the current user. Now reading up on wtforms, I read this about the init method on a form:
obj – If formdata is empty or not provided, this object is checked for attributes
matching form field names, which will be used for field values.
So I guess that means that we could write this (the sample below is my own):
def edit_post(post_id):
post = Post.query.get_or_404(post_id)
if current_user != post.author:
abort(403)
# Below is the line I am concerned about
form = PostForm(formdata=request.form, obj=post)
if form.validate_on_submit():
form.populate_obj(post)
db.session.commit()
return redirect(url_for('user', username=current_user.username))
return render_template('post_form.html', form=form)
I figure that this should fill the form instance from the database model on GET, and from POST data after post. Testing this, it seems to work..
Now my question: is this way of writing an edit view correct? Or should I copy everything over field by field, like in the book?
Loading in a POST MultiDict is certainly the accepted way to map key/value pairs to your WTForms instance. Even more so, if you are using the Flask-WTF extension, this is automatically done for you, it is one of the perks that this extension brings you.
If you would crack open the code of Flask-WTF you would see that it inherits the SecureForm class of WTForms and tries to load in the Werkzeug POST MultiDict (called formdata) by default (if it is present). So loading in your form in your view like this:
form = PostForm(obj=post)
Should be sufficient (if using Flask-WTF) to also fill the fields with POSTed data.
The way it is done in your book example is certainly not wrong, but creates a lot of unnecessary code and is error prone / redundant - one could forget to mention a field in the view that is declared in the WTForms instance.

Keeping typed in form data in django

I have a form where i would like the data to be still present in the form fields if there is a form validation error. By default when i try to submit data in the form django clears all the fields. What is the best approach for doing this using django 1.6 ?
I thought i would just fetch data like so: request.POST['field'], send it to the template and then let the template run a conditional if statement by im getting an error from django telling me that the values isnt found in the multidict but it is found when i fill out the form field so that doesnt seem to work. I also tried to check if the value was present directly in the view but i ended up with a ridicoulus amount of if statements to be able to do this which is just an ugly hack.
Can someone suggest a good working solution on this ? Ive seem threads on SO on kind of the same problem but people just wrote that it should be the default to keep data but that doesnt seem to be the case for django 1.6.
I imagined your form class MyForm(). If the form is not valid send form = MyForm(request.POST) to template.
some view:
def myview(request):
if request.method=='POST':
form = MyForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'success.html')
else:
form = MyForm()
return render(request, 'form.html',{'form':form})
<form action="." method="POST">
{{form}}
</form>

Django - To get form.errors as a dictionary instead of HTML code

Is there a way to get the form errors generated during the django form validation in a dictionary (key as the 'field_name' and value as 'the list of errors relevant to it'), instead of the default HTML code it generates (ul & li combination). I'm not using the generated HTML code, and I'm just bothered about the field name and the errors.
There's not built-in method for return errors as dict, but you can use json.
form = MyForm(data)
print(form.errors.as_json())
doc for errors.as_json
Sure. I use this class often when doing forms that are Ajaxed and I need to return JSON instead. Tweak/improve as necessary. In some cases, you might want to return HTML encoded in the JSON, so I pass in the stripping of HTML tags as an option.
from django import forms
from django.template.defaultfilters import striptags
class AjaxBaseForm(forms.BaseForm):
def errors_as_json(self, strip_tags=False):
error_summary = {}
errors = {}
for error in self.errors.iteritems():
errors.update({error[0]: unicode(striptags(error[1])
if strip_tags else error[1])})
error_summary.update({'errors': errors})
return error_summary
Usage:
# forms.py
class MyForm(AjaxBaseForm, forms.Form): # you can also extend ModelForm
...
# views.py
def my_view(request):
form = MyForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
...
else:
response = form.errors_as_json(strip_tags=True)
return HttpResponse(json.dumps(response, ensure_ascii=False),
content_type='application/json')
Calling dict() worked for me:
print(dict(form.errors))
I prefer this technique because as_json() converts special characters to Unicode escape sequence.
In the view you can do this:
f = FooForm(data)
f.errors
f.errors.as_json()
Use this method anytime you need to identify an error by its code. This enables things like rewriting the error’s message or writing custom logic in a view when a given error is present. It can also be used to serialize the errors in a custom format (e.g. XML); for instance, as_json() relies on as_data().

Categories