Integrate flask-sqlalchemy into project - flask

I am new comer of flask api. I am doing a flask restful api project which purpose is to search stores by store name that is kept in existing database online.
Now is is connect to database using psycopg2.
I would like to improve it by using flask-sqlalchemy. I have searched on the web and the solution seems to use Automap, that I learned from the tutorial. The problem I am facing now is how to integrate those codes into my existing project, like where should I put the settings like automap_base() into my existing codes. Here is my current file structure:
app.py
∟ models
∟ family_mart.py
∟ resources
∟ family_mart.py
app.py:
from flask import Flask
from flask_restful import Api
from resources.family_mart import FamilyMart
app = Flask(__name__)
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
app.run(port=5000, debug=True)
family_mart.py in models folder:
import psycopg2
from config import config
class FamilyMartModel:
#classmethod
def find_by_name(cls, name):
conn = None
try:
params = config()
conn = psycopg2.connect(**params)
cur = conn.cursor()
query = f"""
SELECT extract_date
, store_name
, address
FROM cnvnt_str_fm
WHERE store_name = '{name}'
"""
cur.execute(query)
rows = cur.fetchall()
cur.close()
if rows:
result = {'store':[]}
for row in rows:
append_dict = {'extract_date':row[0].strftime('%Y-%m-%d'),
'store_name':row[1],
'address':row[2]}
result['store'].append(append_dict)
return result
except (Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if conn is not None:
conn.close()
print('Database connection closed.')
family_mart.py in resources folder:
from flask_restful import Resource
from models.family_mart import FamilyMartModel
class FamilyMart(Resource):
def get(self, name):
item = FamilyMartModel.find_by_name(name)
if item:
return item
return {'message':'Store not found.'}, 404

After study the documentation, I restructured my project and integrate with sqlalchemy successfully. I removed model folder and added db.py to configure the existing database, here is the detail :
New file structure:
app.py
∟ resources
∟ family_mart.py
db.py
app.py:
from flask import Flask
from flask_restful import Api
from config import config
from db import db
from resources.family_mart import FamilyMart
params = config()
DB_FULL_URL = f"postgresql+psycopg2://{params['user']}:{params['password']}#{params['host']}/{params['database']}"
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_FULL_URL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
db.init_app(app)
app.run(port=5000, debug=True)
db.py:
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer, String, DateTime
db = SQLAlchemy()
metadata_obj = MetaData()
fm_table = Table('cnvnt_str_fm', metadata_obj,
Column('extract_date', String),
Column('store_name', String),
Column('address', String)
)
family_mart.py:
from flask_restful import Resource
from db import db
from db import fm_table
class FamilyMart(Resource):
def get(self, name):
results = db.session.query(fm_table).filter_by(store_name=name).\
order_by(fm_table.c.extract_date.desc()).limit(3)
output = {'store':[]}
for row in results:
extract_date = str(row[0])
store_name = row[1]
address = row[2]
output['store'].append({'extract_date':extract_date,
'store_name':store_name,
'address':address})
return output

Related

Flask Import errors when importing models on pythoneverywhere

Local everything works fine but when I go to pythoneverywhere I get error: Cannot import name "User" from "Data.Domain.User", without Flask-Migrate db works just fine, also, I tried to copy the content of Data.Domain.User module to app.py and it works, the problem appears just when I want to move it on another folder ( location ).
This is what I have in app.py:
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager, Server
from flask_migrate import Migrate, MigrateCommand
from flask_login import LoginManager
from flask_mail import Mail, Message
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blablablastrongsecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'connstring'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
login_manager = LoginManager(app)
from Data.Domain.User import User
from Presentation import App
if __name__ == '__main__':
manager.run()
Module Data.Domain.User is this:
from app import db, login_manager
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
uuid = db.Column(db.String(50), unique=True)
username = db.Column(db.String(40), unique=True)
def __repr__(self):
return "User(\n\tID: " + str(self.uuid) + "\n\tUsername: " + self.username + "\n)"
I solved the problem renaming and moving the app.py file in another folder, and then I just imported app from it
from Presentation.Server import app
This is the import from Data.Domain.User file

How do I import a database made with SQLAlchemy to Flask app?

new to web developing. I have three files. database_setup.py with my table classes. restaurant.py used to populate tables and script.py for flask app
database_setup.py
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Restaurant(Base):
__tablename__ = 'restaurant'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.create_all(engine)
restaurant.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database_setup import Restaurant, Base, MenuItem
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
restaurant = Restaurant(name= "Kasra")
session.add(restaurant)
session.commit()
script.py
from flask import Flask
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,scoped_session
from database_setup import Base, Restaurant
app = Flask(__name__)
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
DBSession = scoped_session(sessionmaker(bind=engine))
session = DBSession()
#app.route('/')
#app.route('/hello')
def HelloWorld():
restaurant = session.query(Restaurant).first()
return restaurant.name
if __name__ == "__main__":
app.run(debug=True)
What I want to do is import the database that was created in restarant.py to script.py. When I run the app it creates a new and empty database because I'm using "engine = "create_engine()" in script.py and I need to use it in order to bind with engine and use session.
The error I get when running app "AttributeError: 'NoneType' object has no attribute 'name'"
Here is what I do to operate with Flask and SQLAlchemy:
In a database.py file:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base
engine = create_engine(Config.SQLALCHEMY_DATABASE_URI)
session_factory = sessionmaker(bind=engine)
def init_db():
Base.metadata.create_all(bind=engine)
All model classes are located in a models.py file, and are also derived from the declarative base:
Base = declarative_base()
Then the start-up python script contains (key parts only):
app = Flask(__name__)
session = flask_scoped_session(session_factory, app)
if __name__ == "__main__":
database.init_db()
app.run()

Define models in seperate file when using app factory flask

I am creating a flask application where I am using the app factory method. I have a file in the application folder __init__.py which has the create_app function with the following code
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:#localhost/database'
db = SQLAlchemy(app)
if test_config == None:
app.config.from_pyfile('config.py',silent=True)
else:
app.config.form_mapping(test_config)
from flaskr import models
try:
os.makedirs(app.instance_path)
except OSError:
pass
class User(db.Model):
id = db.Column(db.Integer,primary_key=True)
uname = db.Column(db.String(50))
#app.route('/hello')
def hello():
return json.dumps({'message':'hello','status':True})
#app.route('/getusers')
def getusers():
u = User.query.get(1)
return json.dumps({'uname':u.uname})
return app
What I want is to define the models in a seperate file. How can I do this?
I have tried defining in a seperate file and importing it. But the problem is the model inherits the db.Model which is then not available in the imported file.
Leave the creation of db object outside create_app without passing any app instance and use the SQLAlchemy.init_app method to configure and init. your db object, this way you can import it from any file:
db = SQLAlchemy()
#...
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
#...
db.init_app(app)
More on this topic can be found at flask's documentation
I've fought with this problem a few hours too. Couldn't fix it until I came to the realization, that I have to return the app inside the app.app_context()
model.py:
from flask_user import UserMixin
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255, collation='NOCASE'), nullable=False,
unique=True)
# ...
__init__.py:
from flask import Flask
from flask_migrate import Migrate
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile('config.py', silent=True)
migrate = Migrate()
from appname.model import db
db.init_app(app)
migrate.init_app(app, db)
with app.app_context():
# Create all database tables
db.create_all()
import appname.routes
# Apply the blueprints to the app
from appname import bp1, bp2
appname.register_blueprint(bp1.bp)
appname.register_blueprint(bp2.bp)
return app
Run with:
cd ~/Folder/Webapp
. appname/bin/activate
export FLASK_APP=appname
export FLASK_ENV=development
flask run
(in Linux terminal)
or create this file and run it in python shell:
from appname import create_app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)

Flask Manager Command #manager.command doesn't work

I have a problem here.
I want to populate a table in the database with a record when the migration is executed, but actually it doesn't work. I'm using SQLAlchemy for the DB and the Manager object to create the DB.
In the DB I have a table called 'skills' which contains objects of type 'Skill'.
I just want to fill the table with a record during the initialization phase.
Please help me in finding the solution.
manage.py
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import Skill
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db, Skill=Skill)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
#manager.command
def create_skill():
db.session.add(Skill(name='ciaociao', in_use=False))
db.session.commit()
if __name__ == '__main__':
manager.run()
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
login_manager.init_app(app)
return app
models.py
class Skill(db.Model):
__tablename__ = 'skills'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True)
in_use = db.Column(db.Boolean, default=False)

how to use db instance in flask-apscheduler's jobs function

When I used flask-apscheduler(not apscheduler), I have some problems in my flask web project. Especially when I used db(flask-sqlalchemy) objects. The problem may be:
JOBS = [
{
'id': 'job1',
'func': 'app.monitor.views:test',
'args': (),
'trigger': 'interval',
'seconds': 2
}
]
./app/init.py:
from flask import Flask
from flask.ext.bootstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.pagedown import PageDown
from flask_apscheduler import APScheduler
from celery import Celery
# from apscheduler.schedulers.blocking import BlockingScheduler
from config import config,Config
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
pagedown = PageDown()
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL)
# https://pypi.python.org/pypi/Flask-APScheduler
scheduler = APScheduler()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
login_manager.init_app(app)
pagedown.init_app(app)
scheduler.init_app(app)
celery.conf.update(app.config)
if not app.debug and not app.testing and not app.config['SSL_DISABLE']:
from flask.ext.sslify import SSLify
sslify = SSLify(app)
from .monitor import monitor as monitor_1_0_blueprint
from .laser import laser as laser_1_0_blueprint
app.register_blueprint(monitor_blueprint,url_prefix='/monitor/api')
app.register_blueprint(laser_1_0_blueprint,url_prefix='/laser/api/v1.0')
return app
Error 1:db is : Error 2:db is :No handlers
could be found for logger "apscheduler.executors.default" Error 3:db
is : raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context
The key to the problem is to get the db and app objects in flask-apscheduler jobs function(views.py):
from app import scheduler
def test():
#to Solve the log error problem
import logging
log = logging.getLogger('apscheduler.executors.default')
log.setLevel(logging.INFO) # DEBUG
fmt = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
h = logging.StreamHandler()
h.setFormatter(fmt)
log.addHandler(h)
#get the app object
app = scheduler.app
#get the db object and use it
with app.app_context():
print '........................',db
from app import scheduler#
def test():
#to Solve the log error problem
import logging
log = logging.getLogger('apscheduler.executors.default')
log.setLevel(logging.INFO) # DEBUG
fmt = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
h = logging.StreamHandler()
h.setFormatter(fmt)
log.addHandler(h)
#get the app object
app = scheduler.app
#get the db object and use it
with app.app_context():
print '........................',db
def test():
#to Solve the log error problem
import logging
log = logging.getLogger('apscheduler.executors.default')
log.setLevel(logging.INFO) # DEBUG
fmt = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
h = logging.StreamHandler()
h.setFormatter(fmt)
log.addHandler(h)
#get the app object
app = scheduler.app
#get the db object and use it
with app.app_context():
print '........................',db #the right db object