Python Flask Save Form Data to DB - python

I am new to Flask and I am trying to save form data from a Flask form to database using SQLAlchemy and I am not having any luck. I have tried several methods from research I found both here and outside of this forum.
When I take the simplistic route, the web form works and I can enter data but it will not populate the DB.
----Models----
class QIDMapping(db.Model):
id = db.Column(db.Integer, primary_key=True)
qid_number = db.Column(db.Integer)
br_field_name = db.Column(db.String(75))
vendor_field = db.Column(db.String(75))
----Forms----
class QIDForm(Form):
qidnumber = IntegerField('qidnumber', validators=[DataRequired()])
brfieldname = StringField('brfieldname', validators=[DataRequired()])
vendorfieldname = StringField('vendorfieldname')
----Views----
from flask import render_template, flash, redirect, session, url_for,
request, g
from flask_wtf import form
from app import app, db
from .forms import QIDForm
from .models import User, QIDMapping
from flask.ext.sqlalchemy import SQLAlchemy
#app.route('/qidmapping', methods=['GET', 'POST'])
def qid_map_update():
form = QIDForm()
return render_template('qidmapping.html',
title='QID Mapping',
form=form)
----qidmapping.html----
{% block content %}
<h1>Map QIDs to Vendor File</h1>
<form action="" method="POST">
{{form.hidden_tag()}}
<p>
Please enter the QID, BrassRing Field Name and Vendor Tag
<br>
<h2>QID Number {{ form.qidnumber(size=25) }}<br></h2>
<h2>BR Field {{ form.brfieldname(size=25) }}<br></h2>
<h2>Vendor Field {{ form.vendorfieldname(size=25) }}<br></h2>
</p>
<p><br>
</p>
<p><input type="submit" value="Save Fields">
</p>
</form>
{% endblock %}
I have also tried the method in this post Flask - WTForm - save form to db
and when I do, I get a Method Not Allowed error and I'm not sure why.
----view for question 20837209 format----
#app.route('/qidmapping', methods=['GET', 'POST'])
def qid_map_update():
form = QIDForm()
if form.validate_on_submit():
newform = (
form.qidnumber.data,
form.brfieldname.data,
form.vendorfieldname.data
)
db.session.add(newform)
db.session.commit()
return redirect('/qidmapping')
return render_template('qidmapping.html',
title='QID Mapping',
form=form)
Any help would be greatly appreciated!

Try replacing
newform = (
form.qidnumber.data,
form.brfieldname.data,
form.vendorfieldname.data
)
db.session.add(newform)
with
m = QIDMapping()
m.qid_number = form.qidnumber.data
m.br_field_name = form.brfieldname.data
m.vendor_field = form.vendorfieldname.data
db.session.add(m)
... and if that doesn't work. Do your standard POST troubleshooting:
1) Verify POST request
2) Ensure CSRF is working correctly.
3) Log validation errors / success
4) Check for DB exceptions

Related

url_for method inside template is not working

thank you so much for your help.
index.html
{% extends "base.html" %}
{% block content %}
<form action="{{ url_for('details') }}" method="POST">
{{ form.firstName.label }} {{form.firstName}}
<br>
{{form.submit}}
</form>
{% endblock %}
when click on submit button i get Method Not Allowed The method is not allowed for the requested URL. looks like the action attribute in the above code is not redirecting to details function which will take to details view page when submit button is clicked but when i manually type details in the address bar it redirects to details view page with no results. wondering why it button click is not working
details.html
{% extends "base.html" %}
{% block content %}
{{session['firstName']}}
{% endblock %}
p.py
from flask import Flask
from flask import render_template, redirect, url_for, session
from flask_wtf import FlaskForm
from wtforms import StringField, EmailField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo
app = Flask( __name__ )
app.config["SECRET_KEY"] = "for test"
class userInfo(FlaskForm):
firstName = StringField("enter first name:", validators=[DataRequired()])
submit = SubmitField("Submit")
#app.route("/", methods=["POST", "GET"])
def index():
form = userInfo()
if form.validate_on_submit():
session['firstName'] = form.firstName.data
form.firstName.data = ''
return redirect(url_for("details"))
return render_template("index.html", form=form)
#app.route("/details")
def details():
return render_template("details.html")
if __name__ == "__main__":
app.run(debug=True)
i want to navigate to details view page when my form in index view page is submitted but when button is clicked i get error message instead of navigating to different view page.
From the documentation:
By default, a route only responds to a GET request. You can use
the method's argument of the route() decorator to handle different HTTP
methods.
(https://flask.palletsprojects.com/en/1.1.x/quickstart/#http-methods)
You should add a decorator argument methods=["POST"] to receive a POST request.

Prevent duplicate form submissions while awaiting a response Flask Python

I am running a large request via API and massaging some data before the user sees it. I am looking to prevent the user from clicking the download button while all this information is processed. What would be the best way to accomplish this through a Flask Form?
Here is my HTML:
<form method="POST">
<button type="submit" name="download" value="download" class="button is-primary is-light">Download</button>
{% if error_statement %}
<article class="message is-danger">
<div class="message-body">
{{ error_statement }}
</div>
</article>
{% endif %}
</form>
Here is my Flask Form:
from datetime import date
import pandas as pd
from flask_wtf import FlaskForm
from wtforms import *
from flask import (
Flask,
g,
redirect,
render_template,
request,
session,
url_for,
flash,
Response
)
app = Flask(__name__)
app.secret_key = 'secret'
#app.route('/home', methods=["POST", "GET"])
def home():
class MyForm(FlaskForm):
submit = SubmitField('Download')
if request.method == 'POST':
form = MyForm()
if request.form['download'] == 'download':
#At this point I have code where I call a bunch of APIs and convert data to a CSV file
#This process takes anywhere between 1-3 minutes to complete
if not final_df.empty:
today = date.today()
return Response(final_df.to_csv(index=False, header=True), mimetype="text/csv", headers={"Content-disposition": "attachment; filename=export" + today.strftime("%Y/%m/%d") + ".csv"})
else:
error_statement = 'Something Went Wrong Please Try Again'
return render_template("login.html", error_statement=error_statement)
return render_template('home.html', form=form)
return redirect(url_for('login'))
Can anyone provide guidance on how to prevent the user from clicking the download button while my data is processed?

Flask-Security: Customizing Registration

I'm trying to use Flask-Security to login and register users. However, I'm trying to do two things. The first being change the default /registration to /signup which I believe I've done correctly.
The second is I want to customize the registration form. I can get the page loaded but whenever I submit the form nothing happens. No data is sent to the backend or inserted into the sqlite database.
I'm also not sure if I need to write my own app.route function to handle creating a new user and/or checking if they already exist. I'd like to be able to use flask-security's register but just tweak it. If that's even possible.
Here's my main python file:
from flask import Flask, redirect, flash, session, request, render_template
from flask_sqlalchemy import SQLAlchemy
import os, time
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
app.config['DATABASE'] = 'data.db'
app.config['SECURITY_TRACKABLE'] = True
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_REGISTER_URL'] = '/signup'
app.config['SECURITY_REGISTER_USER_TEMPLATE'] = 'security/register.html'
# enable this if you want to send email and confirm
# app.config['SECURITY_CONFIRMABLE'] = True
db = SQLAlchemy(app)
from user import Security, User, user_datastore
from forms import logform, signupform, RegisterForm
security = Security(app, user_datastore, register_form=signupform, login_form=logform,
confirm_register_form=signupform)
db.create_all()
db.session.commit()
#app.route('/')
def hello_world():
log = logform(csrf_enabled=False)
flash("HELLO.")
return render_template("index.html", form=log)
#app.route('/login')
def login():
print "/LOGIN REQUESTED"
form = logform(csrf_enabled=False)
return render_template("security/login_user.html", login_user_form=form, security=security)
#not sure if I need this function at all
#app.route('/signup', methods=['GET', 'POST'])
def signup():
print "/SIGNUP REQUESTED"
form = signupform(csrf_enabled=False)
# form = RegisterForm()
if request.method == 'GET':
return render_template("security/register.html", register_user_form=form, security=security)
else:
#log = logform(csrf_enabled=False)
if form.validate_on_submit() or form.validate():
print "HERE NOW SOMEONE SUBMITTED SOMETHING"
print form.email.data
print form.username.data
print form.password.data
user_datastore.create_user(form)
db.session.commit()
return redirect('/')
I'm not sure if I need the /signup function or if flask-security will handle it on default.
Next here's my logform and signupform classes:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
from flask_security.forms import LoginForm, RegisterForm, Form
class logform(LoginForm):
login = StringField('login', validators=[DataRequired()])
# password = PasswordField('password', validators=[DataRequired()])
class signupform(RegisterForm):
# email = StringField('email', validators=[Email(), DataRequired()])
username = StringField('username', [DataRequired()])
# password = PasswordField('password', validators=[DataRequired()])
Here's the current registration form I'm trying to use. I'm not sure if I should use url_for('signup') as flask-security's default registration form uses url_for_security('register').
<!DOCTYPE html>
{% extends "base.html" %}
{% block content %}
<div id="signup">
<h1>Create Account.</h1>
<h3>TESTING</h3>
{# not sure what to put as my action. default flask-security uses url_for_security('register') #}
<form id="logfrm" name='register_form' method="POST" action="{{ url_for('signup') }}">
{# figure out how to make input required #}
{{ register_user_form.email(placeholder="email", type="email") }}<br>
{{ register_user_form.username(placeholder="username") }}<br>
{{ register_user_form.password(placeholder="password", type="password") }}<br>
{# register_user_form.confirm(placeholder="confirm password") #}<br>
{{ register_user_form.submit }}
<input type="submit" value="SIGNUP" >
</form>
</div>
{% endblock %}
Any help would be great, thanks!
Thanks for the help. That really cleared up most of the trouble I was having.
The only other thing wrong was in my form. I was forgetting that RegisterForm requires a password confirmation so I also added:
{{ register_user_form.password_confirm(...) }}
and to also include a app.config['SECRET_KEY'] = 'blah' in my main file so that the registration doesn't run into an error.
You don't need to define a new route for the registration.
In your signupform class that is inherited from RegisterForm you only need to override the validate method with your custom logic and return True or False as appropriate.
In your html template for registration the action url is {{ url_for_security('register') }}
The same applies for a custom login form - the url action though is {{ url_for_security('login') }}

Determine which WTForms button was pressed in a Flask view

I have a page with multiple links to redirect the user to different pages. I thought using a form would be nicer, so I defined a WTForms Form with multiple SubmitFields. How do I determine which button was clicked and redirect based on that?
class MainForm(Form):
user_stats = SubmitField('User Stats')
room_stats = SubmitField('Room Stats')
#main.route('/')
#login_required
def index():
form = MainForm()
return render_template('index.html', form=form)
<form action="#" method="post">
{{ wtf.quick_form(form) }}
</form>
You added two buttons to the form, so check which of the fields' data is True.
from flask import Flask, render_template, redirect, url_for
from flask_wtf import Form
from wtforms import SubmitField
app = Flask(__name__)
app.secret_key = 'davidism'
class StatsForm(Form):
user_stats = SubmitField()
room_stats = SubmitField()
#app.route('/stats', methods=['GET', 'POST'])
def stats():
form = StatsForm()
if form.validate_on_submit():
if form.user_stats.data:
return redirect(url_for('user_stats'))
elif form.room_stats.data:
return redirect(url_for('room_stats'))
return render_template('stats.html', form=form)
app.run(debug=True)
<form method="post">
{{ form.hidden_tag() }}
{{ form.user_stats }}
{{ form.room_stats }}
</form>

Python Flask - How to use SubmitField to delete object?

I'm a bit confused as how to have a "Delete" button on a page that will delete the object currently in focus.
I'm trying to add this button to /edit/ to delete whichever id is open
Using Python3 and Flask
forms.py
class EditForm(Form):
name = StringField('Server Name', validators = [Length(1, 120), DataRequired()])
ip_address = StringField('IP Address', validators = [Length(1, 16), IPAddress()])
username = StringField('UCX User', validators = [Length(1, 64)])
password = StringField('UCX Password', validators = [Length(1, 64)])
description = StringField('Purpose/Description', validators = [Length(1-120)])
protocol = RadioField('Protocol', [DataRequired()],
choices=[('https', 'HTTPS'), ('http', 'HTTP')], default='https')
submit = SubmitField('Submit')
**delete = SubmitField('Delete')**
Routes.py
#servers.route('/edit/<id>', methods=['GET', 'POST'])
def edit(id):
server = UcxServer.query.filter_by(id=int(id)).first_or_404()
form = EditForm(obj=server)
if form.validate_on_submit():
form.to_model(server)
db.session.commit()
flash('Your changes have been saved.')
return render_template('addserver2.html', form=form)
Routes.py delete function:
#servers.route('/delete/<id>')
def delete(id):
server = UcxServer.query.filter_by(id=int(id)).first_or_404()
try:
db.session.delete(server)
db.session.commit()
flash('Successfully deleted the {} server'.format(server))
return redirect(url_for('servers.index'))
Template (addserver2.html):
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block page_content %}
<div class="page-header">
<h1>UCX Server</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
So basically, I can load the edit/ page, but how do I hook up the "Delete" SubmitField to call the /delete/?
Figured it out. Posting answer for future folks.
Not sure if best way, but only took 2 lines of code:
For the /edit/ route, I simply added this check.
if form.delete.data:
return redirect(url_for('servers.delete', id=id))
Which makes the entire edit route look like this:
def edit(id):
server = UcxServer.query.filter_by(id=int(id)).first_or_404()
form = EditForm(obj=server)
if form.delete.data:
return redirect(url_for('servers.delete', id=id))
if form.validate_on_submit():
form.to_model(server)
db.session.commit()
flash('Your changes have been saved.')
return render_template('addserver2.html', form=form)
Maybe you can use customized validators. Like this:
delete = SubmitField('Delete', validators=delete())
About how to make a function a customized validator, check this link. The custom validators section.

Categories

Resources