Insert data into database using Fast api using python - python

I am new to FastAPI and I'm working on inserting data into MY SQL database using the Fast API POST method. I have created a set of sample codes to create a schema and inserted the single data into the MY SQL table using the below sample code.
Ref link: https://codingnomads.co/blog/python-fastapi-tutorial
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import Optional, List
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker, Session
from sqlalchemy import Boolean, Column, Float, String, Integer
app = FastAPI()
# SqlAlchemy Setup
SQLALCHEMY_DATABASE_URL = 'sqlite+pysqlite:///./db.sqlite3:'
engine = create_engine(SQLALCHEMY_DATABASE_URL, echo=True, future=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# A SQLAlchemny ORM Place
class DBPlace(Base):
__tablename__ = 'places'
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50))
description = Column(String, nullable=True)
coffee = Column(Boolean)
wifi = Column(Boolean)
food = Column(Boolean)
lat = Column(Float)
lng = Column(Float)
Base.metadata.create_all(bind=engine)
# A Pydantic Place
class Place(BaseModel):
name: str
description: Optional[str] = None
coffee: bool
wifi: bool
food: bool
lat: float
lng: float
class Config:
orm_mode = True
# Methods for interacting with the database
def get_place(db: Session, place_id: int):
return db.query(DBPlace).where(DBPlace.id == place_id).first()
def get_places(db: Session):
return db.query(DBPlace).all()
def create_place(db: Session, place: Place):
db_place = DBPlace(**place.dict())
db.add(db_place)
db.commit()
db.refresh(db_place)
return db_place
# Routes for interacting with the API
#app.post('/places/', response_model=Place)
def create_places_view(place: Place, db: Session = Depends(get_db)):
db_place = create_place(db, place)
return db_place
#app.get('/places/', response_model=List[Place])
def get_places_view(db: Session = Depends(get_db)):
return get_places(db)
#app.get('/place/{place_id}')
def get_place_view(place_id: int, db: Session = Depends(get_db)):
return get_place(db, place_id)
#app.get('/')
async def root():
return {'message': 'Hello World!'}
But I need to parse a list of arrays in the request to create_places_view and insert multiple values at once. so how to achieve this in Fast API. Any pointers/help? Thanks
Example:
[{name: str
description: Optional[str] = None
coffee: bool
wifi: bool
food: bool
lat: float
lng: float },
{name: str
description: Optional[str] = None
coffee: bool
wifi: bool
food: bool
lat: float
lng: float }]

I have looped the value to create user. While sending the post request in the list of dict.
# Fast API to create/insert a new user
#app.post('/create_place_view/', responses={404: {"model": Message}})
async def create_place(user: List[Place], db: Session = Depends(get_db)):
if not user:
return JSONResponse(status_code=404, content={"message": "Place details not found"})
else:
places_list = []
for places in place:
db_place = create_places(db, places)
place_json = places.dict()
places_list.append(places_json)
return JSONResponse(content=places_list)

Related

Problems understanding Pydantic model with metaclasses/ aliases mapping when using SqlAlchemy

I'm trying to have my Pydantic/ORM models "output" labels,but when using SqlAlchemy ORM I feel a bit locked/ stuck.
I want the fieldname "test1" ( below in the code) to return ( "Test left side") instead of test1 in the JSONResponse
As an explanation, In sql i would for example use “as”
select test1 as ‘Test left side’ from pretest
I know I can do the same in an ORM statement/query, but I want it to be reachable as a field/ attribute from the orm model class, or perhaps as some validation methods from my Pydantic model.
To explain I have added an example of two short models below
my model in Sqlalchemy:
class Pretest(Base):
__tablename__ = "pretest"
user_id = Column(Integer)
pretest_id = Column(Integer, primary_key=True)
timestamp_pretest = Column(DateTime(timezone=True), default=func.now())
test1 = Column(Integer)
my model in Pydantic: ( I use Optional because im testing at the moment)
class Pretest(BaseModel):
user_id: Optional[int] = None
pretest_id: Optional[int] = None
timestamp_pretest: Optional[datetime] = None
test1: Optional[int] = None
class Config:
orm_mode= True
So I’m wondering if Pydantic have a possibility to validate against a labelslist/array/ object that could contain a type of test1= “Test left side”
Or if the ORM models have some additional metadata that could be used like
test1 = Column(Integer, alias=“Test left side”)
I hope I make this question understandable?
My endpoint look a bit simplified something like this:
#router.post("/pretest", tags=["Medicaldata"], status_code=status.HTTP_200_OK)
def pretest(pretest: Pretest, token: str = Depends(oauth2_scheme)):
try:
query = db.query(models.Pretest).first()
except:
query = "query failed"
return JSONResponse(content=query)
Where I use the pretest-object which is type defined by the Pydantic model as query parameters( not shown here)
This response will create a json-object of the fields and values in the database.
The field/variable test1 will return as test1 instead of "Test left side", since I do not have a place to add labels or some sort of aliases.
I can add and map the json object manually in Python before I return it, but it’s a lot of complex queries spanning several tables, so it feels a bit “wrong” to do it that way.
The reason for all this is so that I can have model and label consistency and use the map function with spread operators in components in React as shown below.
get_backend(“/pretest”,data)
.then setPretestlist(response)
{pretestlist.map((item) => {
return <ShowPretest {...item} key={item.name} />;
})}
This will now show as test1 in the webpage instead of a more explanatory text like this "Test 1 left side"
#snakecharmerb, Thx, you put me on the right track.
The solution, if someone else wonder:
the Pydantic model needs to be changed from this:
class Pretest(BaseModel):
user_id: Optional[int] = None
pretest_id: Optional[int] = None
timestamp_pretest: Optional[datetime] = None
test1: Optional[int] = None
class Config:
orm_mode = True
To this:
class Pretest(BaseModel):
user_id: Optional[int] = None
pretest_id: Optional[int] = None
timestamp_pretest: Optional[datetime] = None
test1: Optional[int] = None
class Config:
fields = {
"test1": "Test left side",
"timestamp_pretest": "Time tested",
}
orm_mode = True
The endpoint needed to change in the way it does its response to this:
#router.post("/pretest", tags=["Medicaldata"], status_code=status.HTTP_200_OK)
def pretest(pretest: Pretest, token: str = Depends(oauth2_scheme)):
try:
query = db.query(models.Pretest).first()
query = Pretest.from_orm(query)
except:
query = "query failed"
return query.dict(by_alias=True)

SQLModel / SQLAlchemy: Is there an opposite function to session.get()?

I'm just getting started with ORM SQLAlchemy / SQLModel and I wonder if there is a shortcut function to get an entity from a session given all columns except the primary key.
For example, right now i have this (which works):
from sqlmodel import SQLModel, Field, create_engine, Session, select
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
engine = create_engine("sqlite:///hero_db.db")
def get_hero(hero: Hero) -> Hero:
with Session(engine) as session:
return session.exec(
select(Hero).where(
Hero.name == hero.name,
Hero.secret_name == hero.secret_name,
)
).one()
And i wonder if there is a shortcut without explicitly listing every column. For example like this:
...
return session.exec(
select(Hero).where(
Hero == hero
)
).one()
Or even better like this:
...
return session.get_reverse(Hero, hero)

Python iterate through object to create tables with Flask-SQLAlchemy

EDITED
I've been trying to create an class that will allow me to iterate through a list, or dictionary, and generate tables and rows.
The code starts as follows:
from flask_sqlalchemy import SQLAlchemy as sa
from flask import Flask as fl
import pymysql
pymysql.install_as_MySQLdb()
app = fl(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:the_other_stuff'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] =False
DEBUG =True
db = sa(app)
a=['some_stuff','gaaahhhh','lalala','rawr','pizza']
class test(db.Model):
__tablename__ = 'stuff'
id = db.Column('id', db.Integer, primary_key = True)
data = db.Column('data', db.Unicode(50))
def __init__(self, id, data):
self.id = id
self.data = data
def stuff():
for i in range(len(a)):
data= a[i]
i = i + 1
id = i
db.session.add(test(id,data))
db.create_all()
return db.session.commit()
stuff()
I'm still going to try and structure it so that it can take a dictionary, or list, and then add the key as the table name if it is a dict. If someone has that code, I won't argue with you sharing, but if not I'll post when that is done.
use db.session.add / db.session.commit. db object is initialized from flask_sqlalchemy. It is not db.Session.
The below code auto inserts data into a one-to-many relationship table set. The recursive function acts like a while loop (%timeit showed the same results for both) I just like the way it looks over a while loop. The function will index to a list in a list, I was going 3 deep but modified the loops to simply. This was originally designed to push a list like so: list[a][i][0] each zero value inside every [i] was the same value type, I set it to only do [a][i] to keep it a little more simple, and so it could be used as as base if someone liked it. [a][i][0] was very fast, but [a][i] might be better off as a list of pd.DataFrame, instead of as np.array. If [i] is not going to the same db.Column() you'll have to declare one for each set of [i] and figure out a way of indexing through it.
A table generator involves a class generator with a base class. I don't have a working example, but can upload when I do
import numpy as np
from flask_sqlalchemy import SQLAlchemy as sa
from flask import Flask as fl
import pymysql
pymysql.install_as_MySQLdb()
app = fl(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:the_other_stuff'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] =False
DEBUG =True
db = sa(app)
list=[np.array(np.ones(100)),np.array(np.ones(100)),np.array(np.ones(100)),
np.array(np.ones(100)),np.array(np.ones(100))]
class Parent(db.Model):
id = db.Column('id', db.Integer, primary_key=True)
data= db.Column('data', db.VARCHAR(45))
_child = db.relationship('child', backref='parent', lazy=True)
def __init__(self, data):
self.data = data
def __repr__(self):
return '<Parent %r>' % self.id
class Child(db.Model):
id = db.Column('id', db.Integer, primary_key = True)
data = db.Column('data', db.VARCHAR(45))
parent_id = db.Column(db.Integer, db.ForeignKey('Parent.id'))
parent = db.relationship('Parent')
def __init__(self,data):
self.data = data
def __repr__(self):
return '<Child %r>' % self.id
def child_loop(i = 0):
for a in range(len(list)):
with db.session.no_autoflush:
try:
p = Parent(symbol_col=data[a])
c = child(data = list[a][i])
s.c.append(child)
db.session.add(p)
db.session.add(c)
except exc.IntegrityError as e:
db.session.rollback()
i = i + 1
if a < len(list[0]):
child_loop(i = i)
return print('inserted open_close')
child_loop(i=0)

Initializing a SQLAlchemy model instance with its relationship as a parameter

I often see, that the model instance with one-to-many relationship is explicitly extended following its initialization, such as:
one = One()
# some code goes here
one.many = [Many(), Many(), Many()]
one.many.append(Many())
But in my case I see it reasonable to initialize a many-to-one object with it's relationship already supplied to __init__:
one = One()
many = Many(one = one)
Is it somehow considered a bad practice?
For a reproducible example, please consider the following code:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, ForeignKey, create_engine
from sqlalchemy.orm import relationship, sessionmaker
Base = declarative_base()
class One(Base):
__tablename__ = 'one'
id = Column(Integer, primary_key=True)
many = relationship("Many", back_populates="one")
class Many(Base):
__tablename__ = 'many'
id = Column(Integer, primary_key=True)
one_id = Column(Integer, ForeignKey('one.id'))
one = relationship("One", back_populates="many")
def __init__(self, one=None):
if one is not None:
self.one = one
# Setup the DB and connection
engine = create_engine('sqlite:///:memory:', echo=True)
conn = engine.connect()
session = sessionmaker(bind=engine)()
Base.metadata.create_all(engine)
# Is it proper to initialize a model instance, with its relationship as an argument?
one1 = One()
many1 = Many(one1)
print(many1.one is one1) # True
print(one1.many[0] is many1) # True
Although it looks clean to me so far, this approach may result in an ambiguous code:
# What happens here?
# Instance of many first initialized with one2a,
# then reassigned to one2b?
one2a = One()
many2 = Many(one=one2a)
print(many2.one is one2a) # True
one2b = One(many=[many2]) # same when One(many=[Many(one=one2a)])
print(many2 is one2b.many[0]) # True
print(many2.one is one2a) # False
print(many2.one is one2b) # True
Finally, please consider the aforementioned __init__ method. Since self.many is by default expected to be an empty list, what would be the desired way to initialize it as such?
def __init__(self, many=None):
if many is not None:
self.many = many

How do I use Mixins with SQLAlchemy to simplify querying and filtering operation?

Assume the following setup:
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class MyClass(Base):
id = Column(Integer, primary_key=True)
name = Column(String)
The normal paradigm to query the DB with SQLAlchemy is to do the following:
Session = sessionmaker()
engine = 'some_db_location_string'
session = Session(bind=engine)
session.query(MyClass).filter(MyClass.id == 1).first()
Suppose, I want to simplify the query to the following:
MyClass(s).filter(MyClass.id == 1).first()
OR
MyClass(s).filter(id == 1).first()
How would I do that? My first attempt at that to use a model Mixin class failed. This is what I tried:
class ModelMixins(object)
def __init__(self, session):
self.session = session
def filter(self, *args):
self.session.query(self).filter(*args)
# Redefine MyClass to use the above class
class MyClass(ModelMixins, Base):
id = Column(Integer, primary_key=True)
name = Column(String)
The main failure seems to be that I can't quite transfer the expression 'MyClass.id == 1' to the actual filter function that is part of the session object.
Folks may ask why would I want to do:
MyClass(s).filter(id == 1).first()
I have seen something similar like this used before and thought that the syntax becomes so much cleaner I can achieve this. I wanted to replicate this but have not been able to. Being able to do something like this:
def get_stuff(some_id):
with session_scope() as s:
rec = MyClass(s).filter(MyClass.id== some_id').first()
if rec:
return rec.name
else:
return None
...seems to be the cleanest way of doing things. For one, session management is kept separate. Secondly, the query itself is simplified. Having a Mixin class like this would allow me to add the filter functionality to any number of classes...So can someone help in this regard?
session.query takes a class; you're giving it self, which is an instance. Replace your filter method with:
def filter(self, *args):
return session.query(self.__class__).filter(*args)
and at least this much works:
In [45]: MyClass(session).filter(MyClass.id==1)
Out[45]: <sqlalchemy.orm.query.Query at 0x10e0bbe80>
The generated SQL looks right, too (newlines added for clarity):
In [57]: str(MyClass(session).filter(MyClass.id==1))
Out[57]: 'SELECT "MyClass".id AS "MyClass_id", "MyClass".name AS "MyClass_name"
FROM "MyClass"
WHERE "MyClass".id = ?'
No guarantees there won't be oddities; I've never tried anything like this before.
Ive been using this mixin to good success. Most likely not the most efficient thing in the world and I am no expert. I define a date_created column for every table
class QueryBuilder:
"""
This class describes a query builer.
"""
q_debug = False
def query_from_dict(self, db_session: Session, **q_params: dict):
"""
Creates a query.
:param db_session: The database session
:type db_session: Session
:param q_params: The quarter parameters
:type q_params: dictionary
"""
q_base = db_session.query(type(self))
for param, value in q_params.items():
if param == 'start_date':
q_base = q_base.filter(
type(self).__dict__.get('date_created') >= value
)
elif param == 'end_date':
q_base = q_base.filter(
type(self).__dict__.get('date_created') <= value
)
elif 'like' in param:
param = param.replace('_like', '')
member = type(self).__dict__.get(param)
if member:
q_base = q_base.filter(member.ilike(f'%{value}%'))
else:
q_base = q_base.filter(
type(self).__dict__.get(param) == value
)
if self.q_debug:
print(q_base)
return q_base

Categories