Flask Rest API SQL Alchemy 连接 Cloud SQL PostgreSQL

huangapple go评论82阅读模式
英文:

Flask Rest Api SQL Alchemy connection Cloud Sql Postgresq

问题

你的问题是Flask应用在调用与SQLAlchemy相关的端点时出现错误,错误信息是: "The current Flask app is not registered with this 'SQLAlchemy'"。这通常是因为没有正确初始化SQLAlchemy或者创建了多个SQLAlchemy实例导致的。

要解决这个问题,你可以尝试以下步骤:

  1. 在app.py中初始化SQLAlchemy:确保在app.py中正确初始化SQLAlchemy,可以使用db.init_app(app)来注册Flask应用。
  1. from flask_sqlalchemy import SQLAlchemy
  2. # ...
  3. app = Flask(__name)
  4. # Initialize SQLAlchemy with your app
  5. db = SQLAlchemy(app)
  1. 删除全局的db变量:在app.py中,删除全局的db变量,因为你已经将SQLAlchemy实例与Flask应用关联,不再需要全局db变量。
  1. db = None
  1. 修改init_connection_pool函数:确保init_connection_pool函数正确获取Flask应用的Unix socket路径。你可以使用app.config来获取应用配置。
  1. def init_connection_pool() -> sqlalchemy.engine.base.Engine:
  2. # use a Unix socket when INSTANCE_UNIX_SOCKET (e.g. /cloudsql/project:region:instance) is defined
  3. unix_socket_path = app.config.get("INSTANCE_UNIX_SOCKET")
  4. if unix_socket_path:
  5. return connect_unix_socket()
  6. raise ValueError("Missing database connection type. Please define one of INSTANCE_HOST, INSTANCE_UNIX_SOCKET, or INSTANCE_CONNECTION_NAME")
  1. 更新User.py文件:确保User.py文件中没有创建额外的SQLAlchemy实例。只使用Flask应用中已经初始化的SQLAlchemy实例。

这些更改应该有助于解决你的问题,确保Flask应用和SQLAlchemy正确集成并使用相同的实例。如果问题仍然存在,请确保你的依赖包已正确安装,并且数据库连接配置正确。

英文:

I have a connection problem with Cloud Sql Postgres from my Flask Rest API app.
I have a db.py file:

  1. import os
  2. from flask_sqlalchemy import SQLAlchemy
  3. import sqlalchemy
  4. db = SQLAlchemy()
  5. def connect_unix_socket() -> sqlalchemy.engine.base.Engine:
  6. """ Initializes a Unix socket connection pool for a Cloud SQL instance of Postgres. """
  7. # Note: Saving credentials in environment variables is convenient, but not
  8. # secure - consider a more secure solution such as
  9. # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
  10. # keep secrets safe.
  11. db_user = os.environ["DB_USER"] # e.g. 'my-database-user'
  12. db_pass = os.environ["DB_PASS"] # e.g. 'my-database-password'
  13. db_name = os.environ["DB_NAME"] # e.g. 'my-database'
  14. unix_socket_path = os.environ["INSTANCE_UNIX_SOCKET"] # e.g. '/cloudsql/project:region:instance'
  15. pool = sqlalchemy.create_engine(
  16. # Equivalent URL:
  17. # postgresql+pg8000://<db_user>:<db_pass>@/<db_name>
  18. # ?unix_sock=<INSTANCE_UNIX_SOCKET>/.s.PGSQL.5432
  19. # Note: Some drivers require the `unix_sock` query parameter to use a different key.
  20. # For example, 'psycopg2' uses the path set to `host` in order to connect successfully.
  21. sqlalchemy.engine.url.URL.create(
  22. drivername="postgresql+pg8000",
  23. username=db_user,
  24. password=db_pass,
  25. database=db_name,
  26. query={"unix_sock": "{}/.s.PGSQL.5432".format(unix_socket_path)},
  27. ),
  28. # [START_EXCLUDE]
  29. # Pool size is the maximum number of permanent connections to keep.
  30. pool_size=5,
  31. # Temporarily exceeds the set pool_size if no connections are available.
  32. max_overflow=2,
  33. # The total number of concurrent connections for your application will be
  34. # a total of pool_size and max_overflow.
  35. # 'pool_timeout' is the maximum number of seconds to wait when retrieving a
  36. # new connection from the pool. After the specified amount of time, an
  37. # exception will be thrown.
  38. pool_timeout=30, # 30 seconds
  39. # 'pool_recycle' is the maximum number of seconds a connection can persist.
  40. # Connections that live longer than the specified amount of time will be
  41. # re-established
  42. pool_recycle=1800, # 30 minutes
  43. # [END_EXCLUDE]
  44. )
  45. return pool

I import the db.py file in my app.py file:

  1. import os
  2. import sqlalchemy
  3. from flask import Flask
  4. from flask_smorest import Api
  5. from flask_sqlalchemy import SQLAlchemy
  6. from db import db, connect_unix_socket
  7. import models
  8. from resources.user import blp as UserBlueprint
  9. # pylint: disable=C0103
  10. app = Flask(__name__)
  11. def init_connection_pool() -> sqlalchemy.engine.base.Engine:
  12. # use a Unix socket when INSTANCE_UNIX_SOCKET (e.g. /cloudsql/project:region:instance) is defined
  13. if unix_socket_path:
  14. return connect_unix_socket()
  15. raise ValueError(
  16. "Missing database connection type. Please define one of INSTANCE_HOST, INSTANCE_UNIX_SOCKET, or INSTANCE_CONNECTION_NAME"
  17. )
  18. db = None
  19. @app.before_first_request
  20. def init_db() -> sqlalchemy.engine.base.Engine:
  21. global db
  22. db = init_connection_pool()
  23. api = Api(app)
  24. @app.route("/api")
  25. def user_route():
  26. return "Welcome user API!"
  27. api.register_blueprint(UserBlueprint)
  28. if __name__ == '__main__':
  29. server_port = os.environ.get('PORT', '8080')
  30. app.run(debug=True, port=server_port, host='0.0.0.0')

The app run correctly, when i call the end point to Get or Post users, the app crash and give me this error:

"The current Flask app is not registered with this 'SQLAlchemy'"
RuntimeError: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances?

This is my User.py class:

  1. from sqlalchemy.exc import SQLAlchemyError, IntegrityError
  2. from db import db
  3. from models import UserModel
  4. from schemas import UserSchema
  5. blp = Blueprint("Users", "users", description="Operations on users")
  6. @blp.route("/user/<string:user_id>")
  7. class User(MethodView):
  8. @blp.response(200, UserSchema)
  9. def get(self, user_id):
  10. user = UserModel.query.get_or_404(user_id)
  11. return user
  12. def delete(self, user_id):
  13. user = UserModel.query.get_or_404(user_id)
  14. db.session.delete(user)
  15. db.session.commit()
  16. return {"message": "User deleted"}, 200
  17. @blp.route("/user")
  18. class UserList(MethodView):
  19. @blp.response(200, UserSchema(many=True))
  20. def get(self):
  21. return UserModel.query.all()

How i can fix this issue?

答案1

得分: 1

@dev_ 你的问题在于你试图混合使用SQLAlchemy CoreSQLAlchemy ORM,好像它们是一样的,这导致了你的问题。使用 sqlalchemy.create_engine 创建的 SQLAlchemy 连接池使用 CORE API,而 Flask-SQLAlchemy 使用了 SQLAlchemy ORM 模型。这是导致你问题的核心原因。最好只使用其中一个。

我建议你纯粹地使用 Flask-SQLALchemy,并使用 cloud-sql-python-connector 库来满足你的用例。这将使你的生活更加轻松。

为了简化,我将去掉你的 db.py,让你的 app.py 文件如下:

  1. from flask import Flask
  2. from flask_smorest import Api
  3. from flask_sqlalchemy import SQLAlchemy
  4. from google.cloud.sql.connector import Connector, IPTypes
  5. from resources.user import blp as UserBlueprint
  6. # 加载环境变量
  7. db_user = os.environ["DB_USER"] # 例如,'my-database-user'
  8. db_pass = os.environ["DB_PASS"] # 例如,'my-database-password'
  9. db_name = os.environ["DB_NAME"] # 例如,'my-database'
  10. instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] # 例如,'project:region:instance'
  11. # Python Connector 数据库连接函数
  12. def getconn():
  13. with Connector() as connector:
  14. conn = connector.connect(
  15. instance_connection_name, # Cloud SQL 实例连接名称
  16. "pg8000",
  17. user=db_user,
  18. password=db_pass,
  19. db=db_name,
  20. ip_type= IPTypes.PUBLIC # IPTypes.PRIVATE 用于私有 IP
  21. )
  22. return conn
  23. app = Flask(__name__)
  24. # 配置 Flask-SQLAlchemy 使用 Python Connector
  25. app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql+pg8000://"
  26. app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
  27. "creator": getconn
  28. }
  29. # 初始化数据库(使用 app!)
  30. db = SQLAlchemy(app)
  31. # 你的代码的其余部分
  32. api = Api(app)
  33. # ...

希望这有助于解决你的问题!

英文:

@dev_ Your issue is that your are trying to intermingle the use of SQLAlchemy Core with SQLAlchemy ORM as if they are the same thing, leading to your issues. SQLAlchemy connection pools created using sqlalchemy.create_engine use the CORE API while Flask-SQLAlchemy uses the SQLAlchemy ORM model. This is the core reason for you issue. It is easier to use one or the other.

I would recommend using purely Flask-SQLALchemy with the use of the cloud-sql-python-connector library for your use-case. It will make your life much easier.

For simplicity, I am getting rid of your db.py leading to your app.py file being as follows:

  1. from flask import Flask
  2. from flask_smorest import Api
  3. from flask_sqlalchemy import SQLAlchemy
  4. from google.cloud.sql.connector import Connector, IPTypes
  5. from resources.user import blp as UserBlueprint
  6. # load env vars
  7. db_user = os.environ["DB_USER"] # e.g. 'my-database-user'
  8. db_pass = os.environ["DB_PASS"] # e.g. 'my-database-password'
  9. db_name = os.environ["DB_NAME"] # e.g. 'my-database'
  10. instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] # e.g. 'project:region:instance'
  11. # Python Connector database connection function
  12. def getconn():
  13. with Connector() as connector:
  14. conn = connector.connect(
  15. instance_connection_name, # Cloud SQL Instance Connection Name
  16. "pg8000",
  17. user=db_user,
  18. password=db_pass,
  19. db=db_name,
  20. ip_type= IPTypes.PUBLIC # IPTypes.PRIVATE for private IP
  21. )
  22. return conn
  23. app = Flask(__name__)
  24. # configure Flask-SQLAlchemy to use Python Connector
  25. app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql+pg8000://"
  26. app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
  27. "creator": getconn
  28. }
  29. # initialize db (using app!)
  30. db = SQLAlchemy(app)
  31. # rest of your code
  32. api = Api(app)
  33. # ...

Hope this helps resolve your issue!

huangapple
  • 本文由 发表于 2023年2月14日 19:28:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75447181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定