I am building a simple web-app which uses existing MySQL DB. It is my first time using Flask and I have been struggling to understand what I am doing wrong for the past couple of hours.
My simple project structure:
/root
/app
__init__.py
db.py
forms.py
views.py
/templates
base.html
index.html
login.html
config.py
run.py
I am trying to query my MySQL DB and fill the template with the result from the query.
My db.py:
from app import app
from flaskext.mysql import MySQL
class DB(object):
mysql = MySQL()
def __init__(self):
app.config['MYSQL_DATABASE_USER'] = 'loguser'
app.config['MYSQL_DATABASE_PASSWORD'] = 'asdzxc'
app.config['MYSQL_DATABASE_DB'] = 'log'
app.config['MYSQL_DATABASE_HOST'] = '127.0.0.1'
app.config['MYSQL_DATABASE_PORT'] = 33006
self.mysql.init_app(app)
def query_db(self):
cursor = self.mysql.connect().cursor()
cursor.execute("SELECT name from users limit 1")
data = cursor.fetchone()
if data is None:
return "No results from query"
else:
return data
And in my views.py I have the following:
from flask import render_template, flash, redirect
from app import app
from .forms import LoginForm
from .db import DB
#app.route('/')
#app.route('/index')
def index():
db = DB()
user = db.query_db()
print(user) (it prints it here so the db connection works)
posts = [ # fake array of posts
{
'author': {'nickname': 'John'},
'body': 'Beautiful day in Portland!'
},
{
'author': {'nickname': 'Susan'},
'body': 'The Avengers movie was so cool!'
}
]
return render_template("index.html",
title='Home',
user=user,
posts=posts)
I get "AssertionError" when I try to assign "user" to the user from the template:
AssertionError: A setup function was called after the first request was handled. This usually indicates a bug in the application where a module was not imported and decorators or other functionality was called too late.To fix this make sure to import all your view modules, database models and everything related at a central place before the application starts serving requests.
I believe I am violating a major principle of the framework. What is the correct way to pass the data to the template?
It is better to use Flask-SQLAlchemy and MySQL-python 1.2 , Successful code and documentation is available on the link below.
http://techarena51.com/index.php/flask-sqlalchemy-tutorial/
From my experience I found that MySQl support is not that good for python 3 atleast, it is better to use PostgreSQL, but that's just my personal opinion.
It's very late to give answer, but it may help someone.
You've to connect to MySQL before adding any route source to an API.
It should be in the order of
# 1. MySQL setup should be done at first
app = Flask(__name__)
api = Api(app)
mysql = MySQL()
app.config['MYSQL_DATABASE_USER'] = 'root'
app.config['MYSQL_DATABASE_PASSWORD'] = 'root'
app.config['MYSQL_DATABASE_DB'] = 'DataBase'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'
mysql.init_app(app)
conn = mysql.connect()
cursor = conn.cursor()
# 2. Create API resource after that
api.add_resource(CreateUser, '/CreateUser')
Related
In development (so sqlite3) I'm getting this error on any database access:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: ujs ...
I got here by saying
export FLASK_ENV=development
export FLASK_APP=my_app.py
flask db init
flask db migrate
flask db upgrade
flask run
and then doing an HTTP GET against that dev server.
I believe the migration workflow succeeded, because when I use the sqlite3 commandline client, I can see the (empty) table with a believably correct schema.
╭╴ (get-db-working *%=)╶╮
╰ jeff#starshine:TN_flask_web $ sqlite3 dev.db
SQLite version 3.27.2 2019-02-25 16:06:06
Enter ".help" for usage hints.
sqlite> .table
alembic_version ujs
sqlite> .quit
╭╴ (get-db-working *%=)╶╮
╰ jeff#starshine:TN_flask_web $
I therefore believe I've made a coding error. But I'm not seeing it.
I have this code (pared down to what I believe is the essential bits):
my_app.py:
from app import create_app, db, cli
from app.models import UJS
app = create_app()
cli.register(app)
#app.shell_context_processor
def make_shell_context():
return {'db': db,
'UJS': UJS}
app/models.py:
from app import db
import time
def now_in_microseconds():
"""Return the current time in microseconds since the epoch.
"""
return time.time() * 1000 * 1000
class UJS(db.Model):
id = db.Column(db.Integer, primary_key=True)
timestamp_microseconds = db.Column(db.BigInteger, default=now_in_microseconds)
ip_hash = db.column(db.String(40))
# And then some more columns, all quite boring.
def __repr__(self):
return '<[{tag}]/[{ip}] {microsec}/{city}>'.format(
tag=self.tag, ip=self.ip_hash,
microsec=self.timestamp_microseconds, city=self.city)
app/__init__.py:
from flask import Flask, request, current_app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
try:
app.config.from_pyfile("../config_local.py")
except FileNotFoundError:
print('No local config found.')
except:
print('Unexpected error on app.config.from_pyfile()')
db.init_app(app)
migrate.init_app(app, db)
...
return app
from app import models
and app/main/routes.py:
from flask import request, g, current_app, session
from app import db
from app.main import bp
from app.models import UJS
#bp.before_app_request
def before_request():
if 'static' == request.endpoint:
# This should only happen in dev. Otherwise, nginx handles static routes directly.
return
# I expect this to return an empty list, but it throws a 500.
print(UJS.query.all())
Any suggestions what I'm missing?
For anyone who might find this question later on: the problem was about having the right absolute path to your DB in your SQLALCHEMY_DATABASE_URI config value.
Also (this wasnt the case here, but it might possibly gotcha with the same symptoms) - if you omit __tablename__ on Model declaration, SQLAlchemy might autogenerate something you wont expect. Just a thing to keep in mind, if you're working with an existing DB with some schema already in place.
I have a app Flask. I want to use Rest api for insert data to Database. I have a app.pyfile and api.py file. I want to write a post method in api.py. I want to get information from client using add.html file and post it to api. then api this information add to Database. I use pyodbc for connect to SQL Server Database.in this Database, there is a table tbl_product with columns P_ID,title,count,price and active. I don't know, how do it. When I run python api.py on http://localhost:5000/add, I see { "message": "The method is not allowed for the requested URL."}. Also, When I run python app.py, on http://localhost:8080/addI see Method Not Allowed. The method is not allowed for the requested URL .
Can you help me?
my app.py file is:
from flask import Flask,render_template,url_for, request, redirect, flash, jsonify,json
import pyodbc
import requests
from api import ProductAdd
app = Flask(__name__)
app.secret_key= "flash_message"
# creating connection Object which will contain SQL Server Connection
conn = pyodbc.connect('Driver={SQL Server};'
'Server=TABRIZIYAN;'
'Database=market_DB;'
'Trusted_Connection=yes;')
cursor = conn.cursor()
#app.route('/add')
def add():
if request.method =='POST':
productDetails= request.form
title= productDetails['title']
count=productDetails['count']
price= productDetails['price']
active= productDetails['active']
create_row_data = { 'title':str(title), 'count':str(count),
'price':str(price), 'active':str(active) }
info = requests.post('http://localhost:5000/add', data= create_row_data)
return info.text #return(render_template('product.html'))
else:
return (render_template('add.html'))
if __name__=='__main__':
app.run(debug=True, port="8080")
my api.py file is:
from flask import Flask, request, render_template,url_for, redirect, flash
from flask_restplus import Api, Resource
from flask import jsonify
import pyodbc
import requests
flask_app = Flask(__name__)
api = Api(app=flask_app)
class ProductAdd(Resource):
def post(self):
productDetails= request.json
title= productDetails['title']
count=productDetails['count']
price= productDetails['price']
active= productDetails['active']
cursor = conn.cursor()
cursor.execute(" INSERT INTO Tbl_product(title, count, price, active) VALUES(?, ?, ?, ?)
",(title,count,price,active))
conn.commit()
resp = jsonify('User added successfully!')
resp.status_code = 200
return resp
api.add_resource(ProductAdd , '/add')
if __name__ == '__main__':
flask_app.run(debug=True)
I am learning to use flask and flask_mongoengine to create a website. Follow the flask tutorial 1.0.2 version. But I ran into a problem, how to implement the get_db() and close_db() function?
Currently, what I am doing is :
myapp.py
....
def create_app():
app = Flask(__name__)
from db import db
db.init_app(app)
#app.route('/')
def home():
...
return app
db.py
from flask import g
from flask_mongoengine import MongoEngine
db = MongoEngine()
def get_db():
g.db = ???
return g.db
def close_db():
db = g.pop('db', None)
if db is not None:
??? # db.close() doesn't exist!!!
I am very confused about how to do this part. Can someone give any suggestions? In flask_mongoengine tutorial page, they don't implement the get_db() and close_db() ...
Confusion happens because in those tutorials there are too many programming patterns. In flask-1.0.2 tutorial they use getter method pattern and but flask-mongoengine relies on bootstraping a db to flask-app-instance, which relies on a builder pattern — Flask Application Factories. It may still be confusing but I'll show you how it's meant to be done.
Bootstrap a flask-mongoengine in create_app:
def create_app(test_config=None):
app = Flask(__name__)
# configure mongo settings here like in flask-mongoengine docs
g.db = db = MongoEngine()
db.init_app(app)
def get_db():
return g.db
def close_db():
pass
What's about close_db()? Well, that function exists in case db you've chosen needs some cleanup actions to be closed. But I haven't found in mongoengine docs any explicit mention that mongoengine db connection need a cleanup actions from you to be closed, so you can just pass it.
I have a Flask website with a MySQL backend. I have a table called, users. It has two columns: username and name and one record:
name username
Jim testuser123
When a user clicks the button on the website, it updates the record to set the name to Bob then print all records where name = 'Bob'. Yet, it returns no results. If I refresh the connection before re-querying, then it does return one result as it should. Does the mdb.connect object cache data? How could it not be returning the correct results?
init.py:
import pandas as pd
import MySQLdb as mdb
from flask import Flask, render_template, request
def sql_con():
return mdb.connect(host='myhost', port=3306, user='root', passwd='root', db='db', use_unicode=True, charset="utf8")
app = Flask(__name__)
def update_record():
con = sql_con()
cur = con.cursor()
sql_string= "Update users set name = 'Bob' where username = 'testuser123'"
cur.execute(sql_string)
con.commit()
#app.route('/', methods=['GET', 'POST'])
def myroute():
con = sql_con()
if request.method == 'POST':
update_record()
print pd.read_sql("select * from users where name = 'Bob'", con=con)
return render_template('1.html')
app.run( debug=True, port=5050)
1.html
<html>
<body>
<form method="POST">
<button id="mybutton" name='btn' value="mybutton">Submit Data</button>
</form>
</body>
For this code to print one result, I must add con=sql_con() right after I call the update(), but before the print statement. Why is that?
In general it is a good practice to use an ORM binding (i.e. Falsk-SQLAlchemy) with web frameworks (manages connection pools, automates commit/rollback, ...) even if an ORM seems overkill for a simple application.
Otherwise, avoid using multiple connections to the same database in the same request if you prefer manage this at low level (database connections).
Try this instead:
import pandas as pd
import MySQLdb as mdb
from flask import Flask, render_template, request
def sql_con():
return mdb.connect(host='myhost', port=3306, user='root', passwd='root', db='db', use_unicode=True, charset="utf8")
app = Flask(__name__)
def update_record(con):
cur = con.cursor()
sql_string= "Update users set name = 'Bob' where username = 'testuser123'"
cur.execute(sql_string)
con.commit()
#app.route('/', methods=['GET', 'POST'])
def myroute():
con = sql_con()
if request.method == 'POST':
update_record(con)
print pd.read_sql("select * from users where name = 'Bob'", con=con)
return render_template('1.html')
app.run( debug=True, port=5050)
If you want to scale a real app based on such solution, you should consider pulling an opened connection from a global connections pool. Creating a new db connection (at each HTTP request) may be time expensive.
So I am using Amazon Web Services RDS to run a MySQL server and using Python's Flask framework to run the application server and Flask-SQLAlchemy to interface with the RDS.
My app config.py
SQLALCHEMY_DATABASE_URI = '<RDS Host>'
SQLALCHEMY_POOL_RECYCLE = 60
My __ init __.py
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
application = Flask(__name__)
application.config.from_object('config')
db = SQLAlchemy(application)
I have my main application.py
from flask import Flask
from application import db
import flask.ext.restless
from application.models import Person
application = Flask(__name__)
application.debug=True
db.init_app(application)
#application.route('/')
def index():
return "Hello, World!"
manager = flask.ext.restless.APIManager(application, flask_sqlalchemy_db=db)
manager.create_api(Person, methods=['GET','POST', 'DELETE'])
if __name__ == '__main__':
application.run(host='0.0.0.0')
The models.py
class Person(db.Model):
__bind_key__= 'people'
id = db.Column(db.Integer, primary_key=True)
firstName = db.Column(db.String(80))
lastName = db.Column(db.String(80))
email = db.Column(db.String(80))
def __init__(self, firstName=None, lastName=None, email=None):
self.firstName = firstName
self.lastName = lastName
self.email = email
I then have a script to populate the database for testing purposes after db creation and app start:
from application import db
from application.models import Person
person = Person('Bob', 'Jones', 'bob#website.net')
db.session.add(person)
db.session.commit()
Once I've reset the database with db.drop_all() and db.create_all() I start the application.py and then the script to populate the database.
The server will respond with correct JSON but if I come back and check it hours later, I get the error that I need to rollback or sometimes the 2006 error that the MySQL server has gone away.
People suggested that I change timeout settings on the MySQL server but that hasn't fixed anything. Here are my settings:
innodb_lock_wait_timeout = 3000
max_allowed_packet = 65536
net_write_timeout = 300
wait_timeout = 300
Then when I look at the RDS monitor, it shows the MySQL server kept the connection open for quite a while until the timeout. Now correct me if I'm wrong but isn't the connection supposed to be closed after it's finished? It seems that the application server keeps making sure that the database connection exists and then when the MySQL server times out, Flask/Flask-SQLAlchemy throws an error and brings down the app server with it.
Any suggestions are appreciated, thanks!
I think what did it was adding
db.init_app(application)
in application.py, haven't had the error since.
Everytime checking rollback or not is troublesome..
I made insert, update functions which need commit.
#app.teardown_request
def session_clear(exception=None):
Session.remove()
if exception and Session.is_active:
Session.rollback()
It seems not to be a problem with the transactions at the first place, but this is probably caused by an MySQL Error like Connection reset by peer beforehand. That means your connection is lost, probably because your application context was not setup correctly.
In general it is preferrable to use the factory pattern to create your app. This has a lot of advantages, your code is
easier to read and setup
easier to test
avoid circular imports
To prevent the invalid transaction error (that is probably caused by an OperationalError: Connection reset by peer) you should ensure that you are handling the database connection right.
The following example is based on this article which gives a nice explanation of the flask application context and how to use it with database connections or any other extensions.
application.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
def create_app():
"""Construct the core application."""
application = Flask(__name__)
application.config.from_object('config') # Set globals
db = SQLAlchemy()
with application.app_context():
# Initialize globals/extensions in app context
db.init_app(app)
# import routes here
from . import routes
return application
if __name__ == "__main__":
app = create_app()
app.run(host="0.0.0.0")
routes.py
from flask import current_app as application
#application.route('/', methods=['GET'])
def index():
return "Hello, World!"
If you still run into disconnect-problems you should also check the SQLAlchemy documentation on dealing with disconnects and have a look at this question.
Here you missing pool recycle as MySql closes session after some time so you need to add pool recycle so that connections in pool get reconnect after pool recycle time.
app.config['SQLALCHEMY_POOL_RECYCLE'] = 3600
This error usually appears when you create sqlalchemy the engine as a singleton. In that case after the connection is invalidated (in my case it was 3600sec) you get the InvalidTransaction error.
Best advice would be to initialise the db session at the time of application initialisation
db.init_app(app)
and import this db session when ever you have to do some CRUD operation.
Never faced this issue post this change on my application.
Alternatively, use this at the end of the script that populates your database:
db.session.close()
That should prevent those annoying "MySQL server has gone away" errors.