Everywhere I need to query the database in my application I import the Session from this file.
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
db_name = 'analytics'
db_url = 'postgresql+psycopg2://**:**#localhost:5432/{0}'.format(db_name)
Engine = create_engine(db_url, echo=False)
SessionMaker = sessionmaker(bind=Engine, autoflush=False)
Session = scoped_session(SessionMaker)
Base = declarative_base()
I'm writing some unit tests and need to mock the Session object so that whenever it is imported in my app, it connects to the unittest database rather than the analytics database.
class FacebookTest(unittest.TestCase):
#classmethod
def setUpClass(self):
"""
create unittest database and insert Premier League 2015-2016 season
"""
con = connect(user='**', host='localhost', password='**', database='postgres')
con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cur = con.cursor()
cur.execute('DROP DATABASE IF EXISTS unittest')
cur.execute('CREATE DATABASE unittest')
cur.close()
con.close()
Engine = create_engine('postgresql+psycopg2://**:**#localhost:5432/unittest')
Base.metadata.create_all(Engine)
Session.connection = Engine.connect()
Set your mock database on a different port, for example 5433 and change the port depending on which DB you want to connect to.
You can do like this. Let's say you have a db.py where you initiate your sessions and import them all over. Modify it like this (ignore reference to configurator, that's my internal module):
import psycopg2
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import configurator
Base = declarative_base()
def connect():
cfg = configurator.Configurator("database")
return psycopg2.connect(
user=cfg.user, host=cfg.host, dbname=cfg.db, port=cfg.port, password=cfg.pwd
)
def connect_test():
conn_string = (
"host='localhost' user='postgres' password='yourPassword' port='5433'"
)
return psycopg2.connect(conn_string)
def Session(database='dev'):
if database == 'dev':
engine = create_engine("postgresql://", creator=connect)
elif database == 'test':
engine = create_engine("postgresql://", creator=connect_test)
Session = sessionmaker(bind=engine)
return Session()
UPDATE: Important correction - added brackets in return session(), sorry about that.
UPDATE2: Changed code after explicit testing.
Related
I am trying to insert data into postgresql server. While doing so, when I try to add the data into the SQLalchemy session, I am getting the error "sessionmaker object has no attribute add":
from sqlalchemy.orm import Session
def create_new_user(user: UserCreate, db: Session):
user=User(username= user.username,
email=user.email,
hashed_password= Hasher.get_password_hash(user.password),
is_active=True,
is_superuser=False
)
db.add(user)
db.commit()
db.refresh(user)
return user
You should create an object from Session as show in this example; which uses a context-manager.
Currently, I use the scoped_session pattern (suitable for most web apps). Here is how my session initialization looks like:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
engine = create_engine("sqlite://")
Session = scoped_session(sessionmaker(bind=engine))
Session() # --> returns the same object in the same thread
I'm trying to test my database in my FastAPI app.
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.db import Base, get_db, get_session
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_get_users():
response = client.get("/users")
assert response.status_code == 200
This gives me an error sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) could not translate host name "db" to address: Name or service not known.
In my app.db, this is how I define the engine:
engine = create_engine(settings.DATABASE_URL, pool_pre_ping=True, echo=True, echo_pool=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_session():
with Session(engine) as session:
yield session
def get_db() -> Generator:
try:
db = SessionLocal()
yield db
finally:
db.close()
I also tried using get_session instead of get_db, then the error is: no such table: users. Any ideas what is wrong with my setup?
if you have an already table in your database , you have to use MetaData to map them like:
from sqlalchemy import create_engine, MetaData
meta = MetaData(bind=engine)
meta.reflect(views=True)
table_name = meta.tables['table_name']
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = Session()
def get_db():
db = session
try:
yield db
finally:
db.close()
I have a simple function in Fast API app that gets called by a cron, something like this (note that this is not a get or post Fast API method but a simple function):
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
where get_db is:
from database import SessionLocal
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
And database.py is:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "://url-to-db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Since crud method takes db (object of SessionLocal yielded by get_db) as a param, how do I use it as a dependency injection with my function just like we use in GET or POST as shown in code read_user method above.
The lines below declare the engine and session objects.
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
URL = 'mysql://root:pass#12.34.56.78/mydb'
engine = create_engine(URL, echo=False)
session = scoped_session(sessionmaker())
session.remove()
session.configure(bind=engine, autoflush=False, expire_on_commit=False)
I would like to know how engine and session objects could be used to check if they are able to connect to the database with URL, database name and credentials provided.
This is how I setup my database for an application (in Flask):
from sqlalchemy.engine import create_engine
from sqlalchemy.orm import scoped_session, create_session
from sqlalchemy.ext.declarative import declarative_base
engine = None
db_session = scoped_session(lambda: create_session(bind=engine,
autoflush=False, autocommit=False, expire_on_commit=True))
Base = declarative_base()
Base.query = db_session.query_property()
def init_engine(uri, **kwargs):
global engine
engine = create_engine(uri, **kwargs)
Base.metadata.create_all(bind=engine)
return engine
If I connect to a file database that has had tables created already, everything works fine, but using sqlite:///:memory: as a target database gives me:
OperationalError: (OperationalError) no such table: users u'DELETE FROM users' ()
when querying like so for ex.:
UsersTable.query.delete()
db_session.commit()
I am accessing this code from a unit test. What is the problem?
Thanks
Edit:
Working setup of the application:
app = Flask(__name__)
app.config.from_object(__name__)
app.secret_key = 'XXX'
# presenters
from presenters.users import users
# register modules (presenters)
app.register_module(users)
# initialize the database
init_engine(db)
The code you posted doesn't contain any table/class declaration. Are you sure that the declaration is done before init_engine() is called?