Use Pydantic BaseSettings for config settings by StephenBrown2 · Pull Request #87 · fastapi/full-stack-fastapi-template

@StephenBrown2

Enables the use of validation for config on load, and pulling from environment implicitly.

I believe I got all the types right, mostly strings. All of the values not critical for running (i.e. email settings) also have a default value, and so are optional. Others, those populated by cookiecutter, are required.

I also updated some deps to be consistent throughout and changed a single keyword arg to match Pydantic 1.0 usage, but I can back those out if needed.

ebreton

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it 🥇

I didnot realize that pydantic provided the BaseSettings class. It's great 🎈

StephenBrown2

) -> ModelType:
obj_data = jsonable_encoder(db_obj)
update_data = obj_in.dict(skip_defaults=True)
update_data = obj_in.dict(exclude_unset=True)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will conflict with PR #106

paul121 added a commit to paul121/farmOS-aggregator that referenced this pull request

Feb 10, 2020

paul121 added a commit to paul121/farmOS-aggregator that referenced this pull request

Feb 10, 2020

paul121 added a commit to paul121/farmOS-aggregator that referenced this pull request

Feb 13, 2020

paul121 added a commit to farmOS/farmOS-aggregator that referenced this pull request

Feb 13, 2020

@sandys

@StephenBrown2 just wondering if using Pydantic is superior to using Starlette config ? https://www.starlette.io/config/

Especially considering security usecases where we do not commit ".env" into the source code repo. Starlette config supports all those kind of cases. Pretty sure you can write them... so just curious

@StephenBrown2

Pydantic by default reads form the environment, but can also read from a dotenv file, as long as the python-dotenv package is installed as well: https://pydantic-docs.helpmanual.io/usage/settings/#dotenv-env-support
Since Pydantic is already being used for validation/parsing, it seemed logical to use it for settings as well. One difference is that settings must be in a class, but that can come in useful for setting config in multiple ways, giving it one more option than Starlette's Config:

Setting value is determined as follows (in descending order of priority):

  1. Arguments passed to the Settings class initialiser.
  2. Environment variables, e.g. my_prefix_special_function as described above.
  3. Variables loaded from a dotenv (.env) file.
  4. The default field values for the Settings model.

I see them as equivalent, and prefer Pydantic's method, but if one is not using Pydantic and instead using Starlette alone, it's perfectly fine to use that.

@sandys

Thanks for your reply. The only reason I asked was that fastapi is built on Starlette and that uses this config system. So i was concerned that was the long term safer way

On Tue, 25 Feb, 2020, 21:15 Stephen Brown II, ***@***.***> wrote: Pydantic by default reads form the environment, but can also read from a dotenv file, as long as the python-dotenv <https://pypi.org/project/python-dotenv/> package <https://pypi.org/project/python-dotenv/> is installed as well: https://pydantic-docs.helpmanual.io/usage/settings/#dotenv-env-support Since Pydantic is already being used for validation/parsing, it seemed logical to use it for settings as well. One difference is that settings must be in a class, but that can come in useful for setting config in multiple ways, giving it one more option than Starlette's Config: Setting value is determined as follows (in descending order of priority): 1. Arguments passed to the Settings class initialiser. 2. Environment variables, e.g. my_prefix_special_function as described above. 3. Variables loaded from a dotenv (.env) file. 4. The default field values for the Settings model. I see them as equivalent, and prefer Pydantic's method, but if one is not using Pydantic and instead using Starlette alone, it's perfectly fine to use that. — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#87>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAASYU5QJ6YWC5SBMEFKGUTREU4JJANCNFSM4JXFGMDA> .

@StephenBrown2

@StephenBrown2

@michaeloliverx

Personally I like Pydantic's BaseSettings more aswell.

You can also use sub models to structure settings somewhat. Prefixes can be set via the Config.

Example:

from pathlib import Path
from typing import List

from pydantic import (
    AnyHttpUrl,
    BaseModel,
    BaseSettings,
    FilePath,
    PostgresDsn,
    SecretStr,
)


class Paths(BaseModel):
    """File paths class. Build file paths using APP_DIR."""

    APP_DIR: FilePath = Path(__file__).resolve().parent
    STATIC_DIR: FilePath = APP_DIR.joinpath("static")
    # Docker secrets directory
    SECRETS_DIR: FilePath = "/run/secrets"


class PostgresSettings(BaseSettings):
    USER: str
    PASSWORD: SecretStr
    HOST: str
    PORT: int
    DB: str

    class Config:
        env_prefix = "POSTGRES_"


class SMTPSettings(BaseSettings):
    USER: str
    PASSWORD: SecretStr
    TLS: bool
    SSL: bool
    HOST: str
    PORT: int

    class Config:
        env_prefix = "SMTP_"


class JWTSettings(BaseSettings):
    EXPIRE_MINUTES: int = 60 * 24 * 8  # 60 minutes * 24 hours * 8 days = 8 days
    EMAIL_EXPIRE_MINUTES: int = 60 * 8  # 8 hours

    class Config:
        env_prefix = "JWT_"


class Settings(BaseSettings):

    PATHS = Paths()

    POSTGRES = PostgresSettings()
    BASE_DB_URL: PostgresDsn = f"postgresql://{POSTGRES.USER}:{POSTGRES.PASSWORD.get_secret_value()}@{POSTGRES.HOST}:{POSTGRES.PORT}"
    DATABASE_URL: PostgresDsn = BASE_DB_URL + f"/{POSTGRES.DB}"
    TEST_DATABASE_URL: PostgresDsn = BASE_DB_URL + f"/{POSTGRES.DB}_test"

    SMTP: SMTPSettings = SMTPSettings()

    JWT: JWTSettings = JWTSettings()

    PROJECT_NAME: str
    SERVER_HOST: AnyHttpUrl = "http://0.0.0.0"
    CORS_WHITELIST: List[AnyHttpUrl] = []

    FASTAPI_ENV: str
    TESTING: bool = False
    DEBUG: bool = False

    FIRST_SUPERUSER: str
    FIRST_SUPERUSER_PASSWORD: str

    USERS_OPEN_REGISTRATION: bool


settings = Settings()

@tiangolo

@tiangolo

gusevyaroslove pushed a commit to gusevyaroslove/fastapi-template that referenced this pull request

Aug 4, 2024
* Use Pydantic BaseSettings for config settings

* Update fastapi dep to >=0.47.0 and email_validator to email-validator

* Fix deprecation warning for Pydantic >=1.0

* Properly support old-format comma separated strings for BACKEND_CORS_ORIGINS

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>

alejsdev pushed a commit that referenced this pull request

Dec 19, 2024