Pydantic
Why do we have multiple classes for the same model? For example:
from pydantic import BaseModel
class UserBase(BaseModel):
name: str
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
orm_mode = True
Here’s the response from copilot:
In Pydantic, we often define multiple schemas like UserBase
, UserCreate
, and User
to handle different use cases and ensure clear separation of concerns. Here’s why we do this:
UserBase
: This schema typically includes the common fields that are shared across different operations. It serves as a base class for other schemas.UserCreate
: This schema extendsUserBase
and includes additional fields required for creating a new user, such as password. It ensures that only the necessary fields are included when creating a user.User
: This schema extends UserBase and includes additional fields that are returned in responses, such as id. It ensures that sensitive information like password is not included in the response.
Pydantic Settings
Pydantic Settings provides optional Pydantic features for loading a settings or config class from environment variables or secrets files.
Basic Usage
Example:
# config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
database_url: str
openai_api_key: str
model_config = SettingsConfigDict(env_file=".env")
def load_settings():
return Settings()
Key points:
- The
Settings
class has two fieldsdatabase_url
andopenai_api_key
- The
model_config
is a member ofBaseSettings
, and we can customize the behavior ofpydantic_settings
by updating its attributes- For example, we can set the value of
_env_file
to.env
- For example, we can set the value of
- The
settings
is an instance of theSettings
class, and we can access the environment variable viasettings.database_url
- The naming convention of these variables are lower case
Unit Tests
Here’s a simple unit test:
# test_config.py
import os
from app.config import load_settings
def test_settings_loading_from_env():
# Create a temporary .env file
with open(".env", "w") as f:
f.write("database_url=sqlite:///test.db\n")
f.write("openai_api_key=secret-key\n")
# Load settings
settings = load_settings()
print(settings)
# Assert that settings are loaded correctly
assert settings.database_url == "sqlite:///test.db"
assert settings.openai_api_key == "secret-key"
# Remove the temporary .env file
os.remove(".env")