How can I integrate flask-sqlalchemy with Google Cloud Functions and something like Cloud SQL?
Looking at the minimal application example, it wraps the app variable:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return '<User %r>' % self.username
But my Google Cloud Function doesn't have access to anything called app:
def my_function(request):
return "Hello world!"
Creating my own app with app = Flask(__name__) doesn't work as this app isn't used by Cloud Functions to return the response.
You can use flask.current_app instead. You'll also have to configure flask-sqlalchemy to use the Unix Domain Socket provided by CloudSQL:
from flask import current_app as app
from flask_sqlalchemy import SQLAlchemy
# Set these in the environment variables for the function
db_user = os.environ.get('CLOUD_SQL_USERNAME')
db_password = os.environ.get('CLOUD_SQL_PASSWORD')
db_name = os.environ.get('CLOUD_SQL_DATABASE_NAME')
db_connection_name = os.environ.get('CLOUD_SQL_CONNECTION_NAME')
# This is for Postgres, it's similar for MySQL
app.config['SQLALCHEMY_DATABASE_URI'] = f'postgresql://{db_user}:{db_password}#/{db_name}?host=/cloudsql/{db_connection_name}'
# This must be set, determine which is best for you
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
...
Then you can query the User model in your function:
def my_function(request):
user = User.query.first()
return f"Hello {user.username)!"
Related
When I try to use flask-migrate with my model and a Postgres database, it does not work. Even if the database is created at startup. Every time migrate only detects the following:
INFO [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['Auth_ID']'
INFO [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['ClosedCourse_ID']'
My Model (much larger but is poorly designed like this)
class SKAuthentication(db.Model):
__tablename__ = 'sk-authentication'
Auth_ID = db.Column(UUID(as_uuid=True), primary_key=True, unique=True)
Basic_Auth = db.Column(Text, nullable=False)
Basic_User = db.Column(Text, nullable=False)
Teacher_ID = db.Column(UUID(as_uuid=True), db.ForeignKey('sk-teacher.Teacher_ID'), nullable=False)
Model = db.Column(Text, nullable=True)
Phone_ID = db.Column(Text, nullable=True)
Brand = db.Column(Text, nullable=True)
VersionInstalled = db.Column(Text, nullable=False, default='0.0.0')
def __init__(self, teacher_id):
chars = string.digits + string.ascii_letters + string.punctuation
self.Basic_Password = ''.join(secrets.choice(chars) for _ in range(256))
self.Auth_ID = create_ID(teacher_id, token_1, auth_crypt)
self.Basic_Auth = sha512_crypt.encrypt(self.Basic_Password)
self.Basic_User = create_ID(self.Basic_Auth, self.Auth_Secret)
self.Teacher_ID = teacher_id
class SKDayilyClosedCourse(db.Model):
__tablename__ = 'sk-daily-closed-course'
ClosedCourse_ID = db.Column(UUID(as_uuid=True), primary_key=True, unique=True)
Teachers_Group_ID = db.Column(UUID(as_uuid=True), db.ForeignKey('sk-teachers-groups.Row_ID'), nullable=False)
Course_Date = db.Column(Date, nullable=False)
Closed = db.Column(BOOLEAN, nullable=False, default=False)
Reminded = db.Column(BOOLEAN, nullable=False, default=False)
def __init__(self, teachers_group_id, course_date, reminded):
self.ClosedCourse_ID = create_ID(teachers_group_id, False, False, course_date)
self.Teachers_Group_ID = teachers_group_id
self.Course_Date = course_date
self.Reminded = reminded
My run.py looks like this:
from flask import Flask, session
from flask_sqlalchemy import SQLAlchemy
from modules.extensions import csrf, migr, cors
from config import DevelopmentConfig
from flask_migrate import upgrade, migrate
db = SQLAlchemy()
migr = Migrate()
def create_app():
app = Flask(__name__)
csrf.init_app(app)
app.config.from_object(DevelopmentConfig)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
cors.init_app(app, resources={r"/mobile/v1/*": {"origins": "GET, POST, OPTIONS"}})
migr.init_app(app, db, render_as_batch=False)
with app.app_context():
import modules.database.model
db.create_all()
db.session.commit()
migrate()
upgrade()
from application import application
from application import application_ticket
from application import application_mobile
from application import application_bookings
app.register_blueprint(application.application)
app.register_blueprint(application_ticket.ticket, url_prefix="/ticket")
app.register_blueprint(application_mobile.mobile, url_prefix="/mobile/v1")
app.register_blueprint(application_bookings.bookings, url_prefix="/bookings/v1")
#app.before_request
def before_request():
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=1)
return app
This is the important part form my extensions.py
from flask_wtf import CSRFProtect
from flask_migrate import Migrate
from flask_cors import CORS
csrf = CSRFProtect()
migr = Migrate()
cors = CORS()
...
And that's my wsgi.py:
from run import create_app
app = create_app()
Once it worked, but I don't know how. I didn't change anything, so I tried to reproduce it and I encountered the same problem I mentioned before. I am not sure how to proceed with this problem.
I tried to create the tables inside the models.py and try to migrate everything from there, but still just the unique constraints.
Another way, I figured out was to import the models, which I also needed to create the tables with Flask-SQLAlchemy, but that changed nothing.
Next solution I've found was, to run everything from console. Nothing changed.
Other solution: Drop Table "alembic_version" -> didn't changed anything.
Hopefully someone can help me soon!
I am expecting to get following output:
INFO [alembic.autogenerate.compare] Detected added table '[<Tablename>]'
...
Thank you all!
I'm setting up a new Flask app, where I want to have all of my SQLAlchemy models in separate files. The overall structure should end up looking like this:
flask-app
|
|--api
| |--user.py
| |--example.py
|
|--controllers
| |--user.py
| |--example.py
|
|--models
| |--user.py
| |--example.py
|
|-main.py
|-modelref.py
|-config.py
In terms of code here's what i have so far:
main.py:
import os
from flask import Flask, send_from_directory
from flask_bcrypt import Bcrypt
from flask_cors import CORS
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import modelref
from apiref import load_api
app = Flask(__name__, static_url_path='', static_folder='./../react/public')
app.config.from_object(os.getenv("APP_SETTINGS", "config.Development"))
CORS(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
bcrypt = Bcrypt(app)
#app.route("/", defaults={'path':''})
def serve(path):
return send_from_directory(app.static_folder,'index.html')
load_api(app)
The modelref import is a shorthand of sorts. In the modelref.py file I import every model that I have in the models folder like so:
modelref.py:
from models.user import User
from models.example import Example
Then the model itself is something along the lines of:
models/user.py:
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(64), unique=True, nullable=False)
email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
first_name = db.Column(db.String(128), nullable=False)
last_name = db.Column(db.String(128), nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
def __init__(self, username, email, password, first_name, last_name):
self.username = username
self.email = email
self.password = bcrypt.generate_password_hash(password, flask.current_app.config.get('BCRYPT_LOG_ROUNDS')).decode()
self.first_name = first_name
self.last_name = last_name
self.registered_on = datetime.datetime.now()
def save(self):
db.session.add(self)
db.session.commit()
The model gets manipulated in a controller class defined here:
controllers/user.py:
class UserController:
#staticmethod
def register(username: str, password: str, email: str, first_name: str, last_name: str) -> dict:
result: dict = {}
try:
user = User(
username=username,
password=password,
email=email,
first_name=first_name,
last_name=last_name
)
user.save()
except IntegrityError:
User.rollback()
raise ResourceExists("user already exists")
return login(user.username, user.password);
#staticmethod
def login(username: str, password: str):
user = User.query.filter_by(username).first()
if user:
if bcrypt.check_password_hash(user.password, password):
token = user.encode_auth_token(user.id)
return {
"username": user.username,
"first_name": user.first_name,
"last_name": user.last_name,
"token": token
}
else:
abort(400, "Invalid Password")
else:
abort(404, "User Not Found")
The controller is accessed in an API resource defined using pluggable views. The views and routes are registered with the app in the load_app(app) method. I'm not sharing that whole pipeline, because I believe it is out of scope, but if anyone believes it's not, than I will add it into the post.
So now here's the problem. If I try to access the model in the code or run flask db migrate, I get an error that db is not defined.
A solution I've seen is to add a db=SQLAlchemy line in the model, but then the flask db migrate doesn't find this model, likely because the mentioned line creates a new SQLAlchemy instance, so it's essentially unaware of the models existence.
With this setup I'm unable to import the db object from main since that causes a circular import due to the model being imported in the controller.
Is there a way I can pass a reference to db to the model without an import, or make the model search for the "current" instance of 'db'. Or is there any way to reorganize the main code so that I can maintain the general structure, but be able to run migrations?
It turns out that in a case such as mine, the best option is to apply the application factory pattern. I now have an app.py module, where I initialize all the modules I need in a create_app method like so:
app.py:
import os
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
bcrypt = Bcrypt()
def create_app():
app = Flask(__name__, static_url_path='', static_folder='./../react/public')
app.config.from_object(os.getenv("APP_SETTINGS", "config.Development"))
import modelref
db.init_app(app)
migrate.init_app(app, db)
bcrypt.init_app(app)
return app
And them my main.py file simply invokes the create_app method and then sets up routes and API resources like so:
main.py:
from flask import send_from_directory
from flask_cors import CORS
from apiref import load_api
from app import create_app
app = create_app()
CORS(app)
#app.route("/", defaults={'path':''})
def serve(path):
return send_from_directory(app.static_folder,'index.html')
load_api(app)
Running import modelref within the create_model method makes all the models imported in modelref visible to the app and the flask db commands.
I have some difficulties with the library lask-sqlalchemy, I tried to create my database with the code bellow, i have no error but no table created.
_init.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
app.config.from_json("C:/Users/lquastana/PycharmProjects/flask_demo/conf/api-config.json")
app.app_context().push()
db.py
from flask_sqlalchemy import SQLAlchemy
from api_lab import app
db = SQLAlchemy(app)
member.py
from api_lab.models.db import db
class Member(db.Model):
__table_args__ = {"schema": "public"}
id = db.Column(db.BigInteger,
primary_key=True,
autoincrement=True)
id_company = db.Column(db.BigInteger,
db.ForeignKey("compagny.id"),
index=True,
nullable=False)
nom = db.Column(db.String(80), unique=True, nullable=False)
age = db.Column(db.Integer, unique=True, nullable=False)
def __repr__(self):
return '<Member %r>' % self.nom
run_api.py
from api_lab import app
from api_lab.models.db import db
def main():
host = app.config["ENV"]["HOST"]
port = app.config["ENV"]["PORT"]
debug = app.config["ENV"]["DEBUG"]
app.run(host=host, port=port, debug= debug)
if __name__ == '__main__':
db.create_all()
#main()
You should put all initialization related logic into init.py
Use SQLAlchemy to setup a db connection with your app.config
init.py
from api_lab import app
from api_lab.models.db import db
from flask_sqlalchemy import SQLAlchemy
app.config.from_json("C:/Users/lquastana/PycharmProjects/flask_demo/conf/api-config.json")
app.app_context().push()
db = SQLAlchemy(app)
# initialize the DB
db.create_all()
I'm having trouble creating a modular application using Flask using blueprints. I'm not sure how to migrate models defined in blueprints.
My app looks as follows:
- app
- __init__.py
- user
__init__.py
models.py
app.__init__.py looks as follows:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config
from flask_migrate import Migrate
from app.auth import bp as user_bp
db = SQLAlchemy()
app = Flask(__name__)
app.config.from_object(Config)
app.register_blueprint(user_bp, url_prefix='/user')
migrate = Migrate(app, db)
from app.auth.models import User
user/.__init__.py:
from flask import Blueprint
bp = Blueprint('user', __name__)
user/models.py:
from app import db
class User(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(128))
last_name = db.Column(db.String(128))
user_name = db.Column(db.String(120))
password_hash = db.Column(db.String(220))
def __repr__(self):
return '<User {}>'.format(self.email)
When trying to run flask db migrate... no models are being detected and I think I have some sort of circular dependency.
Can someone please explain how to do this properly?
The User model (user/models.py) should be imported somewhere in the application, try to import into the blueprint file (user/__init__.py)
from flask import Blueprint
from app.user.models import User
bp = Blueprint('user', __name__)
app.py
from flask import Flask
from flask_restful import Resource, Api
from user import user_blueprint
from flask_cors import CORS
app = Flask(__name__)
api = Api(app)
app.register_blueprint(user_blueprint)
cors = CORS(app, resources={r"/*": {"origins": "*"}})
if __name__ == '__main__':
app.run(debug=True)
model/user.py
from sqlalchemy import Column, INTEGER, String
class User(Base):
__tablename__ = 'USER'
__table_args__ = {'schema': 'MAPPING'}
ID = Column(INTEGER(11), primary_key=True)
FIRST_NAME = Column(String(255), nullable=False, server_default=text("''"))
LAST_NAME = Column(String(255), nullable=False, server_default=text("''"))
EMAIL = Column(String(255), nullable=False, server_default=text("''"))
route/user_route
from flask import Blueprint, jsonify, request
from flask_restful import Resource, Api
user_blueprint = Blueprint('app/', __name__)
user_api = Api(user_blueprint, prefix='/')
class User(Resource):
def get(self):
db_conn = //db connection Object
results = // your query
db_conn.close()
return jsonify(results)
user_api.add_resource(User, '/user')
I am going through a flask/sqlalchemy tutorial
https://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
and I configured my database url to be:
postgres://username:password#localhost:5432/dbname
when I run db.create_all() in the interactive python shell it doesn't throw any errors, but it doesn't do anything either.
From what I understand it is supposed to create the User table with three columns; id, username, and email .
from flask import Flask, url_for, render_template
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://username:password#localhost:5432/dbname'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
app.debug = True
#app.route("/")
def hello():
return render_template('hello.html')
if __name__ == "__main__":
app.run()
It should be the exact format.
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://postgres:postgres#localhost/DBNAME"
Where postgres is username and postgres is password, localhost is address
Try using postgresql in the URL instead of postgres.
That's the form that's used both for JDBC and in the SQLAlchemy doc (http://docs.sqlalchemy.org/en/rel_0_9/dialects/postgresql.html).
Once you install PostgreSQL you can do this:
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://<username>:<password>#localhost/<database_name>"
Where:
<username> is your username (by default postgres)
<password> is your password (by default postgres)
<database_name> the name of your database (by deafault postgres if you are using the DB created once you installed PostgreSQL).
If you have created a role (a user) for example admin with password admin and a database named posts then you can do this:
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://admin:admin#localhost/posts"
more info here https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/