I am using these classes as part of a FastAPI response:
class SomeRules(str, Enum):
a_rule = "something"
b_rule = "something_else"
class RuleChooser(BaseModel):
rule: SomeRules = List[SomeRules]
In the JSON response, I want to get rule as:
{
rule: ["something", "something_else"]
}
How do I achieve this?
Right now I am getting the output as:
rule: type.literal["something", "something_else"]
or
literal.type["A", "B", "C"]
I have tested multiple versions and none of them return the desired result.
rules = Literal["A", "B", "C"]
rule: SomeRules = Somerules.rules
Any help will be appreciated.
I am using Pydantic version 1.9.1
The simplest, straightforward answer is to fix your definition for rule.
This
class RuleChooser(BaseModel):
rule: SomeRules = List[SomeRules]
says that rule is of type SomeRules and its value is a typing.List of SomeRules...which is definitely wrong because the value doesn't match the type. If you want rule to simply contain the string values of the enums, you can type it as List[str] then get all the values of the enums:
from pydantic import Field
class SomeRules(str, Enum):
a_rule = "something"
b_rule = "something_else"
class RuleChooser(BaseModel):
rule: List[str] = Field(default=[rule.value for rule in SomeRules])
#app.get("/")
async def root():
return RuleChooser()
$ curl http://127.0.0.1:8000
{"rule":["something","something_else"]}
Now, I'm guessing you are using the actual enum members in your app (not their string values), and you just want RuleChooser.rule to be strings only as part of the JSON response. Then, you need to again fix the definition of rule to:
from pydantic import Field
class RuleChooser(BaseModel):
rule: List[SomeRules] = Field(default=list(SomeRules))
which says that rule is of type typing.List of SomeRules, and its value are all the members of that Enum.
If you print an instance of RuleChooser().rule, you'll get:
[<SomeRules.a_rule: 'something'>, <SomeRules.b_rule: 'something_else'>]
But as part of the JSON response, FastAPI can handle the conversion from enum to string, and you'll get:
#app.get("/")
async def root():
return RuleChooser()
$ curl http://127.0.0.1:8000
{"rule":["something","something_else"]}
which is the same as what you wanted.
Related
I'm trying to use pydantic and mypy at the same time and I get an error when I try to pass a pydantic type as a function parameter.
What I want to do is the following :
from typing import List
from pydantic import BaseModel
from typing import Generic, TypeVar
from pydantic.generics import GenericModel
DataT = TypeVar("DataT")
class Result(GenericModel, Generic[DataT]):
pageItems: DataT
class TypeA(BaseModel):
id: str
amount: float
class TypeB(BaseModel):
id: str
text: str
def generic_call_to_api(result_type: BaseModel, api_result: dict):
res = Result[List[result_type]](**api_result)
return res
a = {"pageItems": [{"id": "abc", "text": "def"}]}
print(generic_call_to_api(result_type=TypeB, api_result=a))
#>>>>>> pageItems=[TypeB(id='abc', text='def')]
print(generic_call_to_api(result_type=TypeA, api_result=a))
#>>>>>> pageItems -> 0 -> amount field required (type=value_error.missing)
Depending of what I give in result_type I want pydantic to be able to "construct" the good type of variable.
What I tried was using TypeVar[BaseModel] or Type[BaseModel] but I still get this mypy error :
error: Variable "result_type" is not valid as a type
for the line : res = Result[List[result_type]](**api_result)
Surprinsigly the code is working though, depending on what I pass as an argument pydantic is able to correctly parse the requests response.
Any hint on how I can make it work ?
I am trying to set Optional some params in a PUT method from my API.
Using fastAPI and mongodb I've build a simple API to insert students and delete the ones, now I am looking to allow me update the entries but not mandatory "params".
I've checked this Fastapi: put method and looks like something I am looking for mongodb.
And this response from art049 looks similar what I already have in my #api_router.put('/update-student/{id}', tags=['Student']) MongoDb with FastAPI
As example for my question here I have this structure:
Models:
class Student(BaseModel):
age:int
name:str
address:str
class UpdateStudent(BaseModel):
age: Optional[int] = None
name: Optional[str] = None
address: Optional[str] = None
Schemas:
def serializeDict(a) -> dict:
return {**{i:str(a[i]) for i in a if i=='_id'},**{i:a[i] for i in a if i!='_id'}}
def serializeList(entity) -> list:
return [serializeDict(a) for a in entity]
Routes:
#api_router.post('/create-student', tags=['Students'])
async def create_students(student: Student):
client.collegedb.students_collection.insert_one(dict(student))
return serializeList(client.collegedb.students_collection.find())
Also I know I can update the entry without problems in this way:
#api_router.put('/update-student/{id}', tags=['Student'])
async def update_student(id,ustudent: UpdateStudent):
client.collegedb.students_collection.find_one_and_update({"_id":ObjectId(id)},{
"$set":dict(ustudent)
})
return serializeDict(client.collegedb.students_collection.find_one({"_id":ObjectId(id)}))
My problem as you can see with my Models I need a way to validate which params are modified and update the ones only:
If right now I Update for example the age only; since the other params are not required, name and address will be stored as None (null actually) because I set this in my model.
Maybe I can do something like this:
if ustudent.age != None:
students_collection[ObjectId(id)] = ustudent.age
if ustudent.name != None:
students_collection[ObjectId(id)] = ustudent.name
if ustudent.address != None:
students_collection[ObjectId(id)] = ustudent.address
I know I can use this in a simple dictionary but never tried before in a collection in mongodb since pydantic not support ObjectId for iterations and that's why serializeDict was created.
I will really appreciate if somebody can give a hint with my concern
You can use exclude_unset=True argument as suggested in FastAPI documentation:
#api_router.put('/update-student/{id}', tags=['Student'])
async def update_student(id,ustudent: UpdateStudent):
client.collegedb.students_collection.find_one_and_update({"_id":ObjectId(id)},{
"$set":ustudent.dict(exclude_unset=True)
})
return serializeDict(client.collegedb.students_collection.find_one({"_id":ObjectId(id)}))
Here is the documentation for exporting Pydantic models.
I have a function generating a dict template. This function consists of several generators and requires one parameter (i.e., carrier) and has many optional parameters (keyword arguments - **kwargs).
def main_builder(carrier, **params):
output = SamplerBuilder(DEFAULT_JSON)
output.generate_flight(carrier)
output.generate_airline_info(carrier)
output.generate_locations()
output.generate_passengers()
output.generate_contact_info()
output.generate_payment_card_info()
output.configs(**params)
result = output.input_json
return result
# example of function call
examplex = main_builder("3M", proxy="5.39.69.171:8888", card=Visa, passengers={"ADT":2, "CHD":1}, bags=2)
I want to deploy this function to FastAPI endpoint. I managed to do it for carrier but how can I set **kwargs as params to the function?
#app.get("/carrier/{carrier_code}", response_class=PrettyJSONResponse) # params/kwargs??
async def get_carrier(carrier_code):
output_json = main_builder(carrier_code)
return airline_input_json
Using Pydantic Model
Since your function "..has many optional parameters" and passengers parameter requires a dictionary as an input, I would suggest creating a Pydantic model, where you define the parameters, and which would allow you sending the data in JSON format and getting them automatically validated by Pydantci as well. Once the endpoint is called, you can use Pydantic's dict() method to convert the model into a dictionary.
Example
from pydantic import BaseModel
from typing import Optional
class MyModel(BaseModel):
proxy: Optional[str] = None
card: Optional[str] = None
passengers: Optional[dict] = None
bags: Optional[int] = None
#app.post("/carrier/{carrier_code}")
async def get_carrier(carrier_code: int, m: MyModel):
return main_builder(carrier_code, **m.dict())
Sending arbitrary JSON data
In case you had to send arbitrary JSON data, and hence, pre-defining the parameters of an endpoint wouldn't be possible, you could use an approach similar to the one described in this answer (see Options 3 and 4), as well as this answer and this answer.
Is it possible to create a new instance of type T using generics in python?
I know how to do it in C# and have found plenty of examples but cant find anything for python.
For example:
public class Animal<T> where T : ISound, new(){
public T GetInstance()
{
return new T();
}}
Is there a python equivalent to the above C# snippet?
This is what I thought my python code needed to look like:
from typing import TypeVar, Generic
T = TypeVar('T'
class crud(Generic[T])
def create(self, endpoint: str, body, files=None) ->T:
url = self._build_url_str(endpoint)
res = self.http.post(url, json=body).json()
return T.__init__(res)
But I get TypeError: A single constraint is not allowed.
I google that and what I found didn't work or seemed irelevant.
Also, type T has what it needs to parse the response in the constructor so it has to be parametrized.
As stated in the answer here, you can do the following:
class Animal(Generic[T]):
def get_instance(self) -> T:
return self.__orig_class__.__args__[0]()
How do I write my Request class to say that the payload can be Union[EcrPayload] or Union[S3Payload]?
class EcrPayload(CamelModel):
repository_name: str
version: str
class S3Payload(CamelModel):
bucket_name: str
object_key_name: str
class Request(CamelModel):
payload: Union[EcrPayload]
Just use Union with both the classes you wish to include:
class Request(CamelModel):
payload: Union[EcrPayload, S3Payload] # accepts ECR and S3 payloads, but nothing else
Note that this means that the member variable payload has to be either an instance of EcrPayload or S3Payload, but nothing else.
If you wish to include any type of CamelModel instead (including perhaps new models EcsPayload, LambdaPayload or others that you may define in the future), you can write:
class Request(CamelModel):
payload: CamelModel # any CamelModel is accepted, including ECR and S3
You can use the same CamelModel.
class Request(CamelModel):
payload: Union[CamelModel]