I am new to flask an am having an issue with creating a dynamic url from form data. Specifically from the value of a SelectField of WTFORMS. My code is as follows
My form looks like this
from flask_wtf import Form
from wtforms import SelectField
from wtforms.fields.html5 import DateField
class SelectEventForm(Form):
sports = SelectField(u'Select Sport')
start_after_date = DateField('Starts After Date')
start_before_date = DateField('Starts Before Date')
My controller has the following code
#app.route('/event', methods=['GET', 'POST'])
def event():
form = SelectEventForm(request.form)
sports = betfair_client.call_rest_api('listEventTypes/', {"filter": {}})
form.sports.choices = []
for sport in sports:
for key in sport:
form.sports.choices.append((key, sport[key]))
return render_template('events.html', form=form)
#app.route('/event/<sports>', methods=['GET', 'POST'])
def event_select(sports):
#print request.form
#print request.form.get('sports')
return render_template('events_two.html')
The form in html is as follows
<form class="form-style-7" action="{{ url_for('event_select', sports=form.sports.sport) }}" method="post">
<ul>
<li name="sport">
{{ form.sports.label}} {{ form.sports }}
</li>
<li>
{{ form.start_after_date.label }} {{ form.start_after_date }}
</li>
<li>
{{ form.start_before_date.label }} {{ form.start_before_date }}
</li>
<li>
<input type="submit" value="Next">
</li>
</ul>
</form>
What I would like to do, is se the value from the SelectField to generate the url. Have been stuck on this for some time. The form itself and the drop down list display everything correctly
Thanks
I'd change your form to post back to it's generating page, and then deal with the logic from there. So change
{{ url_for('event_select', sports=form.sports.sport) }}
to just:
{{ url_for('event') }}
then adjust your event method to be something like:
from flask import redirect
#app.route('/event', methods=['GET', 'POST'])
def event():
form = SelectEventForm(request.form)
if form.validate_on_submit():
chosen_sport = form.sports.data
return redirect(url_for('event_select', sports=chosen_sport))
sports = betfair_client.call_rest_api('listEventTypes/', {"filter": {}})
form.sports.choices = []
for sport in sports:
for key in sport:
form.sports.choices.append((key, sport[key]))
return render_template('events.html', form=form)
All we're doing is grabbing the from if it's submitted, then grabbing the chosen sport from the form element, and then using that to redirect the user to the event_select method with the chosen sport as the argument.
Related
Apologies if this isn't worded very well. I can't seem to get any error messages to show when I haven't fulfilled the validation requirements. When I submit, the form is just refreshed without error messages such as "This field is required".
Also, when I do input data, if form.validate_on_submit(): doesn't return the expected homepage, it once again just refreshes the page. I'm aware the form does nothing, I plan on adding the data submitted to a database but I'm stuck on this step. Can anybody help? Thanks.
app.py
from cs50 import SQL
from flask import Flask, render_template, session, redirect
from flask_session import Session
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField #bool for checkbox
from wtforms.validators import InputRequired, Email, Length
# configure application
app = Flask(__name__)
app.config["SESSION_PERMENANT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# Allows webpage to update html on refresh
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config['SECRET_KEY'] = 'Itsasecret'
# configure CS50 library to use SQLite database
db = SQL("sqlite:///project.db")
# Usually separate routes, database and form stuff into different files and link together
class LoginForm(FlaskForm):
username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)])
password = PasswordField('Password', validators=[InputRequired(), Length(min=8, max=80)]) #80 is a special number (learn more)
remember = BooleanField('Remember me')
#app.route('/')
def homepage():
return render_template("homepage.html")
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
return redirect("/")
return render_template("login.html", form=form)
login template
{% extends "layout.html" %}
{% from "_formhelpers.html" import render_field %}
{% set active_page = 'login' %}
{% block title %} Login {% endblock %}
{% block body %}
<div class="ls-main">
<form action="/login" method="POST">
{{ form.csrf_token }}
{{ render_field(form.username) }}
{{ render_field(form.password, autocomplete="off") }}
{{ render_field(form.remember) }}
<div class="ls-div">
<input class="ls-input, signinbutton" name="submit" type="submit" value="Submit">
</div>
</form>
</div>
{% endblock %}
_formhelpers.html
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
return render_template("/") is invalid. I think you mean return redirect("/")
Of course you need from flask import Flask, render_template, session, redirect
Created a basic sign in form in flask. The index page renders just fine, however /login does not render any form. No errors have popped up either. [returns a 200 response]
My forms.py is:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
My routes.py is:
from flask import render_template, flash, redirect, url_for
from app import app
from app.forms import LoginForm
#app.route('/')
#app.route('/index')
def index():
return render_template('index.html', title='Home')
#app.route('/login')
def login():
form = LoginForm()
#return render_template('index.html', title='Home')
return render_template('login.html', title='Sign In', form=form)
My template for the form is:
{% extends "base.html" %}
{% block content %}
<h1>Sign In</h1>
<form action="" method="post" novalidate>
<!---{{ form.hidden_tag() }}-->
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}
</p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}
The index base that the template above extends is rendered but no form:
The terminal output:
Tried this on chrome, safari and firefox.
You probably forgot to declare your block in the base.html file.
In base.html :
<body>
... base content ...
{% block body %}
... your login.htlm will set there ...
{% endblock %}
</body>
I'm trying flask nad there is a simple task to submit a form.
Page is showing a one picture and a form, if form was submitted correctly, picture should be changed, if not - be the same.
I can't understand the mechanism of how to show only one object on a page and get another after form submitting.
Tried to use iterator over the list of images is folder "static", but my implementation was not working correctly.
Please provide me a feedback how to do it in rigth way?
Now I have the simple view:
#app.route("/", methods=["GET", "POST"])
def start_view():
picture = None
form = InputForm(csrf_enabled=False)
if form.validate_on_submit():
picture = form.picture.data
form.picture.data = ""
return render_template('04-2.html', form=form, picture=picture)
class InputForm(Form):
picture = StringField('What is on a picture?', validators[DataRequired()])
submit = SubmitField('Submit')
And a simple template:
<body>
<form method="POST">
{{ form.picture.label }} {{ form.picture }}
{{ form.submit() }}
</form>
{% if form.errors %}
<span style="color: red">{{ form.error }}</span>
{% endif %}
</body>
Thank you!
Your form doesnt contain any pictures. It has a StringField and a SubmitField. If you want to see any image you need to have an <img> tag in your HTML pointing to the image location in your server
your view should look like:
from Flask import session
# in order to use sessions you have to use a secret key for your app
app.secret_key = 'some secret key'
#app.route("/", methods=["GET", "POST"])
def start_view():
img_list = ['filename1', 'filename2', 'filename3']
# if this is not the first form submission
if session.has_key('current'):
# if we reach the end of the list show the first image again
if int(session['current']) == len(img_list) - 1:
session['current'] = 0
# move to next image
else:
session['current'] = int(session['current']) + 1
else:
session['current'] = 0
picture = 'first_image_filename' # this should be the img on load
form = InputForm(csrf_enabled=False)
if form.validate_on_submit():
picture = img_list[int(session['current'])] # the filename of the next image
form.picture.data = ""
return render_template('04-2.html', form=form, picture=picture)
so template should look like:
<body>
<form method="POST">
{{ form.picture.label }} {{ form.picture }}
<img src="{{url_for('static', filename='img/' + picture)}}"
{{ form.submit() }}
</form>
{% if form.errors %}
<span style="color: red">{{ form.error }}</span>
{% endif %}
</body>
I have a question about rendering form results. When i submit the form i want to show the term below the form. what did i wrong ?
Now wehen i submit the form i get a 200 Status Code and no error message. But the term dosent show up in the defined place.
# app.py
#app.route('/search')
def search():
return render_template('search/index.html')
#app.route('/search<q>')
def results(q):
return render_template('search/index.html', term=q)
# search/index.html
{% extends 'base.html' %}
{% block content %}
<div>
<form method="get" action="">
<input type="text" name="q" id="">
<input type="submit">
</form>
{{ term }}
</div>
{% endblock %}
You have confused path parameters (which Flask routes parse out and pass to the view) with query parameters (which are available in request.args). Remove your second route and update the first to access the query.
from flask import request
#app.route('/search')
def search():
term = request.args.get('q')
return render_template('search.html', term=term)
I would like to have an input form where a user inputs either an incident_id or a equipment_id or both. The problem I have now is for example, if I fill in incident_id and leave equipment_id blank, the query will return no hits even when incident_id finds a match, because my query uses blank equipment_id in the search. How can I set it up so that when I leave a field blank, it doesn't use it in the query? Do I need to validate this data? I'm not inserting any of it into the db.
I know Django forms uses POST, but in this instance I feel that GET is more appropriate, which means I don't have to use Django forms, but does that mean I also don't have to validate the data? Or should I just use POST anyway and validate that data with Django forms? I'm sorry this is so conceptual. I'm not finding many good answers online.
model.py
from django.db import models
class Incident(models.Model):
incident_id = models.CharField(max_length=1000, blank=True)
equipment_id = models.ForeignKey(Equipment, blank=True)
class Equipment(models.Model):
equipment_id = models.CharField(primary_key=True, max_length=100)
manufacturer = models.ForeignKey(Manufacturer)
equipment_category = models.ForeignKey(Equipment_Category)
validated= models.BooleanField(default=True)
in_service_date = models.DateTimeField('in service date', default=timezone.now)
view.py
#login_required
def search_incidents_query(request):
if request.method == 'GET':
incident_id_query = request.GET.get('incident_id')
equipment_id_query = request.GET.get('equipment_id')
try:
incident_id = str(incident_id_query)
except ValueError:
incident_id = None
try:
equipment_id = str(equipment_id_query)
except ValueError:
username = None
list = [incident_id,equipment_id]
if all(x is None for x in list): #check if `list` contains only None
incident_list = None #this in case the query comes back empty
else: #perform query
incident_list = Incident.objects.filter(incident_id=incident_id, equipment_id=equipment_id)
)
return render(request, 'search_incidents_query.html', {
'incident_list' : incident_list
})
search_incidents_query.html
{% extends "base.html" %}
{% load widget_tweaks %}
{% block content %}
<br>
<div class="container-fluid">
<!-----INPUT FORM------------------->
<form method='GET' action='/search_incidents/'>
<div class="row">
<div class="form-group col-md-3">
<label>Incident ID</label>
<input type="text" name="incident_id" value="{{ incident_id_query }}" class="form-control"/>
</div>
<div class="form-group col-md-3">
<label>Equipment ID</label>
<input type="text" name="equipment" value="{{ equipment_id_query }}" class="form-control"/>
</div>
</div>
</form>
</div>
{% endblock %}
The query
You should use a Q object for similar queries.
from django.db.models import Q
Incident.objects.filter(
Q(incident_id=incident_id) | Q(equipment_id=equipment_id)
)
More on Q objects.
Also, IMO this code needs to live in some Form class. If it was me, I would have put this code in some
The form
class IncidentSearchForm(forms.Form):
incident = forms.CharField(required=False)
# ModelChoiceField because we want django to do the validation for us
# TextInput because the default widget is a select tag
equipment = forms.ModelChoiceField(Equipment.objects.all(), required=False, widget=forms.TextInput) # TextInput because
def clean(self):
# I'd use the clean method to force the user to provide either an incident or equipment value
def search(self):
return Incident.objects.filter(
Q(incident_id=self.cleaned_data['incident']) |
Q(equipment_id=self.cleaned_data['equipment'])
)
The view
Why aren't you using Class Based Views already?
username = None. Wat?
You should use a form because never trust user input.
#login_required
def search_incidents_query(request):
form = IncidentSearchForm(request.GET or None)
incident_list = None
if 'equipment' in request.GET or 'incident' in request.GET:
incident_list = None # Or maybe Incident.objects.none()
if form.is_valid():
incident_list = form.search()
return render(request, 'search_incidents_query.html', {'form' : form})
The template
<form method='GET' action='/search_incidents/'>
<div class="row">
<div class="form-group col-md-3">
{{ form.incident }}
</div>
<div class="form-group col-md-3">
{{ form.equipment }}
</div>
</div>
<input type="submit" />
</form>
You can use a widget on the form to add form-control class to the fields.