Auto Generating SQL Scripts based on User Input using Flask, python - python

Problem: There is an arduous update script we run for work. It takes a lot of time to update because of security issues. We want to make sure we have edited the update script correctly/want to avoid all permission issues.
Solution: Using Flask as a front end gui to eliminate the possibility for adding the wrong value. The Flask app will guarantee correct placement of inputs into the update script by auto generating the script, so we can just copy and paste into SSMS.
The current problem I am having is aesthetics. I was able to generate a flask app using user inputs and exports a SQL script. The problem is, the sql looks horrible, here is an example of the output:
Ideally the output would be multi-line, like all current SQL scripts:
UPDATE table_name
SET ordercomplete = 1
WHERE UserName = 'testuser' and OrderID = 12345
Below are my files:
#templates home.html
<!doctype html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SQL Auto Generate</title>
<link rel="stylesheet" href="https://unpkg.com/purecss#0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
</head>
<h1>SQL Auto Generate</h1>
<h2>Add the following inputs, select 'Submit' to generate a script for SQL</h2>
<body>
<div class="main">
<form class="pure-form" method="POST" action="/">
UserName:<br>
<input type="text" name="UserName"><br>
OrderID:<br>
<input type="number" name="OrderID"><br>
<button type="submit" class="pure-button pure-button-primary" value="Submit">Submit</button>
</form>
</div>
<br>
<div class="main">
{% if UserName %}
<p>
{% print('''
UPDATE table_name
SET ordercomplete = 1
WHERE
UserName = {} and OrderID = {}
'''.format(UserName, OrderID)) %}
</p>
{% endif %}
</div>
</body>
# app.py
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
UserName = ''
OrderID = ''
if request.method == 'POST' and 'UserName' in request.form:
UserName = request.form.get('UserName')
OrderID = int(request.form.get('OrderID'))
return render_template("home.html", UserName=UserName, OrderID = OrderID)
if __name__ == '__main__':
app.run(debug=True)
I also uploaded to github: https://github.com/jpf5046/sqlflask
Ask: Is there a better way to create a text output with multi-line via flask?

By default, HTML ignores extra space, such as line endings. If you want to display the query exactly like you generate it, use <pre>:
<pre>
UPDATE table_name
SET ordercomplete = 1
WHERE
UserName = {{UserName}} and OrderID = {{OrderId}}
</pre>

I think you just need to add br tags in your html
{% if UserName %}
<p>UPDATE table_name <br>
SET ordercomplete = 1 <br>
WHERE <br>
UserName = {{ UserName }} and OrderID = {{ OrderID }}
</p>
{% endif %}

Related

How to limit the number of files MultipleFileField WTForms Flask

The main requirement I need is to limit the amount of files that the end user can upload to the system.
I would also like the files to be listed so that the end user can see what files he uploaded and delete them.
class NameForm(FlaskForm):
field = MultipleFileField('fieldname',validators= [])
I understand that you can make functions and incorporate them into the validator, I am currently doing the verification with jquery.
If you want to use a MultipleFileField but limit the number of files that can be uploaded at once, you can use a Length type validator.
In combination with InputRequired you can also require at least one file.
from flask_wtf import FlaskForm
from wtforms import MultipleFileField
from wtforms.validators import InputRequired, Length
from werkzeug.datastructures import CombinedMultiDict
app = Flask(__name__)
app.secret_key = 'your secret here'
class UploadForm(FlaskForm):
files = MultipleFileField('Files',
validators=[
InputRequired(message='At least one file is required.'),
Length(max=3, message='A maximum of 3 files are allowed.'),
]
)
#app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm(CombinedMultiDict((request.files, request.form)))
if form.validate_on_submit():
# Handle the uploaded files here!
print(form.files.data)
return render_template('upload.html', **locals())
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
{{ form.csrf_token }}
<div>
{{ form.files.label() }}
{{ form.files() }}
{% if form.files.errors -%}
<ul>
{% for error in form.files.errors -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
{% endif -%}
</div>
<button type="submit">Upload</button>
</form>
</body>
</html>
In order to display a list of uploaded files and delete individual files, we need more information about your application. This also applies if you want to limit the maximum number of files a user can upload in total.

Not getting the search result

I just start learning Flask and i'm trying to use a simple search form on my news headline site but when i submit the search it seems that it's not working, i'm using a simple flask get request
Here is my code:
import feedparser
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
rss_feed = {'wsj': "https://feeds.a.dj.com/rss/RSSWSJD.xml",
'cnbc': "https://www.cnbc.com/id/100003114/device/rss/rss.html",
'washington_post': "http://feeds.washingtonpost.com/rss/politics?itid=lk_inline_manual_2"}
#app.route("/")
def get_news():
query = request.args.get('publication')
if not query or query.lower not in rss_feed:
publication = 'wsj'
else:
publication = query.lower
feed = feedparser.parse(rss_feed[publication])
return render_template("home.html", articles=feed['entries'])
if __name__ == "__main__":
app.run(port=5000, debug=True)
and my html code:
<html>
<head>
<title>Headlines</title>
</head>
<body>
<h1>Headlines</h1>
<form>
<input type="text" name="publication" placeholder="search">
<input type="submit" value="Submit">
</form>
{% for article in articles %}
<b>{{article.title}}</b><br />
<i>{{ article.published }}</i><br />
<p>{{ article.summary }}</p>
<hr />
{% endfor %}
</body>
</html>
Now when i submit a search for 'cnbc' it still display the result for 'wsj' what wrong with my code?
I add 4 different notes in the code as comment, please have a look in the Glossary.
Here is the solution:
Python file, here is where the error occur, long stoyry short your if statement was always returning True, hence the publication variable was set as 'wsj
import feedparser
from flask import render_template, request, Flask
# Note 1
app = Flask(__name__)
rss_feed = {'wsj': "https://feeds.a.dj.com/rss/RSSWSJD.xml",
'cnbc': "https://www.cnbc.com/id/100003114/device/rss/rss.html",
'washington_post': "http://feeds.washingtonpost.com/rss/politics?itid=lk_inline_manual_2"}
#app.route("/")
def get_news():
if request.args.get('publication'):
query = request.args.get('publication').lower() # Note 2
print(f'Query is : {query}') # Note 4
if not query or query not in rss_feed: # Note 3
print('Publication is wsj') # Note 4
publication = 'wsj'
else:
print('Publication is the query entry') # Note 4
publication = query
feed = feedparser.parse(rss_feed[publication])
return render_template("view.html", articles=feed['entries'])
return render_template("view.html")
if __name__ == "__main__":
app.run(port=5000, debug=True)
HTML File
<html>
<head>
<title>Headlines</title>
</head>
<body>
<h1>Headlines</h1>
<!-- Note 5 -->
<form method="GET">
<input type="text" name="publication" placeholder="search">
<input type="submit" value="Submit">
</form>
{% for article in articles %}
<b>{{article.title}}</b><br />
<i>{{ article.published }}</i><br />
<p>{{ article.summary }}</p>
<hr />
{% endfor %}
</body>
</html>
NOTES GLOSSARY
Note 1: Have the imports from the same package in the same line
Note 2: Rather then check the query if is lower is better to turn it lower already in the variable as I did.
Note 3: Here is where the real issue was. This statement was always True hence setting the 'publication' variable as 'wsj'. This is because of add 'query.lower' not in rss_feed which obsiously it wasn't. And by the way you should have done it 'query.lower()'
Note 4: Just a reminder to How to find and troubleshot your code.The print() function is the first thing to learn in python and is the most usefull tool for this matter. This is how I found the error in less then 5 minutes because I let the code 'Speak' through the terminal.
Note 5: Few suggestion here, first add a method='GET' (not neccessary) just for readability. Then indent better the HTML file as it was all messed around.
Another solution that may be slightly clearer is this:
#app.route("/")
def get_news():
if request.args.get('publication'):
query = request.args.get('publication').lower()
if not query or query not in rss_feed:
publication = 'wsj'
feed = feedparser.parse(rss_feed[publication])
return render_template("view.html", articles=feed['entries'])
publication = query
feed = feedparser.parse(rss_feed[publication])
return render_template("view.html", articles=feed['entries'])
return render_template("view.html")

How to execute a python script or call a function from a pythons script using flask & wtform

Am very much new to Flask & Python, so want to understand/clear my concepts. I have a webpage which i created using flask & wtforms. Html page is very simple having just single field & a submit button. I want to call a python script (test.py) itself or python function(pythonfunction()) when submit button is clicked. Also Is there a way from the webpage,whatever i enter , i can pass as an attribute to that python script (test.py)? help appreciated
**app.py**
from flask import Flask , render_template,flash,redirect,url_for,session,logging,request
from wtforms import Form,StringField,TextAreaField,PasswordField,validators,SelectField,TextAreaField
from wtforms.widgets import TextArea
import subprocess
import test
app=Flask(__name__)
#app.route ('/')
def index():
return render_template('home.html')
class testpython(Form):
testenter=StringField('Enter something')
#app.route ('/testpage',methods=['GET','POST'])
def testpage():
form=testpython(request.form)
return render_template('testpage.html',form=form,python=testfunc(testenter))
if __name__ == '__main__':
app.run(debug=True)
**test.py**
def pythonfunctiontest (self):
print data #<something i can print here from that text field in webpage>
return "all good"
**testpage.html**
{% extends 'sec_layout.html'%}
{% block body %}
{% from "includes/_formhelpers.html" import render_field %}
<form method="POST" action ="">
<div class="form-group">
{{render_field(form.testenter,cols="1", rows="5",class_="form-control")}}
</div>
<div class="input-bar-item input-bar-item-btn">
<button class="btn btn-info">Submit</button>
</div>
</form>
{% endif %}
{% endblock%}
sec_layout.html
<!DOCTYPE <!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>MY PAGE-TEST</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
</head>
<body>
{% include 'includes/_navbar.html' %}
<div class= "container">
{% block body %}{% endblock%}
</div>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" </script>
</body>
</html>
The question is very general so I will try and give you a steer and perhaps you might revisit this question later with a little more clarity.
Flask asks a server and renders webpages. I.e. it executes some code on the server and passes it to the client web browser. The client web browser can then execute client side code (i.e. Javascript) as the user is browsing and can pass data back to the server using submit forms (to different Flask routes) or via JavaScript AJAX requests (again to other Flask routes). So if you want to execute python script based on some input you will need a separate route.
Here is a simple example of an index page and a second route that will execute something else:
#app.route('/index')
def index():
""" very basic template render """
return render_template('index.html')
#app.route('/data-submit', methods=["POST"])
def calc():
data = request.form.information.data
# do something with data..
x = data + data
return render_template('new_page.html', x)
========= (index.html)
<html>
<body>
<form action="{{ url_for('app.calc') }}" method="POST">
<input name="information" type='text'>
<button name="submit">
</form>
</body>
</html>
Wrap whatever temp.py is doing in a function.
Place it in the same directory as flask.py. Call import temp in flask.py, then use temp.myfunction().

Flask custom form in home Tab with custom action

I'd like to visualize a form in home/admin page that allows users to select a value from a list (values belongs to a db table) and a button to execute a custom python method.
I am not able to understand if it's possibile to show a form without showing data of if it's possible to run code without flask-admin.
P.s. the same (simple) code that I use to create a form (just 2 datepickers) in Flask works but as soon as I put it in /home/admin the html and flask-admin cannot talk (exchange the values in the form) anymore.
Update:
This is part of my Flask-admin code:
class ExampleForm(Form):
dt_start = DateField('DatePicker', format='%Y-%m-%d')
dt_end = DateField('DatePicker', format='%Y-%m-%d')
#app.route('/admin', methods=['POST','GET'])
def index():
form = ExampleForm()
if form.validate_on_submit():
print("Start date: ", form.dt_start.data.strftime('%Y-%m-%d'))
print("End date: ", form.dt_end.data.strftime('%Y-%m-%d'))
return "Form is OK!"
return render_template('admin/index.html', form=form)
HTML CODE:
{% extends "admin/master.html" %}
{% block body %}
<head>
<title>Download form</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<div class="container">
<h1>Please insert the dates</h1>
<br>
<form action="#" method="post">
{{ form.dt_start(class='datepicker') }}
{{ form.hidden_tag() }}
{{ form.dt_end(class='datepicker') }}
{{ form.hidden_tag() }}
<input type="submit"/>
</form>
</div>
{% endblock %}
ERROR:
jinja2.exceptions.UndefinedError: 'form' is undefined
Thanks a lot
Alessandro

Flask-Sqlite posting to DB from webform

I have started just learning Flask and tried to find an answer how to post to SQLite DB from webform. So far haven't managed to get it work and bit lost with this. I manage to print values from DB based on code sample from simplypython but don't know how to add new ones from webform.
I would need to be able to address elements, open connection to the database, Insert values, save and close the connection. As far as I am aware I should add the POST method to the app.py and use request.form statement to pull the elements when submit button is pressed.
then the code should automatically display all the values on index html, which already works. could you please help me with the code I need to add to app.py file to get values added to DB and what to add to form action webform-section on html file?
index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask Intro</title>
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
</head>
<body>
<div class="container">
<h3 potsit </h3>
{% for post in posts %}
Titleotsikko: {{post.title }} <br>
Postotsikko: {{post.description}}
{% endfor %}
</div>
<div>
<form action="/????????????NOT SURE WHERE TO DIRECT" method="post">
<div>
<label for="title">title:</label>
<input type="text" id="title" />
</div>
<div>
<label for="description">description:</label>
<input type="text" id="description" />
</div>
<div class="button">
<button type="submit">Add to db</button>
</div>
</form>
</div>
</body>
</html>
app.py
from flask import Flask, render_template, request, session, g
import sqlite3
app = Flask(__name__)
#app.route('/')
def home():
g.db = sqlite3.connect("sample.db")
cur = g.db.execute('select * from posts')
posts = [dict(title=row[0], description=row[1]) for row in cur.fetchall()]
g.db.close()
return render_template("index.html", posts=posts)
if __name__=='__main__':
app.run(debug=True)
sql.py
import sqlite3
with sqlite3.connect("sample.db") as connection:
c = connection.cursor()
c.execute("DROP TABLE posts")
c.execute("CREATE TABLE posts(title TEXT, description TEXT)")
c.execute('INSERT INTO posts VALUES("Luck", "no luck.")')
EDIT
I made some changes based on the suggestion from Paul Rooney and created file called post.html, moved the form there from index.htmland added another #route on the app.py file. I believe I'm not far off but after trying to figure this out since Tuesday hoping to get it work. Unfortunately now I receive an error 405 'The method is not allowed for the requested URL.' and I'm stuck with this after trying different options.
post.html
<!DOCTYPE html>
<html>
<head>
<title>Flask post</title>
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
</head>
<body
<div>
<form action="/post" method="post">
<div>
<label for="title">title:</label>
<input type="text" id="title" />
</div>
<div>
<label for="description">description:</label>
<input type="text" id="description" />
</div>
<div class="button">
<button type="submit">Add to db</button>
</div>
</form>
</div>
</body>
</html>
(edited) app.py
from flask import Flask, render_template, request, session, g, redirect, url_for
import sqlite3
app = Flask(__name__)
#app.route('/')
def home():
g.db = sqlite3.connect("sample.db")
cur = g.db.execute('select * from posts')
posts = [dict(title=row[0], description=row[1]) for row in cur.fetchall()]
g.db.close()
return render_template("index.html", posts=posts)
#app.route('/post', methods=['POST'])
def post():
title=request.form['title']
description=request.form['description']
print title, description
return redirect(url_for('/'))
if __name__=='__main__':
app.run(debug=True)
To answer your original question you should pick an endpoint to post your data to and use that in both your flask app and the html form action.
I suggested post in comments but it could be anything. I think you've grasped what to do here but for completeness.
The HTML
<form action="/post" method="post">
The Python Code
Add a route to handle the POSTed data.
#app.route('/post', methods=['POST'])
def post():
# Do db stuff then redirect back to index page.
pass
Edited Question
You have an indentation error in your code, which is causing the 405 error. Your post function is inside your home function. Make it more like this
app.py
from flask import Flask, render_template, request, session, g, redirect, url_for
import sqlite3
app = Flask(__name__)
#app.route('/')
def home():
g.db = sqlite3.connect("sample.db")
cur = g.db.execute('select * from posts')
posts = [dict(title=row[0], description=row[1]) for row in cur.fetchall()]
g.db.close()
return render_template("index.html", posts=posts)
# One level of indentation removed from here downwards
#app.route('/post', methods=['POST'])
def post():
title=request.form['title']
description=request.form['description']
return redirect(url_for('home'))
if __name__=='__main__':
app.run(debug=True)
After that I see a 400 Bad Request error, which stems from the fact that you don't have name parameters in your html form.
The attempt to access the title and description values in the form dict in app.py will throw a KeyError exception, as those keys will not be present without the name parameter in the html.
If you add them e.g.
...
<input type="text" id="title" name='title'/>
...
<input type="text" id="description" name='description'/>
...
Then it will run all the way through your function.
The next issue will be
redirect(url_for('/'))
instead use
redirect(url_for('home'))
home being the name of the function called for path '/'.
After that you should be good to go.

Categories