Flask wtf-form override error message - python

Im using wtf-form for my password reset page but im having trouble with the generated error messages. I want to be able to either disable the error that wtf-form generates and use my own or be able to override them.
{% for e in form.password.errors %}
<span class="label label-danger">{{ e }}</span>
{% endfor %}
<form action="http://127.0.0.1:5000/setpassword" method=post>
<div class="form-group">
{{ form.csrf_token }}
{{ wtf.form_field(form.password, class='form-control', placeholder='Enter Password') }}
</div>
class PasswordForm(FlaskForm):
password = PasswordField('',validators=[pass_num, pass_small_lett, pass_big_lett, pass_special, pass_min])
confirm = PasswordField('',[validators.EqualTo('password', message='Not matching')])
submit = SubmitField('Send')

Don't use form_field() if you don't want its extra functionality. This will render just the input field:
{{ form.password(class='form-control', placeholder='Enter Password') }}
In your example the field's label is empty, but in case the field has a label, it must be printed as well. Bootstrap rules are:
Wrap labels and form controls in <div class="form-group"> (needed for optimum spacing)
Add class .form-control to all textual <input>, <textarea>, and <select elements
(source: https://www.w3schools.com/bootstrap/bootstrap_forms.asp)

Related

Failed to get value from html page in Django

I have a problem with trying to get a response from my HTML page using Django (admin).
I have a pretty simple div = contenteditable and need to pass data from this div back after the submit button was clicked.
Everything, including choosing selection and opening the intermediate page works fine. But when I tapped submit button, the condition if "apply" in request.POST failed to work.
Please, tell me, what I'm doing wrong?
This is my Django admin:
class QuestionAdmin(AnnotatesDisplayAdminMixin, admin.ModelAdmin):
def matched_skills(self, question):
return ', '.join(s.name for s in question.skills.all())
def update_skills(self, request, queryset):
if 'apply' in request.POST:
print("something")
skills = []
for question in queryset:
skills.append(self.matched_skills(question))
return render(request,
'admin/order_intermediate.html',
context={'skills': skills})
update_skills.short_description = "Update skills"
This is my order_intermediate.html page:
{% extends "admin/base_site.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
<h1>Adjust skills. </h1>
{% for skill in skills %}
<div>
<div id="title" style="margin-left: 5px" contenteditable="true" > {{ skill }} </div>
</div>
{% endfor %}
<input type="hidden" name="action" value="update_status" />
<input type="submit" name="apply" value="Update skills"/>
</form>
{% endblock %}
Actually, request.POST is an HttpRequest object. For getting available keys in the body of the request, you need to use "request.POST.keys()" method. So, you can simply change your condition to:
if 'apply' in request.POST.keys():
print("something")
In my knowledge, you can not send div content with form submit. However you can use input tag with array in name attribute for this. This will send an array as post variable when submit
First, send skills as a enumerate object from your views
return render(request, 'admin/order_intermediate.html', context={'skills': enumerate(skills)})
Then edit your html to this (Note: if you have css in title id, change it to title class)
{% for i,skill in skills %}
<div>
<input class="title" name="skill[{{ i }}]" value="{{ skill }}" style="margin-left: 5px">
</div>
{% endfor %}
and handle array with any action you want to perform in update_skills()
for skill in request.POST.getlist('skill[]'):
# your code

Populate textarea field using WTForms

As I understand it, if you want to populate a textarea you place the text between the textarea tags. However I am using WTForms. How can I pre-populate the form from views or in my template?
FORM
class ModuleSectionForm(FlaskForm):
title = StringField('Section Title', validators=[DataRequired()])
description = TextAreaField('Description', validators=[DataRequired()])
submit = SubmitField('Add Section')
VIEW
#modules.route('/update_section/<name>/<title>', methods=['GET', 'POST'])
def update_section(name, title):
form = ModuleSectionForm()
module = Module.objects(title=name).first()
section = None
for sect in module.sections:
if sect.title == title:
section = sect
#if form.validate_on_submit():
#save data
return render_template('modules/update_section.html', section=section, form=form)
TEMPLATE
<form method="post" action="{{ url_for('modules.update_section', name=name) }}">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.title.label(class="form-control-label") }}
{{ form.title(class="form-control", value=section.title) }}
</div>
<div class="form-group">
{{ form.description.label(class="form-control-label") }}
{{ form.description(class="form-control", default=section.description) }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn-secondary shadow") }}
</div>
</form>
This can be done by just assigning the text to display beforehand (in the view, for instance).
Try edit your view (update_section) this way:
+ form.description.data = 'text you want to display'
And your template as follows:
- {{ form.description(class="form-control", default=section.description) }}
+ {{ form.description(class="form-control") }}
Mind you, there's an alternative way, which is to specify the placeholder attribute (but I guess is not what you want to do here).
Typically with WTForms (let's assume you're using Flask and Bootstrap here), you'll use the value attribute of the input to pre-populate a form field. Note that pre-populating a field is distinct from providing a 'placeholder', which is just an ephemeral hint. So usually we pre-populate like this:
<div class="form-group row">
<label for="form_subject" class="col-sm-2 col-form-label">Subject</label>
<div class="col-sm-7">
<input class="form-control" id="form_framework" name="form_framework"
value="{{ instruct.Subject }}">
</div>
</div>
With Flask and Bootstrap, the name attribute is required to pass the value back to the Controller upon submission, the value attribute is used to pre-populate the field from the object - in this case, our controller has passed in an object called instruct, which has an attribute Subject.
But you have to be aware that different kinds of inputs in WTForms have different attributes, and this is left as a fun challenge for the developer to figure out.
TextArea doesn't have a value attribute, so in order to pre-populate the field, you have to provide the value between the tags, like so (again, using Bootstrap here in case any of these tags are unfamiliar):
<div class="form-group row">
<label for="form_longish_text"
class="col-sm-2 col-form-label">Longish Text</label>
<div class="col-sm-7">
<textarea class="form-control" rows="3"
id="form_longish_text"
name="form_longish_text">{{ instruct.LongishText }}
</textarea>
</div>
</div>

Validating GET Params with WTForms in Flask

I have spent a couple of days trying to get WTForms to validate my request.args, but I just can not get form.validate() to return True.
The idea is that I have a simple text field for user input in a WTForm as shown below.
form.py
class SearchForm(FlaskForm):
q = StringField('q',
validators=[])
search = SubmitField('Search')
def validate_q(self, q):
if q.data not in allowed_values: #"allowed_values" is just a list I want to check against
raise ValidationError('')
search.html
<form method="GET" action="{{ url_for('finance.search') }}">
<div class="col-9 col-md-5 p-0 m-0">
{% if form.q.errors %} {{ form.q(class="form-control form-control-md is-invalid") }}
<div class="invalid-feedback">
{% for error in form.q.errors %}
<span>{{ error }}</span> {% endfor %}
</div>
{% else %} {{ form.q(class="form-control form-control-md") }} {% endif %}
</div>
<div class="col-2 col-md-2 p-0">
{{ form.search(class="btn btn-md btn-dark") }}
</div>
</form>
routes.py
#finance.route('/finance/search')
def search():
form = SearchForm(request.args)
print(form.validate()) #always gives false
The HTML code for the form is included on several templates and submitting the form always directs to the search route that is shown below. I tried following WTForms documentation and passed in request.args into the form. When I ran the .validate() on the object, the validate function for the q parameter also executed, but for some reason .validate() always returns False.
Can anyone please elaborate on why that might be? I know I can use post request, or add a custom validation function inside the route, but I want to avoid workarounds if possible.
(stack-overflow seems to use a similar type of architecture for their search http://127.0.0.1:8000/finance/search?q=aapl&search=Search vs https://stackoverflow.com/search?q=aapl and I want to follow that if possible.)
Thanks!
I actually just figured out that the error was happening because I was not including a crsf_token in the form. The token is not needed since the its a get request, but this needs to be explicitly stated with meta = {'csrf': False}.
#finance.route('/finance/search')
def search():
form = SearchForm(request.args, meta={'csrf': False})
print(form.validate()) #Now gives True if validation function does not raise error

Django not passing context well

I was trying to pass a query set using context. But on template page the context is not working.
As I am implementing two queries in same view one query set is working fine but the other query is not passed well. Here is my view
# Create your views here.
def xray_result_view(request):
q=query_holding_together.objects.filter(which_user=request.user)
for x in q:
all_reports=xray_result.objects.get(which_query=x)
print(all_reports.sys_gen_result)
return render(request,'XRay/result.html',{'reports':all_reports})
when q is passed as template it is working as it should but it is not working for all reports. here is my template
{% extends "login/successful.html" %}
{% block middle_window %}
</div>
<div class="container adjust-ment">
<div class="row">
<div class="col-12">
Previous X-ray Results
</div>
</div>
<div class="row">
<div class="col-12">
Result
</div>
</div>
<div class="row">
<div class="col-12">
{% for y in reports.iterator %}
File Name:<br>
Date and Time of Upload:<br>
System Generated Result:{{ y.sys_gen_result }}<br>
Doctor's Comment on Result:{{ y.doctor_comment }}<br>
{% endfor %}
</div>
</div>
</div>
{%endblock middle_window %}
You are not passing a queryset in template, instead you are sending an object. Let me explain:
for x in q:
all_reports=xray_result.objects.get(which_query=x) #<-- Here
Here all_reports is a variable which has only a xray_result object. after the iteration is complete, all_reports will contain only the last object from q.
Instead, you can try like this:
def xray_result_view(request):
all_reports=xray_result.objects.get(which_query__which_user=request.user)
return render(request,'XRay/result.html',{'reports':all_reports})
And update the template:
{% for y in reports %}
File Name:<br>
Date and Time of Upload:<br>
System Generated Result:{{ y.sys_gen_result }}<br>
Doctor's Comment on Result:{{ y.doctor_comment }}<br>
{% endfor %}
Finally, consider using PascalCase when writing Class names(as per pep8 standard).
As #ruddra pointed out that there was problem with loop.
so for that I tried below workaround and it worked as a charm.
def xray_result_view(request):
q=query_holding_together.objects.filter(which_user=request.user)
all_reports=xray_result.objects.none()
for x in q:
all_reports = all_reports | xray_result.objects.filter(which_query=x)
for x in all_reports:
print(x.sys_gen_result)
return render(request,'XRay/result.html',{'reports':all_reports})

Message not flashing on some WTForm validation methods

This question is related to this question, but focussing on one of the underlying issues I didn't know about when asking that other question.
I'm using Python, Flask and WTForms to make a form. Here's the python:
from flask import render_template
from flask_wtf import FlaskForm
from wtforms import DecimalField, SubmitField
from wtforms.validators import NumberRange, DataRequired
class NumberForm(FlaskForm):
question_one = DecimalField('Question 1', validators=[NumberRange(min=0, max=10)])
question_two = DecimalField('Question 2', validators=[DataRequired()])
submit = SubmitField('Submit')
#app.route('some_route/', methods=['GET', 'POST'])
def page():
form = NumberForm()
if form.validate_on_submit():
return some_success_or_other
return render_template('page.html', form=form)
And the HTML:
<form method="POST">
<div class="form-group-row">
{{ form.hidden_tag() }}
{{ form.question_one.label }}
<div>
{{ form.question_one }}
</div>
</div>
<div class="form-group-row">
{{ form.question_two.label }}
<div>
{{ form.question_two }}
</div>
</div>
<div class="form-group-row">
{{ form.submit }}
</div>
</form>
The two validators I'm using (NumberRange and DataRequired) behave differently. When data isn't entered in field 2 and the submit button is clicked, the form isn't submitted and an error message pops up next to the field saying "Please fill in this field".
When strings (rather than decimals), or numbers outside the range is submitted in field 1, the form does submit when the button is pressed.
Although the error is logged (and can be shown using {{ form.errors }}, I wanted the same behaviour for both validators - I'd like the form to be prevented from submitting when invalid numbers/strings are entered in field 1.
Anyone know why the validators behave differently?
HTML5 introduces the required attribute which makes it mandatory to fill a field and block form validation if one of the fields (affected by this attribute) has not been populated; this attribute is only available for the input tag.
With flask-wtf, when you use a field with the required validator, it will automatically generate a html field with the required attribute, which will block the form from being sent and display an infobule.
In the case of other validators such as NumberRange, it is not the same.
With pure HTML:
to have the same behavior, ie to check that the number entered is in a well-defined range and to block the sending of the form if it is not the case, it is necessary to use the pattern attribute (more details here). There is no predefined pattern for this particular case, so you will need to create a custom pattern using regular expressions. It will give this: <input type="text" pattern="[0-9]">.
Now we just have to reattake the same logic with wtforms...
With wtforms:
<form method="POST">
<div class="form-group-row">
{{ form.hidden_tag() }}
{{ form.question_one.label }}
<div>
{{ form.question_one(pattern="[0-9]") }}
</div>
</div>
<div class="form-group-row">
{{ form.question_two.label }}
<div>
{{ form.question_two }}
</div>
</div>
<div class="form-group-row">
{{ form.submit }}
</div>
</form>
At line {{ form.question_one(pattern="[0-9]") }}, I use the pattern attribute with a regular expression that checks that the number entered is in the range 0 to 9. For larger ranges, you will need to write regular expressions a little more complex (this link is interresting)

Categories

Resources