Flask Sqlalchemy data from sqlite database not shown - python

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...)

Related

Flask redirect is not working, no error, and db isn't updated

When submitting my form, the page is not redirected. There are no errors; some information is added to the url, and that's it. The database is not updated afterwards.
`from regist import app
from flask import render_template, redirect, url_for
from regist.models import Applicant
from regist.forms import RegisterForm
from regist import db
#app.route('/')
def index():
return render_template('index.html')
#app.route('/redirected')
def redirected():
return render_template('page.html')
#app.route('/join', methods=['GET','POST'])
def join():
form=RegisterForm()
if form.validate_on_submit():
applicant_to_create=Applicant(name=form.name.data,
surname=form.surname.data,
gender=form.gender.data,
dateOfBirth=form.dateOfBirth.data,
address=form.address.data,
qualifications=form.qualifications.data,
email=form.email.data,
phoneNumber=form.phoneNumber.data)
db.session.add(applicant_to_create)
db.session.commit()
return redirect(url_for('redirected'))
return render_template('join.html',form=form) `
The initial url is: "http://127.0.0.1:5000/join"
After adding some dummy data, it becomes: "http://127.0.0.1:5000/join?search=+%28ru%28rzshrhf&csrf_token=ImIwZWIwODcyNWQ4MWNkYjNjYWJkNjE5NTNkY2MwZWE5Yzk4MjNkOTci.YR4veQ.eyoXQWv4kRShFiZMEbPXoWzsbL0&name=dhss&surname=sdh&gender=M&dateOfBirth=sdthdh&address=dshtes&qualifications=dshdh&email=dhr&phoneNumber=dshd&submit=submit"
I intentionally set the date field to accept text.
here is the form html code:
{%extends 'layout.html' %}
{%block head%}
<title>form</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/formstyle.css') }}">
{%endblock%}
{%block body%}
<body>
<h2>Join Us</h2>
<span id="requiredFieldMessage">fields marked (*) are required</span>
<form method="POST">
<!--{{ form.hidden_tag() }}-->
<table id="formTable">
<tr><td class="labelCell">{{ form.name.label()}}</td> <td class="labelCell">{{ form.surname.label()}}</td></tr>
<tr><td>{{ form.name(class="textbox") }}</td><td>{{ form.surname(class="textbox") }}</td></tr>
<tr><td class="labelCell">{{ form.gender.label()}}</td> <td class="labelCell">{{ form.dateOfBirth.label()}}</td></tr>
<tr><td>{{ form.gender() }}</td> <td>{{ form.dateOfBirth(class="textbox", placeholder="dd-mm-yyyy") }}</td></tr>
<tr><td class="labelCell">{{ form.address.label()}}</td> <td class="labelCell">{{ form.qualifications.label()}}</td></tr>
<tr><td>{{ form.address(class="textbox", rows="5", cols="40", placeholder="Enter four or five line address") }}</td> <td>{{ form.qualifications(class="textbox", rows="5", cols="40", placeholder="enter at least one certificate") }}</td></tr>
<tr><td class="labelCell">{{ form.email.label()}}</td> <td class="labelCell">{{ form.phoneNumber.label()}}</td></tr>
<tr><td>{{ form.email(class="textbox") }}</td> <td>{{ form.phoneNumber(class="textbox") }}</td></tr>
<tr colspan="2"><td><br>{{ form.submit(id="submitButton") }}<br></td></tr>
</table>
</form>
{%endblock%}
Assuming that you are using Flask-WTF.
Look at what the doc says about validate_on_submit:
Note that you don’t have to pass request.form to Flask-WTF; it will
load automatically. And the convenient validate_on_submit will check
if it is a POST request and if it is valid.
Source
(emphasis is mine)
Your form should be submitted as POST, not GET. GET forms are rare (mostly search forms because the keywords are propagated in the URL, thus the queries can be shared or bookmarked)
What seems to happen here is that the condition if form.validate_on_submit(): is not true. Once you've fixed your form to use POST, you should display an error message to the user if that condition fails, and ask them to correct their input then, rather than do a silent redirect. It is very irritating to have forms that don't perform validation like they should, and don't communicate clear feedback when errors occur...
All you need to do is add an else clause to your if and display the error messages that should already be available from Flask WTF. When you build a form, test it and make sure it does not accept garbage.

Multiple Instances of a form are unified on submit

I have a form, which has to have some fields multiple times. Thats why I extracted all fields, that should appear multiple times, into the class AdvandcedGame. All fields that are unique are in the Form class. The Form class is also the only one with a submit-button. When I press submit, all instances of AdvancedGame(A,B,C) are unified(A,A,A). Can you tell me why and how could I fix this?
Here is the code as simplified as possible
app.py
from flask import Flask, render_template
from flask_restful import Resource, Api
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
class AdvandcedGame(FlaskForm):
name = StringField('name')
class Ui(FlaskForm):
submit = SubmitField('submit')
app = Flask(__name__)
api = Api(app)
app.config['JSON_SORT_KEYS'] = False
app.config['SECRET_KEY'] = "303"
#app.route('/', methods=['GET', 'POST'])
def root():
form = Ui()
games = [AdvandcedGame(name='A'), AdvandcedGame(name='B'), AdvandcedGame(name='C')]
for game in games:
app.logger.info(game.name.data)
# outputs
# A
# B
# C
if form.validate_on_submit():
for game in games:
app.logger.info(game.name.data)
# outputs
# A
# A
# A
return render_template('index.html', form=form, games=games)
# Run the application
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
templates/index.html
<html>
<head>
</head>
<body>
{% if form is defined %}
<form method="POST" action="{{ form.url }}">
{{ form.csrf_token }}
<table>
<tr>
<td>
{{ games[0].name.label }}
</td>
{% for game in games %}
<td>
{{ game.name }}
</td>
{% endfor %}
</tr>
<tr>
<td colspan=3>
{{ form.submit}}
</td>
</tr>
</table>
</form>
{% endif %}
</body>
</html>

Can't add an item in a SQL database using SQLAlchemy for a Flask web app with Python. How do I proceed to get more details as to why it's not working?

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

flask-wtf same form on same page using for loop

I'm brand new to programming. I came up with a project to help me learn and I'm stuck already. I'm using Flask, Flask-SQLAlchemy and Flask-wtf.
I'm trying to create a club attendance system that lists members and checks them off if they are present and logs the amount they paid (either $15 for 1 lesson, or $25 for the week). I have a table that I populate from my database that looks like this:
I want to click on submit to mark the person as present but this ticks the checkbox for everyone in the list and sets the amount paid to the same for everyone.
I have tried lots of things. I have seen similar issues here and people suggesting using FieldList and FormField - I tried this with no luck. Here is my Form code:
class MemberForm(Form):
form_id = HiddenField()
member_id = DecimalField('id')
member_name = StringField('name')
attend_date = StringField('date', default=todays_date())
is_here = BooleanField('here')
has_paid = SelectField('Amount', choices=[(15, '15'), (25, '25')])
submit = SubmitField("Submit")
def __init__(self, *args, **kwargs):
super(MemberForm, self).__init__(*args, **kwargs)
read_only(self.member_name)
My controller code:
#app.route('/', methods=['GET', 'POST'])
def home():
members = Member.query.order_by(Member.name).all()
form = MemberForm()
if request.method == 'POST': # TODO form validation and database stuff
print('got this far')
print(form.data)
return render_template('index.html', title='Tong Long',
today=todays_date(), members=members,
form=form)
and the jinja2 template part:
<table width="483" border="1">
<tbody>
<tr>
<th width="271"><strong>Member</strong></th>
<th width="152"><strong>Grade</strong></th>
<th><strong>Last Seen</strong></th>
<th width="38"><strong>Paid?</strong></th>
<th><strong>Is Here?</strong></th>
<th>Submit</th>
</tr>
{% for member in members %}
<form action="" method="post" name="{{ member.id }}">
<tr>
<td>{{form.member_name(value=member.name)}}</td>
{% for g in member.grade %}
<td>{{ g.grade }}</td>
{% endfor %}
<td>{{ form.attend_date }}</td>
<td>{{ form.has_paid }}</td>
<td>{{form.is_here}}</td>
<td>
{{ form.submit }}
</td>
</tr>
</form>
{% endfor %}
</tbody>
</table>
Viewing the rendered HTML I can see that all the fields have the same id.
I'm starting to think this can't be done with WTForms. Will I need to use javascript perhaps (something I know nothing about). Or manually create the forms rather than using WTF? Any help appreciated!
This is very late, but perhaps it is helpful to somebody.
What calabash is doing, is create one single form and then display it multiple times in the template.
However, to achieve the desired outcome (independend forms with independend submit buttons), multiple forms need to be created within the route function. They can be passed as a list to the template and then looped over. (A simpler solution would be one form with one submit button and dynamically created "lines" for each member. See FieldList...)
Logic:
def home():
members = Member.query.order_by(Member.name).all()
forms = []
for member in members:
form = MemberForm(prefix=member.name)
form.member_name.data = member.name
forms.append(form)
# validation:
for form in forms:
if form.submit.data and form.validate_on_submit():
# do_something here for each form, e.g. write to database
return render_template('index.html', title='Tong Long',
today=todays_date(),
forms=forms,
members=members)
The different forms need to have individual prefixes. They need to be validated individually and it needs to be checked which submit-button was used.
Note: It is perhaps not a good idea to use a form field for the name, as that information is already known from the members database entry and it might not be intended to change it here. A simple text label would make more sense in that case.
The table rows in the template could look like this:
{% for form in forms %}
<form action="" method="post">
{{ form.hidden_tag() }}
<tr>
<td>{{ form.member_name }}</td>
<td>{{ members[loop.index0].grade }}</td>
<td>{{ form.attend_date }}</td>
<td>{{ form.has_paid }}</td>
<td>{{ form.is_here }}</td>
<td>{{ form.submit }}</td>
</tr>
</form>
{% endfor %}

Render an editable table using Flask, Jinja2 templates, then process the form data returned

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.

Categories