I have setup my first Python FastAPI but getting stuck. I have a function that query some results. The following function query the first entry in the database on a specific date. Now I want the last entry on a date or all results sort by highest id but how do i do this?
def get_workday(db: Session, workday_date: date):
return db.query(DBWorkday).where(DBWorkday.date == workday_date).first()
full code:
from datetime import date
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import Optional, List
from sqlalchemy import Date, create_engine
from sqlalchemy.orm import declarative_base, sessionmaker, Session
from sqlalchemy import Column, String, Integer
app = FastAPI()
# SqlAlchemy Setup
SQLALCHEMY_DATABASE_URL = 'sqlite:///../db/database.db'
engine = create_engine(SQLALCHEMY_DATABASE_URL, echo=True, future=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# A SQLAlchemny ORM
class DBWorkday(Base):
__tablename__ = 'workdays'
id = Column(Integer, primary_key=True, index=True)
date = Column(Date)
start_time = Column(String(4))
end_time = Column(String(4))
type = Column(String, nullable=True)
location = Column(String, nullable=True)
Base.metadata.create_all(bind=engine)
# Workday Pydantic model
class Workday(BaseModel):
date: date
start_time: str
end_time: str
type: Optional[str] = None
location: Optional[str] = None
class Config:
orm_mode = True
# Methods for interacting with the database
def get_workday(db: Session, workday_date: date):
return db.query(DBWorkday).where(DBWorkday.date == workday_date).first()
#app.get('/workday/{date}')
def get_workday_view(date: date, db: Session = Depends(get_db)):
return get_workday(db, date)
return db.query(DBWorkday).where(DBWorkday.date == workday_date).order_by(DBWorkday.id.desc()).first()
Related
The following code raises the error sqlalchemy.exc.InvalidRequestError: A transaction is already begun on this Session when it gets to the line with session.begin():
import datetime
from sqlalchemy import create_engine, select, Integer, Column, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
Base = declarative_base()
class Person(Base):
__tablename__ = "people"
id = Column(Integer, primary_key = True)
name = Column(String(128), nullable = False)
created_at = Column(DateTime, nullable = False,
default=datetime.datetime.now(datetime.timezone.utc))
# # # # # # # # # # # # # # # # # # # #
dsn = 'sqlite:////tmp/people.sqlite'
engine = create_engine(dsn, echo=True)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
session = Session(engine, future=True)
p = Person(name="Able")
session.add(p)
session.commit()
print("About to do a select...")
sel = select(Person).filter_by(name="Able")
q = session.execute(sel).scalars().first()
print(q.created_at)
with session.begin():
print("hello")
Looking at the verbose output I can see that SQLAlchemy starts a transaction when executing the SELECT. I can see why it would start a transaction with an INSERT or UPDATE, but why does it start a transaction with a SELECT?
This question already has answers here:
Foreign key contraint is not getting set in SQLlite using flask alchemy [duplicate]
(1 answer)
SQLite3 "forgets" to use foreign keys
(3 answers)
Closed 1 year ago.
With the code below and following sequence I suspect an IntegrityError with a Foreign Key violation. Unfortunately the error isn't raised.
Create Customer(id=1)
Create Booking(cid=1)
Delete Customer(id=1)
#main.py
from typing import List
from fastapi.params import Depends
from schemas import Customer, ShowCustomer, Booking, ShowBooking
from fastapi import FastAPI, HTTPException
from database import get_db, engine, Base
from sqlalchemy.orm import Session
import models
models.Base.metadata.create_all(engine)
app = FastAPI()
#app.post("/customer")
async def customer(req: Customer, db: Session=Depends(get_db)):
new_customer=models.Customer(name=req.name, type=req.type)
db.add(new_customer)
db.commit()
db.refresh(new_customer)
return new_customer
#app.get("/customer", response_model=List[ShowCustomer])
async def create(db: Session=Depends(get_db)):
customers=db.query(models.Customer).all()
return customers
#app.delete('/customer/{id}')
def destory(id, db: Session=Depends(get_db)):
customer=db.query(models.Customer).filter(models.Customer.id == id)
if not customer.first():
raise HTTPException(status_code=404,
detail=f'Blog with the id {id} is not available '
)
customer.delete(synchronize_session=False)
db.commit()
return 'done'
#app.post("/booking")
async def read_root(req: Booking, db: Session=Depends(get_db)):
new_booking=models.Booking(name=req.name, type=req.type, cid=1)
db.add(new_booking)
db.commit()
db.refresh(new_booking)
return new_booking
#app.get("/booking",response_model=List[ShowBooking])
async def read_root(db: Session=Depends(get_db)):
bookings=db.query(models.Booking).all()
return bookings
# schemas.py
from pydantic import BaseModel
class Customer(BaseModel):
name:str
type:str
class ShowCustomer(Customer):
id:int
class Config():
orm_mode = True
class Booking(BaseModel):
name:str
type:str
class ShowBooking(Booking):
id:int
cid:int
class Config():
orm_mode = True
#models.py
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from database import Base
class Customer(Base):
__tablename__ = "customers"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
type = Column(String)
booking = relationship("Booking", back_populates="customer")
class Booking(Base):
__tablename__ = "bookings"
id = Column(Integer, primary_key=True, index=True)
cid = Column(Integer, ForeignKey("customers.id"), nullable=False)
name = Column(String)
type = Column(String)
customer = relationship('Customer', back_populates="booking")
#database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
SQLALCHEMY_DATABASE_URL = 'sqlite:///./cloud2.db'
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base = declarative_base()
def get_db():
db=SessionLocal()
try:
yield db
finally:
db.close()
structure
.
└── sql_app
├── __init__.py
├── crud.py
├── database.py
├── main.py
├── models.py
└── schemas.py
main.py
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session
from . import crud, models, schemas
from .database import SessionLocal, engine
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
#app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_name(db, name=user.name)
if db_user:
raise HTTPException(status_code=400, detail="Name already registered")
return crud.create_user(db=db, user=user)
#app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
#app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
#app.post("/users/{user_id}/messages/", response_model=schemas.Message)
def create_message_for_user(
user_id: int, message: schemas.MessageCreate, db: Session = Depends(get_db)
):
return crud.create_user_message(db=db, message=message, user_id=user_id)
#app.get("/messages/", response_model=List[schemas.MessageWithUser])
def read_messages(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
messages = crud.get_messages(db, skip=skip, limit=limit)
return messages
crud.py
from sqlalchemy.orm import Session
from . import models, schemas
def get_user(db: Session, user_id: int):
return db.query(models.User).filter(models.User.id == user_id).first()
def get_user_by_name(db: Session, name: str):
return db.query(models.User).filter(models.User.name == name).first()
def get_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.User).offset(skip).limit(limit).all()
def create_user(db: Session, user: schemas.UserCreate):
db_user = models.User(name=user.name)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def get_messages(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Message).offset(skip).limit(limit).all()
def create_user_message(db: Session, message: schemas.MessageCreate, user_id: int):
db_item = models.Message(**message.dict(), user_id=user_id)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password#postgresserver/db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
models.py
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from .database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True, unique=True, nullable=False)
messages = relationship("Message", back_populates="user")
class Message(Base):
__tablename__ = "messages"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True, unique=True, nullable=False)
message = Column(String, index=False)
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="messages")
schemas.py
from typing import List
from pydantic import BaseModel
class MessageBase(BaseModel):
title: str
message: str
class MessageCreate(MessageBase):
pass
class Message(MessageBase):
id: int
user_id: int
class Config:
orm_mode = True
class UserBase(BaseModel):
name: str
class UserCreate(UserBase):
pass
class User(UserBase):
id: int
messages: List[Message] = []
class Config:
orm_mode = True
class UserIdentifier(UserBase):
id: int
name: str
class MessageWithUser(MessageBase):
id: int
title: str
message: str
user: UserIdentifier
Run cmd:
uvicorn sql_app.main:app --reload
Navigate to: 127.0.0.1:8000/docs
Add users and messages for users
Try to read messages
The example result is the correct format but it does not give a response just 500 error and the following
Error:
pydantic.error_wrappers.ValidationError: 4 validation errors for MessageWithUser
response -> 0
value is not a valid dict (type=type_error.dict)
response -> 1
value is not a valid dict (type=type_error.dict)
response -> 2
value is not a valid dict (type=type_error.dict)
response -> 3
value is not a valid dict (type=type_error.dict)
The expected output is what is shown in the example output of the docs page, with UserIdentifier being added to Message but not any other fields from User if there were any, in this case there is not
I don't think that it is possible to cast a SqlAlchemy object with dependencies to a pydantic model out of the box.
#tiagolo (the creator of FastAPI) did this pydantic-sqlalchemy method that seems to do the trick of what you want:
https://github.com/tiangolo/pydantic-sqlalchemy
Otherwise, I think that you can create a list of MessageWithUser manually and return it.
lets say I've 2 tables users and devices. They have relation one-to-many.
In Sql, I can solve the mentioned problem by following query.
SELECT
users.*, devices.*
FROM
users
LEFT JOIN ( SELECT d1.*
FROM devices as d1
LEFT JOIN devices AS d2
ON d1.user_id = d2.user_id AND d1.date < d2.date
WHERE d2.user_id IS NULL ) as device_temp
ON (users.id = device_temp.user_id)
Here is my python code
#user_model.py
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(500), nullable=False)
last_name = Column(String(250), nullable=False)
device_model.py
#device_model.py
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, relation
from sqlalchemy import create_engine
from user_model import User
Base = declarative_base()
class DeviceModel(Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(), nullable=False)
device_id = Column(String(250), nullable=False)
user_uid = Column(String, ForeignKey((User.id)))
owner = relation(User, backref='user_device')
run.py
#run.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from user_model import User, Base
from sleep_session import SleepSession, Base
from device_model import DeviceModel, Base
engine = create_engine(connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
query = session.query(User,DeviceModel).join(DeviceModel)
results = query.all()
for row in results:
print(row.User.first_name +" "+ row.DeviceModel.device_id + " "+ str(row.DeviceModel.created_at))
I know this type of question is asked multiple times, but I could not find one with SqlAlchemy ORM.
I want the same result as described here
Thanks.
I used this question to practive sqlalchemy as I'm new to it.
Closest answer I can get is the following:
If you want to see 1 file full workable code go into the edits - I'll remove boilerplate code
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, relation
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
connection_string = 'postgres://postgres:password#localhost/test'
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(500), nullable=False)
last_name = Column(String(250), nullable=False)
class DeviceModel(Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime(), nullable=False)
device_id = Column(String(250), nullable=False)
user_uid = Column(Integer, ForeignKey((User.id))) # error Key columns "user_uid" and "id" are of incompatible types: character varying and integer.
owner = relation(User, backref='user_device')
engine = create_engine(connection_string)
Base.metadata.bind = engine
#User.__table__.create(engine)
#DeviceModel.__table__.create(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()
My Answer:
from sqlalchemy import and_, or_
from sqlalchemy.orm import aliased
DeviceModel2 = aliased(DeviceModel)
subquery = (session
.query(DeviceModel.created_at)
.outerjoin(DeviceModel2,
and_(DeviceModel.user_uid == DeviceModel2.user_uid,
DeviceModel.created_at < DeviceModel2.created_at))
.filter(DeviceModel2.user_uid == None)
.subquery('subq'))
query = (session
.query(User, DeviceModel)
.outerjoin(DeviceModel)
.filter(or_(
DeviceModel.created_at == subquery.c.created_at,
DeviceModel.id == None)))
print(query)
results = query.all()
for row in results:
if row[1]:
print({**row.User.__dict__, **row.DeviceModel.__dict__})
else:
print(row.User.__dict__)
from db_config import connection_string
from sqlalchemy import create_engine , and_ , inspect
from sqlalchemy.orm import sessionmaker, aliased
from user_model import User, Base
from device_model import DeviceModel, Base
engine = create_engine(connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
DeviceModel_aliased = aliased(DeviceModel)
#make sub-query
query_for_latest_device = session.query(DeviceModel).\
outerjoin(DeviceModel_aliased,
and_(DeviceModel_aliased.user_uid == DeviceModel.user_uid,
DeviceModel_aliased.created_at > DeviceModel.created_at)).\
filter(DeviceModel_aliased.id == None).\
subquery()
use_subquery_and_join = session.query(User.first_name,latest_device).\
join(query_for_latest_device,
query_for_latest_device.c.user_uid == User.user_id).\
all()
for row in join_user_and_device:
print(row._asdict())
How do i extract the table name from SQLAlchemy exist statement
assume we have the following code
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
engine = create_engine('mysql://...')
Session = sessionmaker(bind=engine)
conn = engine.connect()
session = Session(bind=conn)
query_exists = session.query(Person).exists()
how can i extract the table name from the query_exists?
from sqlalchemy.sql.visitors import ClauseVisitor
from sqlalchemy import Table
def extract_tables(sql_stmt):
tables = []
visitor = ClauseVisitor()
cluase_iter = visitor.iterate(elem)
for e in cluase_iter:
if isinstance(e, Table):
tables.append(e)
if isinstance(e, (ValuesBase, UpdateBase)):
tables.append(e.table)
return set(tables)