issue creating an user with calculated field fastAPI - python

I have a simple API to insert data inside an object type dictionary, my issue is when I try to save a calculated field. Example code:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
users = {}
class User(BaseModel):
name: str
email: str
calculated: float
#app.post('/user-create/{id_user}')
def creating_an_user(id_user:int,user:User):
calculated = 1+2+3*2
if id_user in users:
return {"Error":"User ID already exists."}
users[id_user] = {
"name":user.name,
"email":user.email,
"calculated":user.calculated #should I need to put just like that ?
}
return users[id_user]
Obviously I receive an error, I think because my method is waiting a manually insert "calculated" field but is not:
TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
I am lost in this concern, can anybody help me on this?

Related

How to hide field input in FastAPI swagger UI but still accessible if user choose to input it?

How do i hide a field in the FastAPI UI, yet i am able to pass in information if needed?
I tried adding '_' to the field name and added alias to map it, however the alias input is not registered.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Subject(BaseModel):
score: int
_difficulty: str = Field('low', alias='difficulty')
Input data = {'score'= 70, 'difficulty'='high'}
When I post the above data, Subject._difficulty still show as 'low'

How to partially update data with FastAPI and PyDantic BaseModel using PATCH

I'd like to partly update database via PATCH method in FastAPI. I use Postgres as my database, Postman to test.
I followed the example on FastAPI document, link: https://fastapi.tiangolo.com/tutorial/body-updates/#partial-updates-with-patch
I use GET to fetch the original data in DB, copy the content to body raw json, then change the part where I need to update and choose PATCH, click send in Postman, an error occurs: main.Product() argument after ** must be a mapping, not Product
What is the right approach to PATCH data? I omitted the code to connect to Postgres using psycopg2
from fastapi import FastAPI, Response, status, HTTPException, Path
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Product(BaseModel):
name: str
price: float
inventory: int
#app.get("/posts/{id}")
def get_a_post(id: int = Path(None, title='Prod ID')):
cursor.execute('''SELECT * FROM public.products WHERE ID = %s''',(str(id),))
post = cursor.fetchone()
if not post:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail=f"product with id {id} was not found!")
return post
#app.patch("/posts/{id}", response_model=Product)
def patch_posts(id: int, post: Product):
stored_data = post
stored_model = Product(**stored_data)
update_data = post.dict(exclude_unset=True)
updated_data = stored_model.copy(update=update_data)
post = jsonable_encoder(updated_data)
return{"partially updated product": post}
Looks like your issue is caused by trying to get the key/value pairs via **stored_data, but that variable is of type Product.
In your patch_posts function, change stored_data = post to stored_data = post.dict().
Using the example you provided:
import uvicorn
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Product(BaseModel):
name: str
price: float
inventory: int
#app.patch("/posts/{id}", response_model=Product)
def patch_posts(id: int, post: Product):
stored_data = post.dict()
stored_model = Product(**stored_data)
update_data = post.dict(exclude_unset=True)
updated_data = stored_model.copy(update=update_data)
post = jsonable_encoder(updated_data)
return post

Pydantic allows weird types on field with nested model

Currently i have this script:
from typing import List
from uuid import uuid4, UUID
from pydantic import BaseModel, Field
class Id(BaseModel):
value:UUID = Field(default_factory=uuid4)
class User(BaseModel):
id:Id = Field(default_factory=Id)
roles:List[str] = ['student']
class Project(BaseModel):
id:Id = Field(default_factory=Id)
creator:User
id = Id()
print(id)
user = User()
print(user)
project = Project(creator={})
print(project)
The script should run as it is
When i instantiate a project, pydantic allows me to put non User objects, like dicts, strings, Id(objects of type Id) and some others, but i feel this is not expected. The weirdest thing is the fields are auto filled with an object of the correct type when a bad value is passed
what is happening here? Thanks in advance!

Cannot update with FastAPI

When I run put from FastAPI docs with the following code, I get 500 Error: Internal Server Error`` and the terminal shows AttributeError: 'Test' object has no attribute ' items'```` and the terminal shows AttributeError: 'Test' object has no attribute 'items'.
I can create, get, delete, etc. normally, but for some reason I can't just put.
Also, if I try putting in a non-existent ID, I get a 404 error normally.
I would appreciate it if you could tell me more about it.
router
#router.put('/{id}', status_code=status.HTTP_202_ACCEPTED)
def update(id, request:schemas.Test ,db:Session = Depends(get_db)):
test= db.query(models.Test).filter(models.Test.id == id)
if not test.first():
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f'Test with id {id} is not found')
test.update(request)
db.commit()
return 'updated'
models
from sqlalchemy import Column, Integer, String
from db import Base
class Test(Base):
__tablename__ = 'tests'
id = Column('id',Integer, primary_key=True,index=True)
title = Column('title',String(256))
schemas
from pydantic import BaseModel
class Test(BaseModel):
title:str
SQLAlchemy's update method on objects expects a dict. You're giving it a pydantic base model.
Pydantic's BaseModel supports a dict() method to return the object's properties as a dictionary. You can give this dictionary to your update method instead:
test.update(request.dict())
Also be aware that the request name is used for other things in FastAPI and might not be a good name for a route parameter. The id name is also a reference to a built-in Python function, so you'll usually want to name it test_id or something similar instead.

FastAPI - GET Request with Pydantic List field

I'm new to FastAPI (migrating from Flask) and I'm trying to create a Pydantic model for my GET route:
from fastapi import APIRouter,Depends
from pydantic import BaseModel
from typing import Optional,List
router = APIRouter()
class SortModel(BaseModel):
field: Optional[str]
directions: List[str]
#router.get("/pydanticmodel")
def get_sort(criteria: SortModel = Depends(SortModel)):
pass #my code for handling this route.....
When I'm running curl -X GET http://localhost:XXXX/pydanticmodel?directions=up&directions=asc&field=id I'm getting 422 Unprocessable Entity: {"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}
But if I'm changing directions:List[str] -> directions: str I'm getting 200 OK with directions="asc".
What is the reason that str works for query param and List[str] does not? What am I doing wrong?
Thanks.
It is not, as yet, possible to use a GET request with Pydantic List field as query parameter. When you declare a List field in the Pydantic model, it is interpreted as a request body parameter, instead of a query one (regardless of using Depends()—you can check that through Swagger UI docs at http://127.0.0.1:8000/docs, for instance). Additionally, as you are using a GET request, even if you added the List of directions in the body and attempted sending the request, it wouldn't work, as a POST request would be required for that operation.
The way to do this is to either define the List of directions explicitly with Query as a separate parameter in your endpoint, or implement your query parameter-parsing in a separate dependency class, as described here. Remember again to define the List field explicitly with Query, so that directions can be interpreted as a query parameter and appear multiple times in the URL (in others words, to receive multiple values). Example:
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
class SortModel:
def __init__(
self,
field: Optional[str],
directions: List[str] = Query(...)
):
self.field = field
self.directions = directions
router = APIRouter()
#router.get("/")
def send_user(criteria: SortModel = Depends()):
return criteria
The above can be re-written using the #dataclass decorator, as shown below:
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from dataclasses import dataclass
#dataclass
class SortModel:
field: Optional[str]
directions: List[str] = Query(...)
router = APIRouter()
#router.get("/")
def send_user(criteria: SortModel = Depends()):
return criteria
I'm running into the same issue. The following solution will work, but it isn't really what I want however maybe it's good enough for you:
from fastapi import APIRouter,Depends, Query
from pydantic import BaseModel
from typing import Optional,List
router = APIRouter()
class SortModel(BaseModel):
field: Optional[str]
#router.get("/pydanticmodel")
def get_sort(criteria: SortModel = Depends(SortModel), directions: List[str] = Query(...)):
pass #my code for handling this route.....
It's not a Pydantic or FastAPI problem.
If you want to send an array with curl you should use -d flag.
In: curl -X GET "http://127.0.0.1:8000/pydanticmodel?field=123" -d "[\"string\"]"
Out: {"field":"123","directions":["string"]}
Now your code should work perfectly.

Categories