Accessing same db.session across different modules in sqlalchemy - python

I am very new to sqlalchemy and am trying to figure out how to get things cleaner and connecting.
I have created a /model base.py doc where I have created a session and established all my entities in tables (along with relationships and etc.). I want to create another module in which I operate CRUD operations on the entities (tables) in base.py. This file is called object.py and has the class BaseAPI(object) and has the different functions "create" "read" "update" and "delete". I want to make sure that I am connecting to my table (base.py) in object.py and operating on the entity User. For this case, the entity (table) is Users.
This is what I have in the API object.py doc:
#!/usr/bin/env python
from sqlalchemy import create_engine
from sqlalchemy.orm import relationship, backref, sessionmaker
from datetime import datetime, timedelta
import notssdb.model
from base import User #importing from the module base.py -- doesn't work
engine = create_engine('sqlite:///./notssdb.db', echo=True) #in-memory sql engine
# create a Session
Session = sessionmaker(bind=engine)
class BaseAPI(object):
# DBSession = scoped_session(sessionmaker(engine))
# users = DBSession.query(User).all()
def __init__ (self):
session = Session()
# CREATE USER
def create_user(self, username, password, fullname):
new_user = User(username, password, fullname)
self.session.commit(new_user)
print(username, password, fullname)
Am I importing too many things? Do I need to import all the sqlalchemy tools? Does my init constructor under class BaseAPI need to instantiate the DB session?

1. Am I importing too many things? Do I need to import all the sqlalchemy tools?
Sqlalchemy doesn't have it's own coding style, you've to follow Python coding style. If you don't use any module there is no point of importing it.
I don't see this has been used from sqlalchemy.orm import relationship, backref and this should be used while defining models, hence you don't need to import these modules.
2. Does my init constructor under class BaseAPI need to instantiate the
DB session?
There is no hard rule that you've initiate session in your BaseAPI, you can even write your programme like this..
#!/usr/bin/env python
from sqlalchemy import create_engine
from sqlalchemy.orm import relationship, backref, sessionmaker
from datetime import datetime, timedelta
import notssdb.model
from base import User #importing from the module base.py -- doesn't work
engine = create_engine('sqlite:///./notssdb.db', echo=True) #in-memory sql engine
# create a Session
Session = sessionmaker(bind=engine)
session = Session()
class BaseAPI(object):
# CREATE USER
def create_user(self, username, password, fullname):
new_user = User(username, password, fullname)
session.commit(new_user)
print(username, password, fullname)
But it's not good practice to club your connection generation part with user manager, I would suggest you follow this way..
Note: This is just a sample code and I didn't execute this, you just have to follow this to structure your code.
First create seperate module for connection management, may be like connection_manager.py with the below content.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///./notssdb.db', echo=True)
# create a Session
Session = sessionmaker(bind=engine)
class SessionManager(object):
def __init__(self):
self.session = Session()
And the create your user_manger.py and import your SessionManager here.
from base import User # this is your User model
from connection_manager import SessionManager
class UserManager(SessionManager):
def create_user(self, username, password, fullname):
new_user = User(username, password, fullname)
self.session.commit(new_user)
print(username, password, fullname)
def delete_user(self, *args):
pass # your code
This way you can make your code cleaner.

Related

sessionmaker object has no attribute add

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

Put method does not update the database but it shows 200 as a response

I am Facing Issue With put Method it shows me 200 Successfully response but it does not updated on database.
database.py:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
sql_database_url="mysql://root:zeehan#localhost:3306/mybookstore"
engine=create_engine(sql_database_url)
sessionLocal=sessionmaker(autocommit=False,bind=engine)
base=declarative_base()
This is my database file
main.py:
from typing import Optional
from fastapi import FastAPI,Query,Depends,Path,HTTPException
from sqlalchemy import Column,String,Integer,Float
from pydantic import BaseModel, errors
from sqlalchemy.orm import Session, query, session
from sqlalchemy import create_engine
from sqlalchemy.sql.schema import MetaData
from sqlalchemy import databases
from book_Database import engine,sessionLocal,base,sessionmaker
app=FastAPI()
class BookStore(base):
__tablename__="mybookstore"
id=Column(Integer,primary_key=True,index=True,autoincrement=True)
name=Column(String(255),index=True)
author=Column(String(225))
rating=Column(Float)
description=Column(String(225))
class BookStoreSchema(BaseModel):
name:str
author:str
rating:float
description:str
class BookStoreUpdateSchema(BaseModel):
id=int
name:str
author:str
rating:float
description:str
class Config:
orm_model=True
def get_bookdb():
db=sessionLocal()
try:
yield db
finally:
db.close()
base.metadata.create_all(bind=engine)
#app.get("/books")
def getstudent(db:Session=Depends(get_bookdb)):
return db.query(BookStore).all()
#app.get("/books/{id}")
def getstudent_by_name(id:int,db:Session=Depends(get_bookdb)):
bookstore = db.query(BookStore).get(id)
return bookstore
#app.post("/create-book")
def creatBook(*,BookStoreSchema:BookStoreSchema,rating :float=Query( None,gt=0,lt=6)):
bookstore = BookStore( name=BookStoreSchema.name,author=BookStoreSchema.author,rating=BookStoreSchema.rating ,description=BookStoreSchema.description)
with Session(bind=engine) as session:
session.add(bookstore)
session.commit()
return bookstore
#app.put("/update-book/{id}")
def update_student(id:int,bookstore:BookStoreUpdateSchema,db:Session=Depends(get_bookdb)):
bookstore = db.query(BookStore).get(id)
BookStore.name = bookstore.name
BookStore.author=bookstore.author
BookStore.rating=bookstore.rating
BookStore.description=bookstore.description
db.commit()
return bookstore
When I try to run this API using UVICorn I was running successfully but there is some issue with the put method. It shows successful response but it does not update the content of database.
In your database.py file, declarative_base() returns a class so instead of base make it Base
Base = declarative_base()
class BookStore(Base):
pass
In your main.py file for createBook function try something simple first:
#app.post("/create-book")
def creat_a_book(new_book: BookStoreSchema, db: Session = Depends(get_bookdb)):
book = BookStore(**new_book())
db.add(book)
db.commit()
db.refresh(book)
return book
change the variable name of the updated book in your update function arguments, and in your schema definition change orm_model to orm_mode let's call it updated_book
Try grabbing your book from bookstore like this:
bookstore = db.query(BookStore).filter(BookStore.id == id)
and update the book with the update function
bookstore.update(updated_book.dict(), synchronize_session=False)
db.commit()

SQLAlchemy not creating tables

I am trying to setup a database just like in a tutorial but I am getting a programming error that a table doesn't exist when I'm trying to add a User
This is the file that errors (database.py):
from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(
"mysql+pymysql://testuser:testpassword#localhost/test?charset=utf8",
connect_args = {
"port": 3306
},
echo="debug",
echo_pool=True
)
db_session = scoped_session(
sessionmaker(
bind=engine,
autocommit=False,
autoflush=False
)
)
Base = declarative_base()
def init_db():
import models
Base.metadata.create_all(bind=engine)
from models import User
db_session.add(
User(username="testuser", password_hash=b"", password_salt=b"", balance=1)
)
db_session.commit()
print("Initialized the db")
if __name__ == "__main__":
init_db()
To init the database (create the tables) I just run the file.
It errors when it creates the test user.
Here is models.py:
from sqlalchemy import Column, Integer, Numeric, Binary, String
from sqlalchemy.orm import relationship
from database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(16), unique=True)
password_hash = Column(Binary(32))
password_salt = Column(Binary(32))
balance = Column(Numeric(precision=65, scale=8))
def __repr__(self):
return "<User(balance={})>".format(balance)
I tried:
Committing before adding users (after create_all)
Drop existing tables from the database (although it seems like the table never gets committed)
from models import User instead of import models (before create_all)
Sorry if there are so many simillar questions, I promise I scavenged for answers, but it's always silly mistakes I made sure I didn't make (or atleast the ones I saw).
I am using MariaDB.
Sorry for long post, many thanks in advance.
The Base in database.py isn't the same Base that is imported into models.py.
A simple test is to put a print('creating Base') function call just above the Base = declarative_base() statement, and you'll see it is being created twice.
Python calls the module that is being executed '__main__', which you know as you have the if __name__ == '__main__' conditional at the bottom of your module. So the first Base that is created is __main__.Base. Then, in models.py, from database import Base causes the database module to be parsed again, creating database.Base in the namespace, and that is the Base from which User inherits. Then back in database.py, the Base.metadata.create_all(bind=engine) call is using the metadata from __main__.Base which has no tables in it, and as such creates nothing.
Don't execute out of the module that creates the Base instance. Create another module called main.py (or whatever), and move your init_db() function there and import Base, db_session and engine from database.py into main.py. That way, you are always using the same Base instance. This is example of main.py:
from database import Base, db_session, engine
from models import User
def init_db():
Base.metadata.create_all(bind=engine)
db_session.add(
User(username="testuser", password_hash=b"", password_salt=b"", balance=1)
)
db_session.commit()
print("Initialized the db")
if __name__ == "__main__":
init_db()
Declare Base class once(for each database) & import it to all modules which define table classes (inherited from Base)
For Base (a metaclass) to scan & find out all classes which are inherited from it, we need to import all the modules where such table classes (inherited from Base) are defined to module where we call Metadata.create_all(engine).
You need to import the relevant model where you call "Base.metadata.create_all". Example below to create user table
from ModelBase import Base
from UserModel import User
def create_db_schema(engine):
Base.metadata.create_all(engine,checkfirst=True)

How to Generate Fixtures from Database with SqlAlchemy

I'm starting to write tests with Flask-SQLAlchemy, and I'd like to add some fixtures for those. I have plenty of good data for that in my development database and a lot of tables so writing data manually would get annoying. I'd really like to just sample data from the dev database into fixtures and then use those. What's a good way to do this?
i would use factory boy
to create a model factory you just do:
import factory
from . import models
class UserFactory(factory.Factory):
class Meta:
model = models.User
first_name = 'John'
last_name = 'Doe'
admin = False
then to create instances:
UserFactory.create()
to add static data just give as kwarg to create
UserFactory.create(name='hank')
so to seed a bunch of stuff throw that in a for loop. :)
If you need to handle fixtures with SQLAlchemy or another ORM/backend then the Fixture package may be of use: Flask-Fixtures 0.3.3
That is a simple library that allows you to add database fixtures for your unit tests using nothing but JSON or YAML.
While Kyle's answer is correct, we still need to provide the model factory with a database session, otherwise we would never actually commit to the db. Also, factory boy has a dedicated class SQLAlchemyModelFactory for interacting with SQLAlchemy.
https://factoryboy.readthedocs.io/en/stable/orms.html#sqlalchemy
The whole setup could look something like this:
import pytest
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from factory.alchemy import SQLAlchemyModelFactory
engine = create_engine( os.getenv("SQLALCHEMY_DATABASE_URI"))
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# this resets our tables in between each test
def _reset_schema():
db = SessionLocal()
for table in Base.metadata.sorted_tables:
db.execute(
'TRUNCATE {name} RESTART IDENTITY CASCADE;'.format(name=table.name)
)
db.commit()
#pytest.fixture
def test_db():
yield engine
engine.dispose()
_reset_schema()
#pytest.fixture
def session(test_db):
connection = test_db.connect()
transaction = connection.begin()
db = scoped_session(sessionmaker(bind=engine))
try:
yield db
finally:
db.close()
transaction.rollback()
connection.close()
db.remove()
class UserFactory(SQLAlchemyModelFactory):
class Meta:
model = models.User
first_name = 'John'
last_name = 'Doe'
admin = False
#pytest.fixture(autouse=True)
def provide_session_to_factories(session):
# usually you'd have one factory for each db table
for factory in [UserFactory, ...]:
factory._meta.sqlalchemy_session = session

sqlalchemy create_engine() if the db already exists

from pox.core import core
import pox.openflow.libopenflow_01 as of
import re
import datetime
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, Date, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
log = core.getLogger()
engine = create_engine('sqlite:///nwtopology.db', echo=False)
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
if I create call the last four python statements repeateadly by restarting the program will it have a negative impact on the correct functioning of the database.? Will it create the database again if one already exists?
As sberry wrote - calling create_engine and creating session multiple times by rerunning same script will just open connection and create SQLAlchemy engine object with reference to this connection.
Thus doing so won't create new sqlite database file and won't impact the database functioning.
Also I would suggest to make sure that your code always do session.close() at the end of your script. This would ensure that all changes if there were any will be committed to database.
By changes I mean any updates/inserts your script may do to database.

Categories