I'm trying to allow null in the JSON schema for this object:
from pydantic import BaseModel
from typing import Optional
class NextSong(BaseModel):
song_title: Optional[str] = ...
but the schema which results is as follows:
'{"title": "NextSong", "type": "object", "properties": {"song_title": {"title": "Song Title", "type": "string"}}, "required": ["song_title"]}'
The resulting schema isn't allowing null for the value of song_title, which isn't as intended, but I'm not sure how else to specify that null is allowed, but that the field is still required.
I think you need OpenAPI nullable flag. It should change the schema and set nullable flag, but this field still will be required.
from pydantic import BaseModel, Field
from typing import Optional
class NextSong(BaseModel):
song_title: Optional[str] = Field(..., nullable=True)
Resulting schema:
{'title': 'NextSong', 'type': 'object', 'properties': {'song_title': {'title': 'Song Title', 'nullable': True, 'type': 'string'}}, 'required': ['song_title']}
try using OR symbol
class NextSong(BaseModel):
song_title: str|None = ...
note: I'm using python 3.10
Related
FastAPI with uvicorn. So far i have everything working fine, now wanted to add extra validation with pydantic and response_model validation. My challange is, the response is a nested object, example:
{
"name": "always the same",
"value": ["ACL1", "ACL2", "ACL3"]
}
I have tried to:
class ACL(BaseModel):
name: str
#app.post(
"get/acls",
response_model=List[ACL]
)
def get_acls(credentials: HTTPBasicCredentials = Depends(security)):
But obviously it will fail. How to define correctly the response model so that it's correctly validated ? (and also visible in openapi documentation page ?)
Thanks,
You can use nested models in Pydantic and by by extension also in FastAPI. Like so:
class ACL(BaseModel):
name: str
class YourResponseModel(BaseModel):
name: str
value: list[ACL]
#app.post("/get/acls", response_model=YourResponseModel)
def get_acl():
return {"name": "always the same", "value": [{"name":"ACL1"}, {"name":"ACL2"}, {"name":"ACL3"}]}
If you don't want nested objects in your value list, then you have to build some logic to parse it. Below is an example of this:
class ACL(BaseModel):
name: str
class AnotherResponseModel(BaseModel):
name: str
value: list[str]
#app.post("/get/acls2", response_model=AnotherResponseModel)
def get_acl():
acls = [ACL(name="ACL1"), ACL(name="ACL2"), ACL(name="ACL3")]
return {"name": "always the same", "value": [acl.name for acl in acls] }
Please note, it is bad practice to mix up POST and GET requests (you are defining a POST endpoint, that will 'get' some ACLs for the end user). That is however unrelated to this question.
For a FastAPI Pydanctic class I have these values
class ErrorReportRequest(BaseModel):
sender: Optional[str] = Field(..., description="Who sends the error message.")
error_message_displayed_to_client: str = Field(..., description="The error message displayed to the client.")
I use the class as an input model
router = APIRouter()
#router.post(
"/error_report",
response_model=None,
include_in_schema=True,
)
def error_report(err: ErrorReportRequest):
pass
When I run this, sender is a required field. If it's not included in the incoming JSON, I get a validation error.
Input:
{
"error_message_displayed_to_client": "string"
}
Results in:
{
"detail": [
{
"loc": [
"body",
"sender"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
If I remove the Field description like this:
class ErrorReportRequest(BaseModel):
sender: Optional[str]
error_message_displayed_to_client: str = Field(..., description="The error message displayed to the client.")
the request passes.
How can I add a Field description to an optional field so that it's still allowed to omit the field name?
You'll need to provide an explicit default value to Field, for example None instead of ...:
class ErrorReportRequest(BaseModel):
sender: Optional[str] = Field(None, description="Who sends the error message.")
error_message_displayed_to_client: str = Field(..., description="The error message displayed to the client.")
... indicates that the value is required. This is of course in conflict with the Optional, but it looks like pydantic gives higher priority to ....
From the documentation of Field:
default: (a positional argument) the default value of the field. Since the Field replaces the field's default, this first argument can be used to set the default. Use ellipsis (...) to indicate the field is required.
I have a problem with FastAPI and Pydantic.
I want to build a post api, program show this:
#router.post('/productRoute', response_model=SuccessCreate, status_code=status.HTTP_201_CREATED)
async def create_product_route(create: CreatePR):
query = ProductRouteModel.insert().values(
user_id=create.user_id,
route_id=create.route_id,
route_name=create.route_name,
head=create.head.dict(),
body=create.body,
route=create.route
)
await database.execute(query)
return {"status": "Successfully Created!"}
This is Pydantic class:
class RouteSchema(BaseModel):
id: str
next: Optional[List[str]]
prev: List[str]
class HeadSchema(BaseModel):
b1: str
b2: str
b3: str
class BodySchema(BaseModel):
a1: Optional[str]
a2: Optional[str]
class CreatePR(BaseModel):
user_id: str
route_id: str
route_name: str
head: HeadSchema
body: List[BodySchema]
route: List[RouteSchema]
Finally, this i want to post's parameter format:
{
user_id: "test1",
route_id: "route_1",
route_name: "route_name",
head: {...},
body: [{...}, {...}, ..., {...}],
route: [{...}, {...}, ..., {...}]
}
When I executed, I got TypeError: Object of type BodySchema is not JSON serializable.
How can I fix the program to normal operation?
Your code seems OK. I would not make a strong statement, but I suppose your post body is erroneous. Could you please verify whether your JSON format is correct or not. You could check it by using an online JSON editor (ex: https://jsonbeautifier.org/). Probable errors could be the usage of single quotes, missing/extra commas or even perhaps you forgot to put any quotes on your keys.
I'm trying to do something like this:
User:
type: "object"
properties:
id:
type: "integer"
format: "int64"
name:
type: "string"
manager:
$ref: "/definitions/User"
Editor does not throw an exception, but the code generated according to this schema does not work at all.
I generated python-flask server, and on launch, it throws:
ImportError: cannot import name 'User'
Looking through the code I found that 'User' class uses 'User' keyword in __init__ and inside the class.
Also there was the import: from swagger_server.models.user import User
Does python-flask generator know how to implement references like this?
Is there any way to test only particular field in a response is matching with the given text or not while writing a contract using spring-cloud-contract framework.
package contracts
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'GET'
url value(consumer(regex('/app/emp/employee/[0-9]{3}')), producer('/app/emp/employee/151'))
}
response {
status 200
body([
subjectsList: null,
errorResponse: null,
status: 'success',
employeeList: null,
Employee: [
EmployeeId: 151,
firstName: 'xxx',
lastName: 'xxx',
middleName: 'xxx',
dateOfBirth: 01012001,
status: 'inbound',
cin: '345',
ssn: null,
EmployeeType: 'HoH',
preferredLanguage: 'french',
preferredContactMethod: null,
createdBy: null,
creadtedOn: null,
updatedBy: null,
updatedOn: null,
transactionId: null
],
paginated: null
])
headers {
header('Content-Type': value(
producer(regex('application/json.*')),
Employee('application/json')
))
}
}
}
Instead of writing complete response, Is there any way to check only particular attribute present in the response for ex: language = 'french'
Thanks in advance, your help is very much appreciated.
Sure, just remove all the other fields. Whatever you put in the body will get asserted. BTW what you do with the contract looks like a schema. If a field is null that means that it has to be there and it has to be null or rather it's optional?
If you want to do any custom assertion on the part of / whole body you can use this http://cloud.spring.io/spring-cloud-static/Dalston.SR4/multi/multi__contract_dsl.html#_dynamic_properties_in_matchers_sections and pass any jsonpath element for custom assertion
BTW for the response you can write headers { contentType(applicationJson()) }