DatePickerWidget with Flask, Flask-Admin and WTforms - flask

I'm trying to render a template that contains a DatePicker, but I'm getting a 500 error when I try. For my the code is correct, but it seems that something is failing or I'm not understanding correctly the way to do it.
The code is the following:
Reporting.py
from flask.ext.admin import BaseView, expose
from wtforms import DateField, Form
from wtforms.validators import Required
from flask.ext.admin.form import widgets
from flask import request
class DateRangeForm(Form):
start_date = DateField('Start', validators=[Required()], format = '%d/%m/%Y', description = 'Time that the event will occur', widget=widgets.DatePickerWidget)
class ReportingView(BaseView):
#expose('/')
def index(self):
form = DateRangeForm(request.form)
return self.render('reporting.j2', form=form)
Reporting template:
{% extends 'admin/master.html' %}
{% block body %}
{{super()}}
Working on it!
{% if form %}
{{form.start_date}}
{% endif %}
{% endblock %}

As davidism says in the comments, the default DateField just provides date parsing, it'll just be displayed as a normal text-input.
If you're ready to fully embrace html5 then you can use the DateField from wtforms.fields.html5 which will render a datepicker in any browser that supports it:
from flask import Flask, render_template
from flask_wtf import Form
from wtforms.fields.html5 import DateField
app = Flask(__name__)
app.secret_key = 'SHH!'
class ExampleForm(Form):
dt = DateField('DatePicker', format='%Y-%m-%d')
#app.route('/', methods=['POST','GET'])
def hello_world():
form = ExampleForm()
if form.validate_on_submit():
return form.dt.data.strftime('%Y-%m-%d')
return render_template('example.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
The more usual approach is to find a nice datepicker online, that's using CSS and JS to handle it's display and include that code into your html template, and tell it to apply the datepicker style to any html element that has a class of datepicker. Then when you generate your form you can just do:
<!-- all your CSS and JS code, including the stuff -->
<!-- to handle the datepicker formatting -->
<form action="#" method="post">
{{ form.dt(class='datepicker') }}
{{ form.hidden_tag() }}
<input type="submit"/>
</form>
Any attributes (that aren't reserved for other use) you pass into rendering the form element will by default just add it as an attribute, for example the {{ form.dt(class='datepicker') }} will generate <input class="datepicker" id="dt" name="dt" type="text" value=""> so your CSS/JS can then do whatever it needs to do to provide a good interface for your user.

Related

Django Crispy forms input out of order

I am building some forms using django-crispy_forms, I need them to be in the some specific order (specifically the order I stablished on the fields inside forms.py).
forms.py:
class RegistroRelatorio(forms.ModelForm):
class Meta:
model = RelatorioVendas
fields = {"razao_social","codigo_interno","nome_fantasia","endereco","bairro","uf","telefones","cnpj","fundacao",
"negocios","cidades_negocios","c1_nome","c1_whats","c1_email","c1_cargo","c1_comunicacao","c1_preferencia"}
views.py
from django.shortcuts import render
from .models import RelatorioVendas
from django.http import HttpResponse
from .forms import RegistroRelatorio
# Create your views here.
def novo_relatorio(request):
form = RegistroRelatorio(request.POST or None)
if form.is_valid():
form.save()
return HttpResponse('Ok...')
return render(request,'river/novo_relatorio.html',{'form':form})
html:
{% extends 'river/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h3>Novo Relatorio</h3>
<form method="post" >
{% csrf_token %}
{{form | crispy}}
<button type="submit">Ok</button>
</form>
{% endblock %}
I tried to use label_order inside this class, it worked, but when I use the crispy form it stops working...
I even tried to use the Layout method from the crispy library.
Use a 'list' instead of a 'set' for the fields in forms.py:
class RegistroRelatorio(forms.ModelForm):
class Meta:
model = RelatorioVendas
fields = ["razao_social", "codigo_interno", "nome_fantasia", "endereco", "bairro", "uf", "telefones", "cnpj",
"fundacao", "negocios", "cidades_negocios", "c1_nome", "c1_whats", "c1_email", "c1_cargo",
"c1_comunicacao", "c1_preferencia"]
According to the documentation:
A set is an unordered collection with no duplicate elements.
I don't see any problems here for crispy to render fields in the order that you provided. Show your html and views.

WTForms error handling with seamless user experience

I'm new to WTForms, and from what I've learned so far, I find the validation error handling a bit tricky.
First off, I can't implement the native HTML <input required >;
Secondly, when the form failed validation, I have to rerender the page template where the form is at, and if my form is placed on bottom of a page, the user will see the page 'refreshed' and have no idea what is going on.
Does anyone have suggestions on how to integrate WTForms more seamlessly? Any comments, resources, urls, code samples are welcomed, thanks!
Here are some of my related codes:
# /forms.py
from wtforms import Form, BooleanField, StringField, PasswordField, validators
class RegistrationForm(Form):
email = StringField('Email Address', [
validators.required(),
validators.Email(message='Please enter a valid email address'),
validators.length(min=6, max=35)
])
password = PasswordField('New Password', [
validators.required(),
validators.DataRequired(),
validators.length(min=6, max=35)
])
# /views.py
from flask import Flask, Blueprint, flash, render_template, redirect, request, url_for
from forms import RegistrationForm, LoginForm
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
form = RegistrationForm()
context = {
"form": form
}
if request.method == 'POST' and form.validate():
submitted_form = request.form
return redirect('/welcome')
else:
return render_template('form.html', context = context)
# /form.html
<form class="form" action="" method="POST" name="register">
{% for field_name, field_errors in context.reg_form.errors|dictsort if field_errors %}
{% for err in field_errors %}
<li class="error">{{ context.reg_form[field_name].label }}: {{ err }}</li>
{% endfor %}
{% endfor %}
<ul class="form-fields center">
<li class="form-field">
{{ context.form.email(class='email', placeholder='email') }}
</li>
<li class="form-field">
{{ context.form.password(class='password', placeholder='password') }}
</li>
</ul>
</form>
As far as I understand, WTForm will always have to refresh the page in order to show the errors. I worked this out is by letting the front-end to validate the form for me. Do not know if it possible on your case, but I've used AngularJS to do the trick. It's quite easy, not a single line of code is required if you need simple validation like email format, password length and etc, it's all done by html attributes. You can even disable the submit button until your form is ready to go.
Check out this CodePen
You can scroll to the bottom to the page using this, if still needed:
var objDiv = document.getElementById("your_div");
objDiv.scrollTop = objDiv.scrollHeight
If you need a more robust solution, you might come up with a API that return true or false to whatever field you want to validate. Then make angular make a http request to check it as you type, this way you are 100% sure you form is going to validate. But depending on what you are checking, you might expose a security hole. There is an excellent blog post about this on Ng-Newsletter
For a better experience with forms in Flask you should use the Flask-WTF extension. Install it:
$ pip install flask-wtf
import it
from flask.ext.wtf import Form
and use it along with wtforms module.
.py file:
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
.html file:
<form method="POST">
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }} #this is your submit button
</form>

Django forms - unwanted error message on first display of the form

I've been doing the django tutorial and I'm now trying to adapt it to my needs.
In part 04, the turorial teaches us how to write forms.
I have re-used this part of the tutorial to try and write a radio-button select form.
Most of it is working as the different inputs are shown, selectable, and validating does send me to the associated page.
The code is supposed to display an error_message if the form is validated without an answer.
My problem is that this message is already displayed the first time I open the page.
I've looked for the reason everywhere but I seem to be the only one having this problem.
Here is the index.html file
<!DOCTYPE html>
<html>
<head>
<title>Syl - Projects</title>
</head>
<h1>Project List</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="" method="post">
{% csrf_token %}
{% for choice in project_list %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.name }}</label><br />
{% endfor %}
<input type="submit" value="Accéder au projet" />
</form>
</html>
And here is the views.py file
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext, loader
from django.core.urlresolvers import reverse
from gantt.models import Project
from gantt.models import *
def index(request):
project_list = Project.objects.order_by('name')
try:
selected_project = project_list.get(pk=request.POST['choice'])
except (KeyError, Project.DoesNotExist):
return render(request, 'gantt/index.html', {
'project_list': project_list,
'error_message': "Vous n'avez pas fait de choix.",
})
else:
return HttpResponseRedirect(reverse('syl:project', args=(selected_project.slug,)))
...
...
Fixed thanks to your answers.
I had to add an IF condition to prevent the call to request.post on the first page load.
Adding this however, meant I had to add another render for the initial page load since it wasn't possible to use the Redirect for that purpose (its arguments didnt exist yet).
Thansk again.
New version of the views.py file :
def index(request):
project_list = Project.objects.order_by('name')
if request.method=="POST":
try:
selected_project = project_list.get(pk=request.POST['choice'])
except (KeyError, Project.DoesNotExist):
return render(request, 'gantt/index.html', {
'project_list': project_list,
'error_message': "Vous n'avez pas fait de choix.",
})
else:
return HttpResponseRedirect(reverse('syl:project', args=(selected_project.slug,)))
else:
return render(request, 'gantt/index.html', {'project_list': project_list})
Try adding an "If request.method == 'POST'" before you try to get your selected project in the view. When this view is loaded with a GET request by someone viewing the page for the first time, your code is already looking for a POSTed variable to use. It isn't there yet, so you get the error on load.
Wrapping that part in such an if block will tell your code "if there is no submission yet, just display the form. If there is a submission, process it." Right now it tries to skip to processing right away, sees that no selection has been made, and throws the error.

Flask wtform forms.validate_on_submit() is always false

I have looked at other similar problems on here and a few other places, but the solutions don't seem to help with my problem. Even though, I am not seeing too much of a difference between this simple code that I've got and other similar code. Especially this one Flask - wtforms: Validation always false
forms.validate_on_submit() is always false and I can't see why.
I'm going through the Flask Web Development Book by Miguel Grinberg, but I wanted to change some things in order to learn more.
It works when using the wtf.quick_form(form) in the html template, but if I remove the quickform entry and put in form fields , then it doesn't work
The screen just refreshes and it doesn't change Stranger to whatever name is entered
HTML index template
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
<form action="" method='POST'>
{{ form.name.label }} <br>
{{ form.name }}
{{ form.submit }}
</form>
{% endblock %}
relevant code hello.py
from flask import Flask, render_template, request
from flask.ext.script import Manager
from flask.ext.bootstrap import Bootstrap
from flask.ext.moment import Moment
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField, RadioField, TextField, validators
from wtforms.validators import Required
from wtforms.validators import DataRequired
app = Flask(__name__)
class NameForm(Form):
name = StringField('What is your name?',validators=[Required()] )
submit = SubmitField('Submit')
#app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm(request.form) #From the docs I read I don't need
# request.form but it
# doesn't work either with it or without it
if form.validate() == True:
name='True' #never happens is not validating or is always set to False for
# some reason
if form.validate_on_submit(): #isn't validating or working
name = form.name.data #'Stranger' will disappear from the html template and
#replaced with the name the user entered in the
# Stringfield
form.name.data = '' #clear stringfield for next round
return render_template('index.html',form=form, name=name)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)'
what am I not understanding\missing?
Thanks
g
The problem is with wtf not finding the CSRF Tokens as part of your form data. Add {{ form.hidden_tag() }} or {{ form.csrf_token }} as the top element of your form.
Just a small remind for anyone who uses bootstrap template form like me.
Be sure to add "name" attribute into the input tag as well. For example,
<label>Your name</label>
<input name = 'name' required>
<label>Your email</label>
<input name = 'email' required>

django render an object as html in a template

I am trying to render an object in a template like what we can do with forms {{ form }} but the output is turned into text and not html code. How to really include html code?
my_object = MyObject()
{{ my_object }}
The class:
from django.template import Context, Template
from django.utils.safestring import mark_safe
class MyObject(object):
def __str__(self):
return self.render()
def render(self):
t = Template('<p>This is your <span>{{ message }}</span>.</p>')
c = Context({'message': 'Your message'})
html = t.render(c)
return mark_safe(html)
You should be implementing __unicode__ instead of __str__. The templating module stringifies context variables as unicode.
You can also use
{% autoescape off %}
{{ info }}
{% endautoescape off %}
But you must be care to avoid xss vulnerabilities.
If the attempt is to get html on to the page then you should be using {{myobject|safe}}. That should render the HTML code instead of text