Is it possible to use response_model data in read_hero function before returning it - fastapi

I am reading hero with it's foreign key data from database and then return it using response_model. On read_hero function bellow, in this line hero = session.get(Hero, hero_id) hero is different from what this function return when I print it.
hero and team schema:
class HeroBase(SQLModel):
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
sponsor: Sponsor = Field(sa_column=Column(JSON(), nullable=False))
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
class Hero(HeroBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
team: "Team" = Relationship(back_populates="heroes")
class TeamBase(SQLModel):
name: str = Field(index=True)
headquarters: str
class Team(TeamBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
heroes: List["Hero"] = Relationship(back_populates="team")
class HeroReadWithTeam(HeroRead):
team: "TeamRead" = None
#app.get("/heroes/{hero_id}", response_model=HeroReadWithTeam)
def read_hero(*,hero_id: int, session: Session = Depends(get_session)):
hero = session.get(Hero, hero_id)
print(hero)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
Line: print(hero) print out secret_name='clement' name='John' team_id=1 age=14 id=2
But the function it self return something like this because of response_model:
{
"name": "John",
"secret_name": "clement",
"age": 14,
"team_id": 1,
"id": 2,,
"team": {
"name": "Orlando Pirates",
"headquarters": "Orlando",
"id": 1
}
}
Is it possible to have this data inside read_hero function and use it before returning it.
You can refer from this for full code: https://sqlmodel.tiangolo.com/tutorial/fastapi/relationships/

I think you can do something like:
hero_dict = HeroReadWithTeam.from_orm(hero).dict
print(hero_dict)

Related

retrofit 2 post - getting internal server error (500)

I'm trying to use retrofit2 with Koltin in Android Studio as part of jetpack compose application. I'm sending a POST and keep getting error 500. I don't have access to the server code so I'm trying to figure out what am I doing wrong.
This is the interface I have declared for building the retrofit object:
I tried three different ways of declaring the POST endpoint.
#Singleton
interface IsrPayService {
#Headers ("Content-Type: application/json")
#POST("v3/driver/new-credit-driver")
suspend fun signUp(#Body user: UserDriver): Call\<WsError?\>
#Headers (
"Content-Type: application/json",
"Accept: application/json")
#POST("v3/driver/new-credit-driver")
suspend fun signup2(#Body user: UserDriver): retrofit2.Response<WsError>
#FormUrlEncoded
#POST("v3/driver/new-credit-driver")
suspend fun signupUrlEncoded(
#Field("firstName") firstName: String,
#Field("lastName") lastName: String): retrofit2.Response<WsError>
}
The data I am trying to send is UserDriver and I declared all the internal data classes below:
data class UserDriver(
#SerializedName("firstName") val firstName: String = "default",
#SerializedName("lastName") val lastName: String = "default",
#SerializedName("civilId") val civilId: String = "default",
#SerializedName("vehicleLicensingNumber") val vehicleLicensingNumber: String = "default",
#SerializedName("vehicleManufacturer") val vehicleManufacturer: String = "default",
#SerializedName("vehicleModel") val vehicleModel: String = "default",
#SerializedName("vehicleManufactureYear") val vehicleManufactureYear: String = "1973",
#SerializedName("counterModel") val counterModel: String = "default",
#SerializedName("authorizedEmployerNumber") val authorizedEmployerNumber: String = "default",
#SerializedName("bankAccountId") val bankAccountId: String = "default",
#SerializedName("bankAccountBranch") val bankAccountBranch: Int = 0,
#SerializedName("bankId") val bankId:Int = 123456,
#SerializedName("email") val email: String = "default",
#SerializedName("dob") val dob: DateIndicator = DateIndicator(date = 3, month = 4, year = 2023),
#SerializedName("address") val address: Address = Address (Coordinates(0,0),"מודיעין","טשרניחובסקי","12"),
#SerializedName("phoneNumber") val phoneNumber: String = "default",
#SerializedName("driverLicenseId") val driverLicenseId: Int = 0,
#SerializedName("civilIdPhoto") val civilIdPhoto: String = "default",
#SerializedName("driverLicensePhoto") val driverLicensePhoto: String = "default",
#SerializedName("signaturePhoto") val signaturePhoto: String = "default"
)
data class DateIndicator(
#SerializedName("date")
#Expose
val date: Int,
#SerializedName("month")
#Expose
val month: Int,
#SerializedName("year")
#Expose
val year: Int
)
I think #Expose is not required but tried it just in case..
data class Address (
#SerializedName("coordinates")
#Expose
val coordinates: Coordinates,
#SerializedName("city")
#Expose
val city: String,
#SerializedName("street")
#Expose
val street: String,
#SerializedName("number")
#Expose
val number: String
)
data class Coordinates (
#SerializedName("latitude")
#Expose
val latitude: Int,
#SerializedName("longitude")
#Expose
val longitude: Int
)
I tried to methods of getting the response:
suspend fun driverSignUp(user: UserDriver, onResult: (WsError?) -> Unit) {
try {
ws.signUp(user = user). enqueue (
object: Callback<WsError?> {
override fun onResponse(call: Call<WsError?>, response: Response<WsError?>) {
Log.d("driverSignUp",
"onResponse: response.isSuccessful = ${response.isSuccessful}")
var wsError: WsError? = null
wsError = if(!response.isSuccessful){
WsError(
body = "",
isError = true,
error = yz.learning.isrpaytest.model.Error(
errorCode = response.code(),
errorMessage = ErrorMessage(
enUs = response.message(),
heIl = response.message())))
} else {
response.body()
}
onResult(wsError)
}
override fun onFailure(call: Call<WsError?>, t: Throwable) {
Log.d("driverSignUp", "onFailure: ")
onResult(null)
}
}
)
} catch (exception: Exception) {
Log.d("driverSignUp", "driverSignUp exception: ${exception.message}")
onResult(
WsError(
body = "",
isError = true,
error = yz.learning.isrpaytest.model.Error(
errorCode = 0,
errorMessage = ErrorMessage(
enUs = exception.message!!,
heIl = exception.message!!)))
)
}
}
suspend fun driverSignUp2(user: UserDriver): retrofit2.Response<WsError>{
return ws.signup2(user)
}
I don't understand why I keep getting Internal server error. I have a feeling I have to send the data as a JSON string and not as an Object but as far as I understand this is supposed to be automatically using the gson converter, no?
I can try a simpler endpoint, but I think I will end up with the same problem.
I will appreciate any help since I'm stuck with this issue for a couple of days.
Thanks,
Yariv

Is there a way to paginate related data in sqlmodel on the frontend

The sqlmodel docs gives an example of two classes
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
heroes: List["Hero"] = Relationship(back_populates="team")
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
team: Optional[Team] = Relationship(back_populates="heroes")
I could get a Team object using the following code example
def get_team():
with Session(engine) as session:
statement = select(Team).where(Team.name == "avengers")
result = session.exec(statement)
avengers = result.one()
return avengers
and doing avengers.heroes should return a list of all heroes related to that object but What if the list contains thousands of items? is there a way to paginate this without having to make a separate query to the heroes table by myself?
To do this, you have to work with the underlying SQLAlchemy library, which you can do using the sa_relationship_kwargs argument to Relationship.
As mentioned in this answer, if you specify the relationship as dynamic, you can then paginate it. Using SQLModel, it looks something like this:
class Team(SQLModel, table=True):
...
heroes: List["Hero"] = Relationship(
back_populates="team",
sa_relationship_kwargs={"order_by": "Hero.name", "lazy": "dynamic"},
)
# When calling it
first_ten_heroes = team.heroes[:10] # or team.heroes.limit(10)

Not able to map data from sql join to pydantic models in fastapi

I am trying to map data from sql join of two tables to pydantic model, but getting null value
models.py
class Countries(BaseModel):
country_name: str = Field(None, alias="country_name")
class Tools(BaseModel):
tool_name:str = Field(None, alias="tool_name")
tool_id: int = Field(None, alias="tool_id")
class ShowData(BaseModel):
countries:Countries = Field(None, alias="countries")
tools:Tools = Field(None, alias="tools")
class Config:
orm_mode = True
repository.py
def test(db: Session = Depends(get_db)):
statement = select(Tool.tool_name, UserCountryToolAccess.tool_id, Country.country_name).where(Tool.tool_id == UserCountryToolAccess.tool_id, Country.country_id == UserCountryToolAccess.country_id)
results = db.exec(statement).fetchall()
print(results)
return results
This is the data I am receiving in repository.py [('pdf compressor', 1, 'United States of America'), ('image resizer', 4, 'United Kingdom'), ('cost budgeting', 2, 'Russia'), ('scenario planner', 5, 'Germany'), ('cropping image', 1, 'United States of America'), ('leave dashboard', 3, 'Russia')]
test.py
#router.get("/test",tags=['test'],response_model=schemas.ShowData)
def get_user(db: Session = Depends(get_db)):
result = repository.test(db)
return result
I need data in the below json structure
{
"countries": {
"country_name": "India"
},
"tools": {
"tool_id": 1,
"tool_name": "pdf compressor"
}
}

Flask restx api model not showing model data

I have a model as follows:
class Menu(db.Model):
itemId = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(255),index=True)
price = db.Column(db.Numeric)
description = db.Column(db.String(255),index=True)
image = db.Column(db.LargeBinary)
restaurantId = db.Column(db.Integer, db.ForeignKey('restaurants.id'))
createdOn = db.Column(db.DateTime,server_default=db.func.now())
status = db.Column(db.Integer,server_default="1")
orders = db.relationship('Orders', backref='menu', lazy='dynamic')
def __repr__(self):
return '<Menu of restaurant id {}>'.format(self.restaurantId)
And I have the following api model corresponding to it:
menu_model = api.model('Menu',
{'itemId':fields.Integer(),
'name':fields.String(),
'price':fields.Float(),
'description':fields.String(),
'restaurantId':fields.Integer(),
'createdOn:':fields.DateTime(),
'status':fields.Integer()})
The problem is that even though the createdOn values are correctly generated on DB side, the response shows the createdOn field as null. What could be the reason?
"menu":
{
"itemId": 1,
"name": "Menu item",
"price": 30.0,
"description": "Menu item description",
"restaurantId": 1,
"createdOn:": null,
"status": 1
}
this will accept the desired output. The first parameter is a label, not part of the json
menus = api.model(
"menu_item",
{
'itemId':fields.Integer(),
'name':fields.String(),
'price':fields.Float(),
'description':fields.String(),
'restaurantId':fields.Integer(),
'createdOn:':fields.DateTime(),
'status':fields.Integer()
},
)
menu_model = api.model(
"Menu",
{
"menu": Nested(menus),
},
)

Looping through a list using foreach

I have a project that requires me to populate User information along with their personal information.
So far, I was able to loop through a list of users and personal information, but I was not able to populate null values.
For example:
public List<UserDetailModel> UserInformation()
{
List<UserDetailModel> userdetails = new List<UserDetailModel>();
var user = _context.User.Where(x => x.Id > 0).ToList()
foreach(var item in user)
{
var personaldetails = _context.PersonalDetails.Where(x => item.PId == x.PId).ToList();
foreach (var item2 in personaldetails)
{
UserDetailModel userModel = new UserDetailModel();
userModel.UserId = item.UserId;
userModel.Name = item.UserName;
userModel.PhoneNumber = item.Number;
userModel.CreditCardNumber = item2.CCNumber;
userModel.SIN = item2.SinNumber;
userdetails.Add(userModel);
}
}
return userdetails;
}
What I'm expecting is:
"userId": 1,
"name": "john"
"phoneNUmber": 123-123-1234,
"creditCardNumber": 44455544445554545,
"sin": 9589898568
"userId": 1,
"name": "john"
"phoneNUmber": ,
"creditCardNumber": 44455544445554545,
"sin": 9589898568
"userId": 1,
"name": "john"
"phoneNUmber": 123-123-1234,
"creditCardNumber": ,
"sin": 9589898568
"userId": 1,
"name": "john"
"phoneNUmber": 123-123-1234,
"creditCardNumber": 44455544445554545,
"sin":
But what I'm getting with the above code is:
"userId": 1,
"name": "john"
"phoneNUmber": 123-123-1234,
"creditCardNumber": 44455544445554545,
"sin": 9589898568
How can I get all users along with their null values?
I guess the logic was not implemented correctly, Two loops not required seems,
Public List<UserDetailModel> UserInformation(){
List<UserDetailModel> userdetails = new List<UserDetailModel>();
var user = (from user in _context.User
join personal in _context.PersonalDetails
on user.PId equals personal.PId
Where user.Id>0).ToList();
foreach(var item in user)
{
UserDetailModel userModel = new UserDetailModel();
userModel.UserId = item.UserId;
userModel.Name = item.UserName;
userModel.PhoneNumber = item.Number;
userModel.CreditCardNumber = item2.CCNumber;
userModel.SIN = item2.SinNumber;
userdetails.Add(userModel);
}
So when personal details null it won't enter in the loop.

Resources