Currently i am struggling to pass mongoengine model in a post call by fastapi which only accepts pedantic models. But i have written my mongo models in mongoengine ODM. And if i try to pass these mongoengine models in apis then it throws error
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
#app.post("/items/")
async def create_item(item: Item):
return item
Import mongoengine and uvicorn
from mongoengine import *
import uvicorn
Create a MongoDB connection
DATABASE_URI = "mongodb://localhost:27017"
db=DATABASE_URI+"/mydatabase"
connect(host=db)
Declare a MongoEngine Class similar to your Item Class
class MongoItem(Document):
name=StringField()
description= StringField()
price=FloatField()
tax= FloatField()
Add a one line of code to store a item in MongoDB
MongoItem(**item.dict()).save()
The complete code of main.py:
from fastapi import FastAPI
from pydantic import BaseModel
from mongoengine import *
import uvicorn
DATABASE_URI = "mongodb://localhost:27017"
db=DATABASE_URI+"/mydatabase"
connect(host=db)
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
class MongoItem(Document):
name=StringField()
description= StringField()
price=FloatField()
tax= FloatField()
app = FastAPI()
#app.post("/items/")
async def create_item(item: Item):
mongoitem=MongoItem(**item.dict()).save()
return item
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
Or even shorter using a DynamicDocument Class
class MongoItem(DynamicDocument):
pass
Related
I have this following class:
class Quiz(BaseModel):
question: str
subject: str
choice: str = Query(choices=('eu', 'us', 'cn', 'ru'))
I can render the form bases on this class like this
#api.post("/postdata")
def post_data(form_data: Quiz = Depends()):
return form_data
How can I display a drop down list for choice field ?
Option 1
Use literal values. Literal type is a new feature of the Python standard library as of Python 3.8 (prior to Python 3.8, it requires the typing-extensions package) and is supported by Pydantic. Example:
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import Literal
app = FastAPI()
class Quiz(BaseModel):
question: str
subject: str
choice: Literal['eu', 'us', 'cn', 'ru'] = 'us'
#app.post('/submit')
def post_data(data: Quiz = Depends()):
return data
Option 2
Use Enums (also, see Python's enum module, as well as FastAPI's documentation on Predefined values). By having your Enum sub-class inheriting from str, the API docs will be able to know that the values must be of type string and will be able to render correctly. Example:
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from enum import Enum
app = FastAPI()
class Country(str, Enum):
eu = 'eu'
us = 'us'
cn = 'cn'
ru = 'ru'
class Quiz(BaseModel):
question: str
subject: str
choice: Country = Country.us
#app.post('/submit')
def post_data(data: Quiz = Depends()):
return data
When using FastApi with a pydantic model at the reponse model, I found that the uuid are always returned lowercase by the http response. Is there any standard way to return them upper cased?
from fastapi import FastAPI
from pydantic import BaseModel
from uuid import UUID
app = FastAPI()
class Test(BaseModel):
ID: UUID
#app.get("/test", response_model=Test)
async def test():
id_ = uuid.uuid4()
return Test(ID=id_)
When making the request the returned uuid will be in lowercased.
from requestr
a = requests.get("http://localhost:800/test").text # you ir
# a -> '{"ID":"fffc0b5b-8e8d-4d06-b910-2ae8d616166c"}' # it is lowercased
The only somewhat hacky way I found to return them uppercased is overwriting the uuid class __str__ method or sub-classing uuid:
What I tried (and works):
# use in main.py when importing for first time
def newstr(self):
hex = '%032x' % self.int
return ('%s-%s-%s-%s-%s' % (hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])).upper()
uuid.UUID.__str__ = newstr
But I was wondering if there is a standard way of doing this without modifying the original class, maybe a post process in pydantic or a setting in FastApi.
You can define custom json_encoders:
class Test(BaseModel):
ID: UUID
class Config:
json_encoders = {
UUID: lambda val: str(val).upper()
}
I want to include a custom class into a route's response. I'm mostly using nested pydantic.BaseModels in my application, so it would be nice to return the whole thing without writing a translation from the internal data representation to what the route returns.
As long as everything inherits from pydantic.BaseModel this is trivial, but I'm using a class Foo in my backend which can't do that, and I can't subclass it for this purpose either. Can I somehow duck type that class's definition in a way that fastapi accepts it? What I have right now is essentially this:
main.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Foo:
"""Foo holds data and can't inherit from `pydantic.BaseModel`."""
def __init__(self, x: int):
self.x = x
class Response(BaseModel):
foo: Foo
# plus some more stuff that doesn't matter right now because it works
#app.get("/", response_model=Response)
def root():
return Response(foo=Foo(1))
if __name__ == '__main__':
import uvicorn
uvicorn.run("main:app") # RuntimeError
It's not documented, but you can make non-pydantic classes work with fastapi. What you need to do is:
Tell pydantic that using arbitrary classes is fine. It
will try to jsonify them using vars(), so only straight forward
data containers will work - no using property, __slots__ or stuff like that[1].
Create a proxy BaseModel, and tell Foo to offer it if someone
asks for its schema - which is what fastapis OpenAPI pages do.
I'll just assume that you want them to work too since they're
amazing.
main.py
from fastapi import FastAPI
from pydantic import BaseModel, BaseConfig, create_model
app = FastAPI()
BaseConfig.arbitrary_types_allowed = True # change #1
class Foo:
"""Foo holds data and can't inherit from `pydantic.BaseModel`."""
def __init__(self, x: int):
self.x = x
__pydantic_model__ = create_model("Foo", x=(int, ...)) # change #2
class Response(BaseModel):
foo: Foo
#app.get("/", response_model=Response)
def root():
return Response(foo=Foo(1))
if __name__ == '__main__':
import uvicorn
uvicorn.run("main:app") # works
[1] If you want more complex jsonification, you need to provide it to the Response class explicitly via Config.json_encoders.
Here is a full implementation using a subclass with validators and extra schema:
from psycopg2.extras import DateTimeTZRange as DateTimeTZRangeBase
from sqlalchemy.dialects.postgresql import TSTZRANGE
from sqlmodel import (
Column,
Field,
Identity,
SQLModel,
)
from pydantic.json import ENCODERS_BY_TYPE
ENCODERS_BY_TYPE |= {DateTimeTZRangeBase: str}
class DateTimeTZRange(DateTimeTZRangeBase):
#classmethod
def __get_validators__(cls):
yield cls.validate
#classmethod
def validate(cls, v):
if isinstance(v, str):
lower = v.split(", ")[0][1:].strip().strip()
upper = v.split(", ")[1][:-1].strip().strip()
bounds = v[:1] + v[-1:]
return DateTimeTZRange(lower, upper, bounds)
elif isinstance(v, DateTimeTZRangeBase):
return v
raise TypeError("Type must be string or DateTimeTZRange")
#classmethod
def __modify_schema__(cls, field_schema):
field_schema.update(type="string", example="[2022,01,01, 2022,02,02)")
class EventBase(SQLModel):
__tablename__ = "event"
timestamp_range: DateTimeTZRange = Field(
sa_column=Column(
TSTZRANGE(),
nullable=False,
),
)
class Event(EventBase, table=True):
id: int | None = Field(
default=None,
sa_column_args=(Identity(always=True),),
primary_key=True,
nullable=False,
)
as per #Arne 's solution you need to add your own validators and schema if the Type you are using has __slots__ and basically no way to get out a dict.
Link to Github issue: https://github.com/tiangolo/sqlmodel/issues/235#issuecomment-1162063590
I have a Pydantic model as below
class Student(BaseModel):
name:str
age:int
With this setup, I wish to get the OpenAPI schema as following,
So, how can I use the Pydantic model to get the from query parameter in FastAPI?
You can do something like this,
from fastapi import FastAPI, Depends
from pydantic import BaseModel
app = FastAPI()
class Student(BaseModel):
name: str
age: int
#app.get("/")
def read_root(student: Student = Depends()):
return {"name": student.name, "age": student.age}
Also, note that the query parameters are usually "optional" fields and if you wish to make them optional, use Optional type hint as,
from fastapi import FastAPI, Depends
from typing import Optional
from pydantic import BaseModel
app = FastAPI()
class Student(BaseModel):
name: str
age: Optional[int]
#app.get("/")
def read_root(student: Student = Depends()):
return {"name": student.name, "age": student.age}
This is continue to this question.
I have added a model to get query params to pydantic model
class QueryParams(BaseModel):
x: str = Field(description="query x")
y: str = Field(description="query y")
z: str = Field(description="query z")
#app.get("/test-query-url/{test_id}")
async def get_by_query(test_id: int, query_params: QueryParams = Depends()):
print(test_id)
print(query_params.dict(by_alias=True))
return True
it is working as expected but description(added in model) is not reflecting in swagger ui
But if same model is used for request body, then description is shown in swagger
Am I missing anything to get the description for QueryParams(model) in swagger ui?
This is not possible with Pydantic models
The workaround to get the desired result is to have a custom dependency class (or function) rather than the Pydantic model
from fastapi import Depends, FastAPI, Query
app = FastAPI()
class CustomQueryParams:
def __init__(
self,
foo: str = Query(..., description="Cool Description for foo"),
bar: str = Query(..., description="Cool Description for bar"),
):
self.foo = foo
self.bar = bar
#app.get("/test-query/")
async def get_by_query(params: CustomQueryParams = Depends()):
return params
Thus, you will have the doc as,
References
Validate GET parameters in FastAPI--(FastAPI GitHub) It seems like there is less interest in extending the Pydantic model to validate the GET parameters
Classes as Dependencies--(FastAPI Doc)
This worked for me
from fastapi import Depends, FastAPI, Query
#app.post("/route")
def some_api(
self,
query_param_1: float = Query(None, description="description goes here", ),
query_param_2: float = Query(None, description="Param 2 does xyz"),
):
return "hello world"
The accepted answer refers to the use of custom dependencies using FastAPI classes as dependencies to define the query parameters in bulk and while I think it works great, I feel the using dataclasses would be better in this case and reduces the code duplication as the __init__ will be generated automatically.
Normal class as dependency
class QueryParams:
def __init__(self,
x: Query(
None, description="Arg1", example=10),
y: Query(
None, description="Arg2", example=20)
):
self.x = x
self.y = y
While for lesser number of query params it would be just fine to do it this way but if the number of args is large as it was for me for one of my api endpoints, this quickly becomes cumbersome.
Using dataclass as a dependency
#dataclass
class QueryParams:
x: Query(None, description="Arg1", example=10)
y: Query(None, description="Arg2", example=20)
Plus you will also have added goodies of dataclasses if you need more complex functioanlities.