Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Pydantic BaseSettings for config settings #87

Merged
merged 5 commits into from
Apr 17, 2020

Conversation

StephenBrown2
Copy link
Contributor

@StephenBrown2 StephenBrown2 commented Dec 7, 2019

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.

@StephenBrown2 StephenBrown2 force-pushed the pydantic_basesettings branch 3 times, most recently from 88f4e33 to a59f546 Compare January 29, 2020 23:30
Copy link
Contributor

@ebreton ebreton left a comment

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 🎈

@@ -41,7 +41,7 @@ def update(
self, db_session: Session, *, db_obj: ModelType, obj_in: UpdateSchemaType
) -> ModelType:
obj_data = jsonable_encoder(db_obj)
update_data = obj_in.dict(skip_defaults=True)
update_data = obj_in.dict(exclude_unset=True)
Copy link
Contributor Author

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
Copy link

sandys commented Feb 25, 2020

@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
Copy link
Contributor Author

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
Copy link

sandys commented Feb 25, 2020 via email

@StephenBrown2
Copy link
Contributor Author

Aye, but FastAPI is also built on Pydantic for the data parts, so either option should work fine for the long term.

One benefit Starlette's implementation might have is it understands Comma Separated Strings, while Pydantic requires complex vars to be in JSON, so if we used that, we wouldn't need to change BACKEND_CORS_ORIGINS https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/87/files#diff-d7ae4ef57958180c199332f0ce9c5a2fR13

@michaeloliverx
Copy link

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 merged commit 79631c7 into fastapi:master Apr 17, 2020
@tiangolo
Copy link
Member

Great job @StephenBrown2 ! 🙇 🤓

Thanks for your contribution! 🚀 🎉

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants