Replies: 29 comments 12 replies
-
from pydantic import BaseModel,Field
from datetime import datetime
class User(BaseModel):
name:str
create_at: datetime = datetime.now()
user_in_system = User(name='includeamin')
print(user_in_system.dict(exclude={'create_at'}))
# output: {'name': 'includeamin'} |
Beta Was this translation helpful? Give feedback.
-
Sorry I should have clarified, how do you hide them in the openapi spec? :) |
Beta Was this translation helpful? Give feedback.
-
I mean you could create two models, one internal and one external, but you have to be careful between converting between two models (performance v.s. validation). |
Beta Was this translation helpful? Give feedback.
-
Yeah it doesn't seem like there is a good way to convert between two models without validation? |
Beta Was this translation helpful? Give feedback.
-
I did notice that Pydantic has a schema_extra function that you can provide but fast didn't seem to be picking it up when generating the openapi docs. |
Beta Was this translation helpful? Give feedback.
-
Check the docs here: https://fastapi.tiangolo.com/tutorial/response-model/#add-an-output-model Having a model declared as having an attribute and then making it not have the attribute in some other way would make your editor, linter (and you) believe that that model has that attribute while it doesn't. So it's better/easier to declare the 2 models that inherit from the same base. |
Beta Was this translation helpful? Give feedback.
-
Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues. |
Beta Was this translation helpful? Give feedback.
-
@enilsen16 did you ever find a clean way to hide a particular field from the schema? In my case I have some required headers that are set downstream by my ingress. FastAPI expects to see those headers but it's confusing for my users to see them in the docs. |
Beta Was this translation helpful? Give feedback.
-
Pydantic allows you to define extra arguments on Field. from pydantic import BaseModel, Field
class CustomBaseModel(BaseModel):
def dict(self, **kwargs):
hidden_fields = set(
attribute_name
for attribute_name, model_field in self.__fields__.items()
if model_field.field_info.extra.get("hidden") is True
)
kwargs.setdefault("exclude", hidden_fields)
return super().dict(**kwargs)
class Book(CustomBaseModel):
author: str
title: str
genre: str = Field(..., hidden=True)
m = Book(
author="Antoine de Saint-Exupéry",
title="The Little Prince",
genre="Novella",
)
print(m.genre) # Novella
print(m.dict()) # {'author': 'Antoine de Saint-Exupéry', 'title': 'The Little Prince'} |
Beta Was this translation helpful? Give feedback.
-
Does anyone know what method / function FastAPI is using to generate the OpenAPI schema from Pydantic models? We could then remove the to be hidden fields on the fly like in @aberrier's example It is not |
Beta Was this translation helpful? Give feedback.
-
You need to modify the schema processing of Pydantic to achieve this. Easiest way I've found was to monkey the field_schema function and raise a SkipField error. You can define your 'hidden' fields as follows:
Apply the patch
|
Beta Was this translation helpful? Give feedback.
-
@sgiroux thanks! I have some private fields which I want to provide for my "trusted" customers. Obviously, I don't want other people to see such fields in the documentation (because they will not get these fields in their responses anyway). |
Beta Was this translation helpful? Give feedback.
-
i have the same problem, but i can't find a elegant solution class UserCreationSchema(BaseModel):
username: str
password: str
created_by: Optional[str] = None but i don't want the the official doc mentioned above: add an output model just can't help, it's not the same thing. so what's the best way to go about this? |
Beta Was this translation helpful? Give feedback.
-
Hey @Su57 , I had exactly the same issue. Creating extra models (classes) can help. class UserBase(BaseModel):
username: str
password: str
class UserCreationSchema(UserBase):
created_by: Optional[str] = None So the autogenerated docs can use UserBase class, while your backend can use UserCreationSchema class. |
Beta Was this translation helpful? Give feedback.
-
You can also just make them private, fastapi respects that: from pydantic import BaseModel,
from datetime import datetime
class User(BaseModel):
class Config:
underscore_attrs_are_private = True
name:str
_create_at: datetime = Field(default_factory=datetime.now)
user = User(name="foo")
print(user._created_at)
# 2021-08-27 10:20:05.741129
print(user.dict())
# {'name': 'foo'}
print(User.schema())
# {
# "title": "User",
# "type": "object",
# "properties": {"name": {"title": "Name", "type": "string"}},
# "required": ["name"],
# } |
Beta Was this translation helpful? Give feedback.
-
That's probably the most elegant, but the "_" is not possible in my project, any way to mark the field as private as an attribute ? Something along this: class User(BaseModel):
name: str = Field(private=True) |
Beta Was this translation helpful? Give feedback.
-
Would it be ok in your project to make it private AND use an alias? |
Beta Was this translation helpful? Give feedback.
-
@wookiesh As @ghandic said, the right way to handle that would be to assign like this: class User(BaseModel):
class Config:
underscore_attrs_are_private = True
_my_name: str = Field(alias="my_name") Instantiation of that class will look like this Not exactly intuitive, but that's the way it is supported. |
Beta Was this translation helpful? Give feedback.
-
Don't know why I was doubting, but yes indeed, that works in my case, thanks ! |
Beta Was this translation helpful? Give feedback.
-
Is there a way to conditionally remove fields tagged with |
Beta Was this translation helpful? Give feedback.
-
I am using a stupid way: class UserDB(BaseModel):
name: str
password: str
class User(BaseModel):
name: str
@app.get('/user/{name}', response_model=User)
def create_user(name: str):
user_db = db.select('user', 'name', name)
user = User(name=user_db.name)
return user Just do not inhererit from its brother model. |
Beta Was this translation helpful? Give feedback.
-
Maybe this will help, by using from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, SecretStr
app = FastAPI()
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Union[str, None] = None
class UserOut(UserIn):
password: SecretStr = Field(..., exclude=True) |
Beta Was this translation helpful? Give feedback.
-
I'm late to the party, but if you want to hide Pydantic fields from the OpenAPI schema definition without either adding underscores (annoying when paired with SQLAlchemy) or overriding the You can therefore add a schema_extra static method in your class configuration to look for a Here is how I did it: from pydantic import BaseModel, Field
class User(BaseModel):
public_field: str
hidden_field: str = Field(hidden=True)
class Config:
@staticmethod
def schema_extra(schema: dict, _):
props = {}
for k, v in schema.get('properties', {}).items():
if not v.get("hidden", False):
props[k] = v
schema["properties"] = props This can also be done by creating a "proxy" BaseModel class per example. from pydantic import BaseModel as _BaseModel
class BaseModel(_BaseModel):
class Config:
@staticmethod
def schema_extra(schema: dict, _):
props = {}
for k, v in schema.get('properties', {}).items():
if not v.get("hidden", False):
props[k] = v
schema["properties"] = props You can certainly optimize this function, but that's the cleanest way I've been able to find. |
Beta Was this translation helpful? Give feedback.
-
import pydantic
class A(pydantic.BaseModel):
name: str
surname: str
class B(A):
pass
if type(B.__fields_set__) == set:
B.__fields_set__.remove('surname')
del B.__fields__['surname']
print(B.schema_json()) |
Beta Was this translation helpful? Give feedback.
-
I found an interesting way of making it, although I'm not sure how reliable it is. from pydantic import root_validator
from pydantic import BaseModel
class RecipeCreate(BaseModel):
name: str
description: str
instructions: str
prep_time: int
cook_time: int
servings: str
@root_validator
def calculate_total_time(cls, values):
values['total_time'] = values['prep_time'] + values['cook_time']
return values
class RecipeView(BaseModel):
name: str
description: str
instructions: str
prep_time: int
cook_time: int
servings: str
total_time: int |
Beta Was this translation helpful? Give feedback.
-
What is the best way to deal with conditionally hiding fields that are deeply nested? The two approaches I see would be to explicitly from pydantic import BaseModel
# Models with all fields
class Chunk(BaseModel):
text: str
embedding: list[float]
class Document(BaseModel):
id: str
name: str
chunks: list[Chunk]
class User(BaseModel):
id: str
name: str
documents: list[Document]
class Team(BaseModel):
id: str
name: str
users: list[User]
class Company(BaseModel):
id: str
name: str
teams: list[Team]
# Reponse models with hidden fields. The only field we actually need to hide is "embedding", deeply nested
class ChunkResponse(BaseModel):
text: str
# embedding field is now hidden
class DocumentResponse(BaseModel):
id: str
name: str
chunks: list[ChunkResponse]
class UserResponse(BaseModel):
id: str
name: str
documents: list[DocumentResponse]
class TeamResponse(BaseModel):
id: str
name: str
users: list[UserResponse]
class CompanyResponse(BaseModel):
id: str
name: str
teams: list[TeamResponse]
# CompanyResponse is returned from a `get_company` endpoint, and should have all fields except for embedding.
# Seemingly we need to create a new "Response" model for every single nested class just to hide the single field.
# Every single model is identical to the original except except for the Chunk model Is there a better way to do this? Seems like a crazy amount of code duplication just to conditionally hide the embedding field. For sure I could implement some custom serialization with the exclude flag, but that wont have good typing and could break easily. Ideally I could have some flag set on the embedding |
Beta Was this translation helpful? Give feedback.
-
@Su57 @tonythree @a-recknagel @lelecanfora I think i found most elegance way to hide field!! from datetime import datetime
from fastapi import FastAPI
from pydantic import BaseModel, PrivateAttr, computed_field
app = FastAPI()
class CreateUserRequest(BaseModel):
email: str
_created_at: int = PrivateAttr(default_factory=datetime.now)
@computed_field(
# repr=False,
return_type=datetime
)
def created_at(self):
return self._created_at
@app.post('/')
def haha(request: CreateUserRequest):
return request.model_dump() By using PrivateAttr and computed_field, It didn't show at swagger. |
Beta Was this translation helpful? Give feedback.
-
I use SkipJsonSchema to hide field in pydantic v2, You can check the link to get more information
from pydantic.json_schema import SkipJsonSchema
class User(BaseModel):
username: str
password: str
class UserResponseSchema(User):
password: SkipJsonSchema[str] |
Beta Was this translation helpful? Give feedback.
-
I defined my request model like below and it got removed from FastAPI docs .
further more I defined the methods to |
Beta Was this translation helpful? Give feedback.
-
Example:
I have created_at and updated_at fields that I want to auto populate on the model using default_factory, but I don't want the user to be able to set them via an endpoint. I thought this could be achieved by the read only Field arg but then I can never set it.
What's the best way to go about this?
Beta Was this translation helpful? Give feedback.
All reactions