Fastapi sqlalchemy pydantic relational field - sqlalchemy

I am just a starter in FastAPI/Pydantic & SqlAlchemy - I have two model Post and Category where I want Post should fetch Category name instead of only id
when I try to use the below code it gives following error in console
Any help in solving this is much appreciated thank you!
response -> 1 -> category_name
field required (type=value_error.missing)
post.py models
class Post(Base):
__tablename__="post"
id = Column(Integer, primary_key=True, index=True)
title=Column(String(50))
user_id=Column(Integer, ForeignKey("users.id"))
category_id = Column(Integer, ForeignKey("category.id"))
category_name=relationship("Category", backref="post")
class Category(Base):
__tablename__="category"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
Pydantic models
class CategoryGet(BaseModel):
id:int
name:str
class Config:
orm_mode=True
class Post(BaseModel):
id = int
title=str
user_id=int
category_id = int
category_name=CategoryGet
class Config:
orm_mode=True
My mainapp.py
router = APIRouter()
#router.get("/", response_model=List[schemas.VehicleGet])
def get_vehicle(db: Session = Depends(get_db), skip: int = 0, limit: int = 50) -> Any:
vehicle = crud.post.get_multi(db, skip=skip, limit=limit)
return vehicle

Pydantic models attributes uses : instead of =
class Post(BaseModel):
id: int
title: str
user_id: int
category_id: int
category_name: CategoryGet
class Config:
orm_mode = True

I guess the problem is that, you forgot orm_mode=True config for you Post model and consequently it is unable to recognize the category_name field. I hope this will solve but if not you could check this thread where an example and some clarification about relationship handling with pydantic.

Related

Sort list of results in nested field graphql - python - grapheneSQLAlchemy

I have a question regarding the way I can sort a list of results in a many to one relationship using GraphQL.
Let's take the example from graphene-sqlalchemy.
The schema is:
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
name = Column(String)
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String)
hired_on = Column(DateTime, default=func.now())
department_id = Column(Integer, ForeignKey('department.id'))
department = relationship(
Department,
backref=backref('employees',
uselist=True,
cascade='delete,all'))
And here is my Schema :
import graphene
from models import (Employee as EmployeeModel, Department as DepartmentModel)
from graphene_sqlalchemy import (
SQLAlchemyObjectType
)
class Employee(SQLAlchemyObjectType):
class Meta:
model = EmployeeModel
class Department(SQLAlchemyObjectType):
class Meta:
model = DepartmentModek
class Query(graphene.ObjectType):
find_departments = graphene.List(of_type = Department)
def resolve_find_departments(root, info) :
return db.session.query(DepartmentModel).all()
In my query I would like to access the list of employee from my department and order the results by name. My query could look like :
query findDepartments(){
findDepartments{
department{
employee(orderBy : 'name'){
name
}
}
}
}
But this query won't work. How can I achieve the sorting of the nested field employee ?
There are multiple ways to do this, but a simple way is to create a custom resolver with the ordering that you want, i.e. something like
class Query(graphene.ObjectType):
find_departments = graphene.List(of_type = Department)
find_departments_by_name = graphene.List(of_type = Department)
def resolve_find_departments(root, info):
return db.session.query(DepartmentModel).all()
def resolve_find_departments_by_name(root, info):
return db.session.query(DepartmentModel).order_by("name").all() # this is a guess of how sqlalchemy sorting syntax
You can override resolver for employees:
import graphene
from models import (Employee as EmployeeModel, Department as DepartmentModel)
from graphene_sqlalchemy import (
SQLAlchemyObjectType
)
class Employee(SQLAlchemyObjectType):
class Meta:
model = EmployeeModel
class Department(SQLAlchemyObjectType):
class Meta:
model = DepartmentModel
employees = graphene.List(of_type=Employee, order_by=graphene.String())
def resolve_employees(root, info, **kwargs):
query = self.employees
order_arg = kwargs.get('order_by')
if order_org:
# should check that `order_arg` is a column of `Employee`
query = query.order_by(order_arg)
return query.all()
class Query(graphene.ObjectType):
find_departments = graphene.List(of_type = Department)
def resolve_find_departments(root, info) :
return db.session.query(DepartmentModel).all()

Pydantic how to get relationship value

I need to get one value from relationship Many-to-one model in pydantic BaseModel.
How can I do this?
My children class
class Picnic(Base):
__tablename__ = 'picnic'
id = Column(Integer, primary_key=True, autoincrement=True)
city_id = Column(Integer, ForeignKey('city.id'), nullable=False)
city = relationship('City', backref='picnics')
class City(Base):
__tablename__ = 'city'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, unique=True, nullable=False, index=True)
I need to get city name value :
class Picnics(BaseModel):
id: int
# city: str[CityBaseInDB.name] not working
# city: str = Field(source='city.name') not working
# city_name: str not working
class Config:
orm_mode: bool = True
from sqlalchemy import Column, ForeignKey, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, relationship, sessionmaker, Query
from pydantic import BaseModel
Base = declarative_base()
engine = create_engine("sqlite://", echo=True)
class Picnic(Base):
__tablename__ = 'picnic'
id = Column(Integer, primary_key=True, autoincrement=True)
city_id = Column(Integer, ForeignKey('city.id'), nullable=False)
city = relationship('City', backref='picnics')
class City(Base):
__tablename__ = 'city'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, unique=True, nullable=False, index=True)
class PicnicModel(BaseModel):
id: int
# city: str[CityBaseInDB.name] not working
# city: str = Field(source='city.name') not working
city_name: str
class Config:
orm_mode: bool = True
picnic = Picnic(city=City(name='Shenzhen'))
Base.metadata.create_all(engine)
LocalSession = sessionmaker(bind=engine)
db: Session = LocalSession()
db.add(picnic)
db.commit()
q: Query = db.query(Picnic.id, City.name.label('city_name'))
q = q.select_from(Picnic).join(City)
row = q.one_or_none()
model = PicnicModel.from_orm(row)
print(model)
You need to have a pydantic model for your parent model too and put it as the type in the relationship field.
from pydantic import BaseModel
class City(BaseModel):
id: int
name: str
class Config:
orm_mode: bool = True
class Picnics(BaseModel):
id: int
city: City
class Config:
orm_mode: bool = True

How to add a column with a count result to a sqlalchemy query and pass the pydantic check?

Description
Previously, my query returned the contents of a single Stories table. Now I want to add more information: I need to output the prizes_count for each Story. There is no field prizes_count in the Stories table so I made the following query.
db.query(models.Stories, func.count(models.Stories.prizes).label("prizes_count")).join(models.Prizes)\
.group_by(models.Stories.id).all()
But I have two problems with it.
I get validation errors from Pydantic, because this query returns a list of tuples like (<database.models.Stories object at 0x0000026BB0055E20>, 1). I have to insert the prizes_count value into the Stories object or vice versa, pull all fields into the tuple. I can do it manually, of course, but I think there is a better way.
With this query I lose all stories with 0 prizes because my join ignores them.
Code
endpoint
#app.get("/stories/", response_model=List[schemas.StoryFullInfo])
def get_stories(db: Session = Depends(get_db)):
return crud.get_stories(db)
crud
def get_stories(db: Session):
return db.query(models.Stories, func.count(models.Stories.prizes).label("prizes_count")).join(models.Prizes)\
.group_by(models.Stories.id).all()
models
class Stories(Base):
__tablename__ = "stories"
id = Column(INTEGER(unsigned=True), primary_key=True)
title = Column(String(length=128), index=True)
text = Column(String(length=1000))
author_id = Column(INTEGER(unsigned=True), ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
status = Column(TINYINT(unsigned=True), server_default="0")
genre_type = Column(TINYINT(unsigned=True), server_default="0")
likes_count = Column(INTEGER(unsigned=True), server_default="0")
image = Column(Text)
added_to_best_by = Column(INTEGER(unsigned=True))
creation_DT = Column(DateTime, server_default=func.now())
change_status_DT = Column(DateTime)
author = relationship("Users", back_populates="stories")
comments = relationship("Comments", back_populates="story")
prizes = relationship("Prizes", back_populates="story")
class Prizes(Base):
__tablename__ = "prizes"
id = Column(INTEGER(unsigned=True), primary_key=True)
title = Column(String(length=128), nullable=False)
image_id = Column(TINYINT(unsigned=True))
story_id = Column(INTEGER(unsigned=True), ForeignKey("stories.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
user_id = Column(INTEGER(unsigned=True), ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
text = Column(String(length=512), nullable=False)
creation_DT = Column(DateTime, server_default=func.now())
story = relationship("Stories", back_populates="prizes")
author = relationship("Users", back_populates="prizes")
schemas
class StoryBaseInfo(BaseModel):
id: int
title: str = None
author_id: int
class Config:
orm_mode = True
class StoryUpdateInfo(StoryBaseInfo):
#title: str = None
text: str = None
status: int
genre_type: int
likes_count: int
image: str = None
added_to_best_by: int = None
change_status_DT: datetime = None
class Config:
orm_mode = True
class StoryFullInfo(StoryUpdateInfo):
creation_DT: datetime
author: UserBaseInfo
prizes_count: int
class Config:
orm_mode = True
class PrizeBaseInfo(BaseModel):
id: int
story_id: int
class Config:
orm_mode = True
class PrizeInfo(PrizeBaseInfo):
title: str
image_id: int
text: str
creation_DT: datetime
author: UserBaseInfo
story: StoryBaseInfo
class Config:
orm_mode = True
Well, it turns out I was thinking in the wrong direction when I asked this. The problem is solved by the features of SQLAlchemy. I can count prizes by using my configured relationship.
My solution is to add the hybrid property to Stories SQLAlchemy model
class Stories(Base):
__tablename__ = "stories"
id = Column(INTEGER(unsigned=True), primary_key=True)
title = Column(String(length=128), index=True)
text = Column(String(length=1000))
author_id = Column(INTEGER(unsigned=True), ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
status = Column(TINYINT(unsigned=True), server_default="0")
genre_type = Column(TINYINT(unsigned=True), server_default="0")
likes_count = Column(INTEGER(unsigned=True), server_default="0")
image = Column(Text)
added_to_best_by = Column(INTEGER(unsigned=True))
creation_DT = Column(DateTime, server_default=func.now())
change_status_DT = Column(DateTime)
author = relationship("Users", back_populates="stories")
comments = relationship("Comments", back_populates="story")
prizes = relationship("Prizes", back_populates="story")
#hybrid_property
def prizes_count(self):
return len(self.prizes)
And then the following query will satisfy the Pydantic scheme.
def get_stories(db: Session):
return db.query(models.Stories).all()

SqlAlchemy error: Foreign key could not find table

So I'm building an app and I'm trying to save new changes to my database but when I try to commit the changes in the flask using db.session.commit() it returns me the following error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'products.country_id' could not find table 'countries' with which to generate a foreign key to target column 'id'
In my models.py I have the following:
from app import db
from . import db
from datetime import datetime
def now():
return datetime.now()
class Countries(db.Model):
__tablename__ = 'countries'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
code = db.Column(db.String(45))
def __repr__(self):
return f'Id {self.id}'
class Categories(db.Model):
__tablename__ = 'categories'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
def __repr__(self):
return f'Id {self.id}'
class Brands(db.Model):
__tablename__ = 'brands'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
logo = db.Column(db.String(5000))
feed = db.Column(db.String(5000))
feed_type = db.Column(db.String(45))
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
awinmid = db.Column(db.Integer)
def __repr__(self):
return f'Id {self.id}'
class Products(db.Model):
__tablename__ = 'products'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
url = db.Column(db.Text)
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
price = db.Column(db.Float)
currency = db.Column(db.String(45))
discount_price = db.Column(db.Float)
shipping = db.Column(db.Float)
brand_id = db.Column(db.Integer, db.ForeignKey('brands.id'))
Am I doing anything wrong when associating a column in products with a foreign key? This is the first time I encounter this error so I'm really lost on what to do right now.
To fix I just added the schema to the db.ForeignKey and it worked
Example:
db.ForeignKey('products_data.countries.id')
PS:
Not my idea. Just wanted to post the answer in case someone visits the post later with the same problem.
Gord Thompson thanks for the help!
First of all, I don t see any table Categories. Secondly, you copy pasted your schema from the Products table into your Countries one.
PS: By default sqlalchemy gives the tables the name of the class (lower cased). So your __tablename__='products' does nothing actually.
EDIT:
The problem with your code lies in how you set the __table_args__ attribute. You assign an object to it, which by their specifications is wrong.
Take a look at the following example and modify your code accordingly
__table_args__ = ({'schema': 'products_data'})
Also for further reference, take a look at this https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/table_config.html

How to get current user in a models of Pylons?

I have model like this:
class CreatedMixin(DeclarativeBase):
__abstract__ = True
#declared_attr
def updated_by(cls):
return Column(Integer, ForeignKey('user.user_id',
onupdate="cascade", ondelete="restrict"),
onupdate=CURRENT_USER_ID)
updated_at = Column(DateTime, nullable=False, default=dt.now(),
onupdate=dt.now())
And auth with repoze.what-quickstart. How to get CURRENT_USER_ID?
I think you could do the following
identity = request.environ.get('repoze.who.identity')
and the gettin the user id
id = identity['user'].id