Hello Stackoverflow community,
I am new to FLASK however while the learning curve has been very steep, there is one item that I have not been able to get my head around.
I am using a very simple HTML seach form, into which users type the name of a city, this input gets passed to a Mysql query and returns output into a Table.
Everything works except that I can't get the variable to pass into Mysql... if I fix the query it works.
I tried to work with FLASK WTForms, POST and GET requets, but I don't know where I am going wrong.
The variable data that I am passing is not confidencial, so I have no concern if it shows up in the URL.
Here just the simple FORM (I guess not correct)
<form>
<div class="form-group">
<div class="col-sm-3">
<input type="text" placeholder="City Name" name="City_Name" action=/search class="form-control">
</div>
<div class="form-group">
<div class="col-sm-2">
<input type="submit" value="SEARCH" class="btn btn-primary btn-block">
</div>
</div>
</form>
Here the table output (working perfectly)
<table class="table table-striped">
<tr>
<th>PO_Number</th>
<th>Plant Name</th>
<th>GMID</th>
<th>Material</th>
<th>INCOTERM</th>
<th>Vendor</th>
<th>Vendor Count</th>
</tr>
{% for row in tabledata %}
<tr>
<td>{{ row['PO_Number'] }}</td>
<td>{{ row['PN'] }}</td>
<td>{{ row['GD'] }}</td>
<td>{{ row['MN'] }}</td>
<td>{{ row['INCO'] }}</td>
<td>{{ row['VNGS'] }}</td>
<td>{{ row['CVNGS'] }}</td>
</tr>
{% endfor %}
</table>
Here the Python code
from flask import Flask, render_template, request, url_for
from dbhelper_single_search import DBHelper
app = Flask(__name__)
DB = DBHelper()
#app.route('/table')
def table():
try:
tabledata = DB.table_inputs()
except Exception as e:
print(e)
tabledata = None
return render_template("table.html", tabledata=tabledata)
if __name__ == '__main__':
app.run(port=5000, debug=True)
Data Base Helper Mysql (the valye for PLN should change based on the input in the Form.
import pymysql
class DBHelper:
def table_inputs(self):
connection = self.connect()
PLN="**City_Name**"
try:
query = "SELECT Plant_Geo, Plant_Code, Plant_Name, GMID, Material_Name, GROUP_CONCAT(DISTINCT Vendor_Name_GS ORDER BY Vendor_Name_GS) as VNGS, sum(2014_USD), sum(2015_USD), sum(2016_USD) FROM invoice_report WHERE plant_code like '%s' GROUP BY GMID ORDER BY sum(2015_USD) DESC" %(PLN);
with connection.cursor(pymysql.cursors.DictCursor) as cursor:
cursor.execute(query)
return cursor.fetchall()
finally:
connection.close()
Thank you in advance for any help.
I think you need to set the action on the <form> element, rather than <input> and you want to direct it to the same Flask endpoint (I assume?):
<form method="GET" action>
<div class="form-group">
<div class="col-sm-3">
<input type="text" placeholder="City Name" name="City_Name" class="form-control">
</div>
</div>
<div class="form-group">
<div class="col-sm-2">
<input type="submit" value="SEARCH" class="btn btn-primary btn-block">
</div>
</div>
</form>
Update your helper class a little to accept a city variable from your view function (you could tighten this up a bit more):
import pymysql
class DBHelper:
def table_inputs(self, city):
connection = self.connect()
PLN = "**%s**" % city
try:
query = "SELECT Plant_Geo, Plant_Code, Plant_Name, GMID, Material_Name, GROUP_CONCAT(DISTINCT Vendor_Name_GS ORDER BY Vendor_Name_GS) as VNGS, sum(2014_USD), sum(2015_USD), sum(2016_USD) FROM invoice_report WHERE plant_code like '%s' GROUP BY GMID ORDER BY sum(2015_USD) DESC";
with connection.cursor(pymysql.cursors.DictCursor) as cursor:
# actually better to pass parameters like this:
cursor.execute(query, (PLN,))
return cursor.fetchall()
except Exception as err:
# this may also help find errors generated above...
print(err)
finally:
connection.close()
Then, update your view function to test if city is submitted and submit it to your helper class:
#app.route('/table')
def table():
// the second argument is the default if "City_Name" is not submitted
city = request.args.get('City_Name', 'New York')
try:
tabledata = DB.table_inputs(city)
except Exception as e:
print(e)
tabledata = None
return render_template("table.html", tabledata=tabledata)
Related
So I'm having a bit of an issue here. I'm pretty new to Flask and web dev in general. I'm trying to add a new item in a database with SQLAlchemy and for some reason it's not working. This is my first practice project and it's extremely basic. You're likely going to find many issues; things that break best practice rules. I understand that, but please be indulgent. I'm just trying to get the very basics to work, then I'll improve the code.
There is the database model:
class TaskType(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False, unique=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
name = db.Column(db.Text(255))
color_code = db.Column(db.Text, default='NULL')
creation_timestamp = db.Column(db.DateTime, default=datetime.utcnow())
last_update_timestamp = db.Column(db.DateTime, default='NULL')
def __repr__(self):
return '<Task type %r>' % self.id
Here is the code for the app.route code:
#app.route('/options/tasks-types', methods=['POST', 'GET'])
def options_tasks_types():
global active_user_id
if active_user_id != 0:
if request.method == 'POST':
task_type_name = request.form['name']
task_type_color = request.form['color_code']
timestamp = datetime.now().replace(microsecond=0)
new_task_type = TaskType(user_id=active_user_id,
name=task_type_name,
color_code=task_type_color,
creation_timestamp=timestamp,
last_update_timestamp='NULL')
try:
db.session.add(new_task_type)
db.session.commit()
return redirect('/options/task-types')
except:
return "The task type could not be created."
else:
task_types = TaskType.query.filter(User.id == active_user_id).order_by(TaskType.name.desc()).all()
return render_template('options_tasks_types.html', task_types=task_types)
else:
return "You were disconnected."
Here is the HTML form:
{% extends 'base.html' %}
{% block head %}
<title>Task Manager</title>
{% endblock %}
{% block body %}
<div class="content" style="padding: 50px">
<h1>Task types</h1>
<table id="tasks_types">
<tr>
<th>Name</th>
<th>Color</th>
</tr>
{% for task_type in task_types %}
<tr>
<td>{{ task_type.name }}</td>
<td>
<div class="color-box" style="background-color: {{ task_type.color_code }} ;"></div>
</div>
</td>
<td>
Edit
<br>
Delete
</td>
</tr>
{% endfor %}
<tr>
<form action="{{ url_for('options_tasks_types') }}" method="POST">
<td>
<input type="text" name="name" id="name" placeholder="Task type name">
</td>
<td>
<input type="text" name="color_code" id="color_code" placeholder="Color code">
</td>
<td>
<input type="submit" value="Add new task type">
</td>
</form>
</tr>
</table>
</div>
{% endblock %}
For some reason I keep getting the Except message "The task could not be created". Unfortunately I can't really know why the exception is even happening. Is there a way to get a more detailed error message?
I've tried removing the try and except statements to just get the Flask error message, but it's not exactly detailed...
All I get is:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
I have a feeling that the issue is probably coming from my model and that an argument for one of the columns is preventing the TaskType item to be added to the database, but I can't figure out what it is.
Could someone help?
You are assigning a column in your table last_update_timestamp to 'NULL'. It only accepts DateTime objects.
From my understanding you are passing in 'NULL' it has quotes. So it is a string. Try passing in a DateTime object or null
I want to code a small message feed for my homepage. I want to send a text from a textarea to my Database
this is my html template:
{% extends 'layout.html'%}
{% block body%}
<form action="/feed" method="post">
<table>
<tr>
<td><textarea name="feedtext" id="feedtext" placeholder="Type message here!" rows="4" cols="53"></textarea></td>
</tr>
<tr>
<td><button style="width: 65%;" "submit"\>Send</button></td>
</tr>
</table>
</form>
{% endblock %}
now my problem is the python part. I try anything but I don't get the right code.
this is my python code now:
#app.route('/feed', methods=['POST'])
def feed():
try:
conn = mysql.connect()
cursor = conn.cursor()
feedtxt = request.form['feedtext']
cursor.execute('INSERT INTO nachrichten (Vorname, Nachname, Nachricht) VALUES(%);', (feedtxt))
finally:
cursor.close()
conn.close()
Welcome.
Can not make out "I try anything but I don't get the right code".
Anyway, out of the columns
(Vorname, Nachname, Nachricht)
it seems only the Nachricht gets the value of (feedtxt).
Try giving 3 items within VALUES
I've created a simple web page with a text field and a button. I want my application to update a record in my database with the content of the text field when I click the button. Seems simple enough, but I can't figure out what I'm missing. Here's my code thus far:
app.py sample
#app.route('/update-audit/', methods=['POST'])
def update_audit(test_name, description):
cur = connect_db()
cur.execute('UPDATE audit SET description = ? WHERE test_name = ?', (description, test_name,))
return render_template('clicked.html')
audit.html sample
<form action="{{ url_for('update_audit') }}" method="post">
<td>{{ row[2] }}</td>
<td>
<input type="text" id="desc" value="{{ row[3] }}" size="140">
<input type="hidden" name="update_audit" value="{{ row[2] }}, desc"/>
<input type="submit" class="btn btn-success" value="Update"/>
</td>
</form>
clicked.html
<!DOCTYPE html>
{% extends "layout.html" %}
{% block content %}
<body>
{{ form.description }}<br />
</body>
{% endblock %}
table sample
id | tool name | test name | description
========================================
1 | "tool1" | "test1" | "update me!"
Not sure if I'm missing an essential concept (I played around with flask_wtf and didn't get anywhere) or if I'm a step or two away from making this happen.
Set a name attribute for text input so that it gets sent with the submitted form.
<input name="description" type="text" id="desc" value="{{ row[3] }}" size="140">
Update your view function to get description from the POST dictionary attribute of request. test_name also needs to be updated to an appropriate value.
#app.route('/update-audit/', methods=['POST'])
def update_audit():
description = request.form.get('description')
test_name = request.form.get('update_audit')
cur = connect_db()
with cur:
cur.execute(
'UPDATE audit SET description = ? '
'WHERE test_name = ?;', (description, test_name,))
# commit changes to the database
return render_template('clicked.html')
Figured it out:
app.py sample
#app.route('/update-audit/', methods=['POST'])
def update_audit():
description = request.form.get('description')
test_name = request.form.get('test_name')
sql = 'UPDATE audit SET description=? WHERE test_name=?'
conn = sqlite3.connect(DATABASE)
cur = conn.cursor()
cur.execute(sql, (description, test_name))
conn.commit()
conn.close()
return render_template('clicked.html', data=(test_name, description))
audit.html sample
<form action="{{ url_for('update_audit') }}" method="POST">
<td>
<input type="hidden" name="test_name" value="{{ row[2] }}">{{ row[2] }}</input>
</td>
<td>
<input type="text" name="description" id="desc" value="{{ row[3] }}" size="100" maxlength="140"/>
<input type="submit" class="btn btn-success" value="Update"/>
</td>
</form>
</tr>
The answer is a combination of the right SQL-Alchemy commands, and ensuring that I'm sending the data to the update_audit function via two input-tags in audit.html.
Your render_template should get a form argument:
return render_template('clicked.html', form=form)
It is also not clear in the code you provided where the Forms are treated in python and where the variable row comes from.
I'm learning to use a database in flask at https://www.tutorialspoint.com/flask/flask_sqlalchemy.htm
and my problem is that data doesn't show up on html!
My code :
from flask import Flask, redirect, url_for, request, render_template, session, escape, flash
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///students.sqlite3'
db = SQLAlchemy(app)
class Students(db.Model):
id = db.Column(db.Integer, primary_key=True)
stu_num = db.Column('Student Number', db.Integer)
stu_name = db.Column('Name', db.String(100))
def __init__(self, number, name):
self.num = number
self.name = name
#app.route('/students/')
def show_students():
return render_template('show_students.html', students=Students.query.all())
#app.route('/addstudents/', methods=['POST', 'GET'])
def add_students():
if request.method == 'POST':
if not request.form['stu_num'] or not request.form['stu_name']:
flash('Please fill in all the fields')
else:
student = Students(request.form['stu_num'], request.form['stu_name'])
db.session.add(student)
db.session.commit()
flash('Record added')
return redirect(url_for('show_students'))
return render_template('add_students.html')
if __name__ == '__main__':
db.create_all()
app.run(debug = True)
show_students.html:
<!doctype>
<html>
<body>
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
<h2>Add Students</h2>
<table>
<tr>
<th>Id</th>
<th>Student Number</th>
<th>Name</th>
</tr>
{% for student in students %}
<tr>
<td>{{ student.id }}</td>
<td>{{ student.num }}</td>
<td>{{ student.name }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
add_students.html:
<!doctype>
<html>
<body>
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
<form action="http://localhost:5000/addstudents/" method="post">
<p>Student Number: <input type="text" name="stu_num"></p>
<p>Student Name: <input type="text" name="stu_name"></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
When I add data to the database, Id, which I set to auto-increment shows up, but the rest of data isn't shown.
You are using inconsistent variable names here
in init function, use
def __init__(self, number, name):
student.stu_num=number
student.stu_name=name
in show_students.html try using
<td>{{ student.stu_num }}</td>
<td>{{ student.stu_name }} </td>
SQLAlchemy provides an __init__ function for you which takes care of creating new objects, initializing fields, database, etc. By writing your own __init__, you are overriding SQLAlchemy's
__init__(), and preventing it from doing its thing!
So try getting rid of your __init__ function!
(Or if there's stuff you want to do in __init__, then make sure to call super().__init__ before or after doing whatever it is that you want to do.)
(But to start, just try getting rid of it...)
I'm using Flask and Jinja2 and I need to make an editable table with multiple rows.
This is what the table will look like:
And here's HTML for that:
<form action="/support/team-members-update" method="post">
<table>
<tbody><tr>
<th>Name</th>
<th>Id</th>
<th>Inbox Share</th>
</tr>
<tr>
<td>Ben</td><td>55555</td><td><input type="text" name="share_55555" value="0"></td></tr> <tr>
<td>Steve</td><td>66666</td><td><input type="text" name="share_66666" value="1"></td></tr> <tr>
<td>Harry</td><td>77777</td><td><input type="text" name="share_77777" value="1"></td></tr> <tr>
<td>Sally</td><td>88888</td><td><input type="text" name="share_88888" value="1"></td></tr></tbody></table>
<button type="submit">Send</button>
</form>
My current implementation is in Lua, where I'm hard coding a bunch of strings and connecting up the post data to native Lua types by hand (fun!). If I have to, I can process the form data by hand in Python as well, but I imagine there's probably a better solution out there.
I have explored WTForms a bit, but haven't had much luck getting it to work correctly.
I did find FieldList, but that seems to deal with a list of the same field, not multiple rows with the same exact fields.
I also found TableWidget, but the documentation is sparse and I can't figure out how to implement it to know if that would do what I'm looking to do.
FieldList will work, you need to make a list of a FormField. Specify your FormField like so:
class MemberForm(Form):
name = StringField('name')
member_id = StringField('member_id')
inbox_share = IntegerField('inbox_share')
# etc.
class TeamForm(Form):
title = StringField('title')
teammembers = FieldList(FormField(MemberForm))
Then you can create the forms from your database in a view function like so:
#app.route('/support/team-members-update', methods=['GET','POST'])
def update_team_members():
teamform = TeamForm()
teamform.title.data = "My Team" # change the field's data
for member in DB.get('teammembers') # some database function to get a list of team members
member_form = MemberForm()
member_form.name = member.name # These fields don't use 'data'
member_form.member_id = member.id
member_form.inbox_share = member.share
teamform.teammembers.append_entry(member_form)
return render_template('edit-team.html', teamform = teamform)
And then in the template, you can iterate over each item in teammembers as you create your table rows:
<html>
<head>
<title>Edit Team Members</title>
</head>
<body>
<h1>Edit Team</h1>
<div>
<form action="" method="post" name="teamform">
{{ teamform.hidden_tag() }}
Team Title: {{ teamform.title }}<br>
<div>
<table>
<tr>
<th> Name </th>
<th> ID </th>
<th> Inbox Share </th>
</tr>
{% for member in teamform.teammembers %}
<tr>
<td>{{ member.name }}</td>
<td>{{ member.member_id }}</td>
<td>{{ member.inbox_share }}</td>
</tr>
{% endfor %}
</table>
</div>
<p><input type="submit" name="edit" value="Send"></p>
</form>
</div>
</body>
</html>
I never was able to get WTForms to work quite how I wanted. I think it was a bit too heavy for my needs, so I ended up just using my own Jinja2 template to build the form and then used the formencode library to parse the post variables into a dict. This works well enough for me. (Thanks to this question for pointing me to the formencode library).
I'll give you a rough look at the various files I'm using and then explain the important parts at the bottom:
app.py:
from flask import Flask, render_template, request
from formencode import variabledecode
import pickledb
app = Flask(__name__)
DB = pickledb.load('data/data.db', False)
#app.route('/team-members', methods=['GET', 'POST'])
def team_members():
global DB
teammembers = DB.get('teammembers')
# teammembers looks like this, roughly:
# [{"id": 55555, "name": "Ben", "share": 0},
# {"id": 66666, "name": "Amy", "share": 1},
# {"id": 77777, "name": "Ted", "share": 1}]
if request.method == 'POST':
postvars = variabledecode.variable_decode(request.form, dict_char='_')
for k, v in postvars.iteritems():
member = [m for m in teammembers if m["id"] == int(k)][0]
member['share'] = v["share"]
DB.set('teammembers', teammembers)
DB.dump()
return render_template('team-members.html', teammembers=teammembers)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--debug', '-d', action='store_true')
parser.add_argument('--port', '-p', default=5000, type=int)
parser.add_argument('--host', default='0.0.0.0')
args = parser.parse_args()
app.run(args.host, args.port, debug=args.debug)
I have three template files, but you of course don't need this many. team-members.html has the code that's relevant to this problem.
_formhelpers.html:
{% macro render_input(id, fieldname, value) %}<input type="text" name="{{ id }}_{{ fieldname }}" value="{{ value }}" />{% endmacro %}
layout.html:
<!doctype html>
<html>
<head>
<title>Support Team Site</title>
</head>
<body>
<div class="page">
<h1>Support Team Site</h1>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
</body>
</html>
team-members.html:
{% from "_formhelpers.html" import render_input %}
{% extends "layout.html" %}
{% block body %}
<form action="/team-members" method="post">
<table>
<tr>
<th>Name</th>
<th>ID</th>
<th>Inbox Share</th>
</tr>
{% for member in teammembers %}
<tr>
<td>{{member['name']}}</td>
<td>{{member['id']}}</td>
<td>{{ render_input(member['id'], 'share', member['share']) }}</td>
</tr>
{% endfor %}
</table>
<button type="submit">Send</button>
</form>
{% endblock %}
This will render the following HTML:
<!doctype html>
<html>
<head>
<title>Support Team Site</title>
</head>
<body>
<div class="page">
<h1>Support Team Site</h1>
<form action="/team-members" method="post">
<table>
<tr>
<th>Name</th>
<th>ID</th>
<th>Inbox Share</th>
</tr>
<tr>
<td>Ben</td>
<td>55555</td>
<td><input type="text" name="55555_share" value="0" /></td>
</tr>
<tr>
<td>Amy</td>
<td>66666</td>
<td><input type="text" name="66666_share" value="1" /></td>
</tr>
<tr>
<td>Ted</td>
<td>77777</td>
<td><input type="text" name="77777_share" value="1" /></td>
</tr>
</table>
<button type="submit">Send</button>
</form>
</div>
</body>
</html>
It's worth mentioning what's going on in the if request.method == 'POST': part of app.py. The request.form variable will be of type ImmutableMultiDict, which would look kind of like this when printed out:
ImmutableMultiDict([('55555_share', u'0'), ('66666_share', u'1'), ('77777_share', u'1')])
This is somewhat useful, but we'd still have to parse this by hand to do anything with it. Note the format of the keys there, in the id_fieldname format (e.g. 55555_share). This was thanks to the render_input macro we put in our _formhelpers.html template file. When we process the post form input, we use variabledecode.variable_decode(request.form, dict_char='_'), which parses the form data and turns it into a dictionary based on the naming convention we used for the name values of the form inputs. Here's what it looks like:
{
"55555": {
"share": "0"
},
"66666": {
"share": "1"
},
"77777": {
"share": "1"
}
}
This makes it easy to map back to our original data and update it.