Flask SQLAlchemy and Blueprints - python

I am still learning flask and I have created an restful API with SQLAlchemy. The app is getting to be big and I would like to split it up into multiple files. When I separate the routes section from the main file, the app starts complaining about the SQL modules not being found. I have added all the imports to the routes file and it doesn't seem to help either. The error I am getting is:
module>
Session.configure(bind=engine)
NameError: name 'engine' is not defined
How can I get the Alchemy module to talk to admin.py?
app
app.py
admin
__init__.py
admin.py
######################## admin.py ########################
from flask import Flask, request, jsonify, Blueprint, render_template
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
import json
import logging
admin = Blueprint("admin", __name__)
#initialize Session
Session = sqlalchemy.orm.sessionmaker()
Session.configure(bind=engine)
session = Session()
employee_schema = EmployeeSchema()
#create Employee
#admin.route('/', methods=['POST'])
def add_employee():
..........
#Get Employee
#admin.route('/', defaults={'id': None}, methods=['GET'])
#admin.route('/<id>', methods=['GET'])
..........
#Delete Employee
#admin.route('/<id>', methods=['DELETE'])
def delete_employee(id):
..........
#update Employee
#admin.route('/<id>', methods=['PUT'])
def update_employee(id):
..........
######################## app.py ########################
from flask import Flask, request, jsonify
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
import json
import logging
from admin.admin import admin
#Init app
app = Flask(__name__)
#Allows URL's to be with a trailing / or not
app.url_map.strict_slashes = False
app.register_blueprint(admin, url_prefix="/")
#Gets password info from json file
..........
# Define the MariaDB engine using MariaDB Connector/Python
engine = sqlalchemy.create_engine(f"mariadb+pymysql://{DB_USER}:{DB_PASS}#{DB_HOST}:{DB_PORT}/{DB}")
Base = declarative_base()
class Employee(Base):
__tablename__ = 'employees'
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
firstname = sqlalchemy.Column(sqlalchemy.String(length=100))
lastname = sqlalchemy.Column(sqlalchemy.String(length=100))
active = sqlalchemy.Column(sqlalchemy.Boolean, default=True)
Base.metadata.create_all(engine)
#initialize Session
Session = sqlalchemy.orm.sessionmaker()
Session.configure(bind=engine)
session = Session()

I suppose your error is traceable to admin.py, because in there you want to initialize the session again using enginewhich is not known in admin.py.
A possible solution would be to import engine from app.py.

Found a workaround. I created a init file inside the folder instead and it is now working.

Related

How to import a Model from Controller in Flask api

Im kinda new in api creations and trying to make one in Flask from zero. I have a issue making the model. Here is the code.
main.py :
from flask import Flask
from flask_restful import Api, Resource, reqparse #Reqparse sobra (?)
from controllers.attribute_controller import Attribute
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)
Attribute()
api.add_resource(Attribute, "/attribute/<int:attribute_id>")
if __name__ == "__main__":
app.run(debug=True)
attribute_controller.py
from flask_restful import Api, Resource, reqparse
from models.attribute_model import AttibuteModel
attribute_put_args = reqparse.RequestParser()
attribute_put_args.add_argument("name", type=str, help="Name is required", required=True )
attributes = {}
class Attribute(Resource):
def get(self, attribute_id):
return attributes[attribute_id]
def put(self, attribute_id):
args = attribute_put_args.parse_args()
attributes[attribute_id] = args
return attributes[attribute_id],201
attribute_model.py
from main import db
class AttibuteModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
def __repr__(self):
return f"Attribute(name={name})"
test.py
import requests
BASE = "http://127.0.0.1:5000/"
response = requests.put(BASE + "attribute/1", {"name": "red"})
print(response.json())
I got this error:
I know why i got the error, but i dont know any other solution to acced the model in my controllers.
I need the attribute_model in my attribute_controller to change it but i dont know how to solve the error. I've tried to follow this instructions:
How to avoid circular imports in a Flask app with Flask SQLAlchemy models?
But I didn't understand it at all so I don't know how to continue :(. Thx for your time.
Your problem is a circular import.
In attribute_controller.py you're importing AttibuteModel (missing an 'r' there by the way).
In attribute_model.py you're importing db from main.
In main.py you're importing Attribute from attribute_controller.py (which imports AttibuteModel which imports db) on line 3, before db has been created on line 11.
Move the import statement to after db initialisation:
db = SQLAlchemy(app)
from controllers.attribute_controller import Attribute

Can I avoid circular imports in Flask and SQLAlchemy

app/init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__ name __)
db = SQLAlchemy(app)
from app import views, models
app/models.py:
from app import db # I want to avoid this everywhere
I really don't like my submodules having a dependency on their parent. Also can the global package variables be avoided too? I want a more OO solution.
One alternative for app is to use Blueprints I think, but then I loose the route decorator. Also the same cannot be done for db with SQLAlchemy (or can it?).
Take a look at this project: https://github.com/sloria/cookiecutter-flask
It's a great example for doing things the right way. Many of great Flask features are used: blueprints, application factories and more.
Here is how they register extensions, such as SQLAlchemy Database:
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
# app/app.py
from app.extensions import db
def create_app(config_object=ProdConfig):
app = Flask(__name__.split('.')[0])
app.config.from_object(config_object)
register_extensions(app)
...
def register_extensions(app):
db.init_app(app)
...
Try use 3rd. We create exts.py file to instancing SQLAlchemy like this:
exts.py
from flask_sqlalchemy import SQLAlchemy
from flask_xxx import xxx
db = SQLAlchemy()
...
run.py
from flask import Flask
from .exts import db, ...
def register_extensions(app):
db.init_app(app)
...
def create_app(config):
app = Flask(__ name __)
app.config.from_object(config)
register_extensions(app)
return app
app = create_app(config)
models.py
from .exts import db
class XXXModel(db.Model):
pass

Create flask-sqlalchemy database is failing

Im trying to import my app config (db_config.py) via:
>>> from app.db_config import db_session
Error received 'ImportError: No module named 'app.db_config'; 'app' is not a package
app.py looks like:
import pymysql.cursors
from flask import Flask, render_template, request
from flask_migrate import Migrate
from app.db_config import db_session
#app setup
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mypass#localhost/mydb'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
#app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
#app.route('/', methods=('GET', 'POST'))
def index():
return 'meow'
if __name__ == '__main__':
app.run()
db_config.py:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('mysql+pymysql://root:mypass#localhost/mydb',
convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
import app.db_table
Base.metadata.create_all(bind=engine)
db_table.py:
from sqlalchemy import Column, Integer, String, DateTime, Boolean
from sqlalchemy.ext.declarative import declarative_base
from app.db_config import Base
Base = declarative_base()
class Tld(Base):
__tablename__ = 'Tld'
id = Column(Integer, primary_key=True)
uri = Column(String(80), unique=True)
tmstmp = Column(DateTime())
auth = Column(Boolean())
def __init__(self, uri=None):
self.uri = uri
self.tmstmp = tmstmp
self.auth = auth
What am I doing wrong here? I'm following this tutorial but Im not receiving the output i expected.
Is something wrong with my app.py that makes it not callable like that? or something else wrong with it?
(First time alchemy user)
Thank you so much
I resolved this myself. I needed to do from db_config import db_session, instead of from app.db_config import db_session

How do I remove a circular dependency between a Flask blueprint and the application initialisation? [duplicate]

I want to structure my Flask app something like:
./site.py
./apps/members/__init__.py
./apps/members/models.py
apps.members is a Flask Blueprint.
Now, in order to create the model classes I need to have a hold of the app, something like:
# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy
db = SQLAlchemy(current_app)
class Member(db.Model):
# fields here
pass
But if I try and import that model into my Blueprint app, I get the dreaded RuntimeError: working outside of request context. How can I get a hold of my app correctly here? Relative imports might work but they're pretty ugly and have their own context issues, e.g:
from ...site import app
# ValueError: Attempted relative import beyond toplevel package
The flask_sqlalchemy module does not have to be initialized with the app right away - you can do this instead:
# apps.members.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
# fields here
pass
And then in your application setup you can call init_app:
# apps.application.py
from flask import Flask
from apps.members.models import db
app = Flask(__name__)
# later on
db.init_app(app)
This way you can avoid cyclical imports.
This pattern does not necessitate the you place all of your models in one file. Simply import the db variable into each of your model modules.
Example
# apps.shared.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# apps.members.models
from apps.shared.models import db
class Member(db.Model):
# TODO: Implement this.
pass
# apps.reporting.members
from flask import render_template
from apps.members.models import Member
def report_on_members():
# TODO: Actually use arguments
members = Member.filter(1==1).all()
return render_template("report.html", members=members)
# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members
reporting = Blueprint("reporting", __name__)
reporting.route("/member-report", methods=["GET","POST"])(report_on_members)
# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting
app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)
Note: this is a sketch of some of the power this gives you - there is obviously quite a bit more that you can do to make development even easier (using a create_app pattern, auto-registering blueprints in certain folders, etc.)
an original app.py: https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
...
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
class Computer(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
# Create the database tables.
db.create_all()
...
# start the flask loop
app.run()
I just splitted one app.py to app.py and model.py without using Blueprint. In that case, the above answer dosen't work. A line code is needed to work.
before:
db.init_app(app)
after:
db.app = app
db.init_app(app)
And, the following link is very useful.
http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

Flask Blueprint setup with SQLAlchemy gives UnboundExecutionError

I am using Flask to build an experimental application using Blueprint. Here is my project's structure:
-myproject/
requirements.txt
run.py
-apps/
-static/
-template/
-database/
__init__.py
views.py
model.py
auth.py
message.py
I init my app in init.py, using blueprint to integrate other parts of the app.
app = Flask(__name__)
from .views import views
from .model import model, db
from .auth import auth, login_manager
db.init_app(app)
app.register_blueprint(views)
app.register_blueprint(model)
app.register_blueprint(auth)
Then in views.py:
from flask import Flask, request, redirect, render_template, session, g, url_for, Blueprint
views = Blueprint('views', __name__)
from sqlalchemy.orm import scoped_session, relation, sessionmaker
# ORM Session
orm_session = scoped_session(sessionmaker())
#views.route('/courses')
def courses():
courses = orm_session.query(Course).order_by('-id')
return render_template('courses.html', courses=courses)
My Course class is defined in model.py:
class Course(db.Model):
__tablename__ = 'course'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
subjects = db.relationship('Subject', secondary='course_subject_link')
students = db.relationship('Student', secondary='course_student_link')
active = db.Column(db.Boolean)
def __repr__(self):
return "<{0}:{1.name}:{1.subjects!r}:{1.students!r}>".format(Course, self)
In template folder, I have put something like {% for course in courses %} and {% set active_page = "courses" %} in the template file. When I run the app, it gives me this error:
UnboundExecutionError: Could not locate a bind configured on mapper Mapper|Course|course, SQL expression or this Session
I did not use Blueprint before so the app used to be able to run. But after I used Blueprint the urls seem to be broken. How can I fix this?
I know what the problem is.
From the hint in the error message I guess that I forget to create_engine and pass the engine to sessionmaker using bind=engine.
Here is the code I changed in views.py:
from os.path import join, dirname
_cwd = dirname(__file__)
engine = create_engine('sqlite:///' + join(_cwd, 'database/courses.sqlite'))
Session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
orm_session = Session()
Here database/courses.sqlite is the location of my database file.

Categories