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 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'
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
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!
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.
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.