We have a web application at work that needs to connect to our Azure SQL database for the duration of the request. To make querying the database easier, we use SQL Alchemy and pyodbc.
We have a few goals we want to achieve:
First, to make sure we open a new session for every request, we can wrap the request functions into decorators that make sure a new session is created and subsequently destroyed.
To make sure our session is easily accessible from all over the application without having to hand it over in every function call, we use a singleton pattern. However, since we run multiple threads to handle multiple requests at the same time, we have to make sure that there are no race conditions with respect to the session objects.
SQL Alchemy has a great utility to make this easier: the scoped session.
To use this, we wrap our connection factory into a scoped_session() call:
from urllib.parse import quote_plus from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session connection_string = "..." engine = create_engine("mssql+pyodbc:///?odbc_connect={}".format(quote_plus(connection_string)) session_factory = sessionmaker(bind=engine) Session = scoped_session(session_factory)
Now, whenever we want to use the session, we just call Session() and SQL Alchemy makes sure we reuse the existing session object. If we are done, we can call Session.remove() to close the session. There is no way for SQL Alchemy to tell if the thread is done so we have to do this ourselves.
There are some posts that describe how to setup the connection to Azure SQL databases using access tokens, but the best resource for this is the SQL Alchemy docs themselves.
Let's go over the details together. First, we need a connection string. Since we want to rely on managed identity (or the Azure CLI for local development), we do not put any credentials into the connection string:
Driver={ODBC Driver 18 for SQL Server};Database=YOUR_DB;Server=tcp:you.database.windows.net,1433;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30
Our general plan is:
Now, let's take a look at the code:
from urllib.parse import quote_plus from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session connection_string = "..." engine = create_engine("mssql+pyodbc:///?odbc_connect={}".format(quote_plus(connection_string)) session_factory = sessionmaker(bind=engine) Session = scoped_session(session_factory)
With this, we achieved our goals. With the scoped_session() we do not need to open a new session every time a request comes in (this will be handled for us) but we should close the session at the end so that we do not have too many dangling sessions.
We also connect to the SQL database using our own identity (for local dev) or the managed identity of the web service. We modify the connection string each time a new connection is created.
A big thank you to David for helping me figure out the concept of scoped sessions.
The above is the detailed content of Connect to Azure SQL database in SQL Alchemy using Entra ID tokens. For more information, please follow other related articles on the PHP Chinese website!