From 7559c6612780cf8714bce9290029614ee483e8bd Mon Sep 17 00:00:00 2001 From: Antoine Bertin Date: Tue, 27 Apr 2021 10:19:29 +0200 Subject: [PATCH] feat: other databases support --- docker-compose.dev.yml | 2 +- docker-compose.yml | 2 +- docs/docs/getting-started/install.md | 26 +++++++++++++------------- mealie/core/config.py | 18 +----------------- mealie/db/db_setup.py | 3 +-- mealie/db/models/db_session.py | 9 ++------- mealie/db/models/mealplan.py | 4 ++-- mealie/db/models/recipe/api_extras.py | 2 +- mealie/db/models/recipe/ingredient.py | 2 +- mealie/db/models/recipe/instruction.py | 2 +- mealie/db/models/recipe/note.py | 2 +- mealie/db/models/recipe/nutrition.py | 2 +- mealie/db/models/recipe/tag.py | 4 ++++ mealie/db/models/recipe/tool.py | 2 +- mealie/db/models/users.py | 2 +- mealie/routes/debug_routes.py | 3 +-- mealie/schema/debug.py | 3 +-- template.env | 5 ++--- tests/conftest.py | 2 +- tests/unit_tests/test_config.py | 11 ++--------- 20 files changed, 39 insertions(+), 67 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6e1aac0a9..927e17364 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -26,8 +26,8 @@ services: ports: - 9921:9000 environment: - db_type: sqlite TZ: America/Anchorage # Specify Correct Timezone for Date/Time to line up correctly. + DB_URL: sqlite:///mealie.db volumes: - ./dev/data:/app/dev/data - ./mealie:/app/mealie diff --git a/docker-compose.yml b/docker-compose.yml index 457f81576..fbf963220 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,4 +9,4 @@ services: ports: - 9090:80 environment: - db_type: sqlite + DB_URL: sqlite:///mealie.db diff --git a/docs/docs/getting-started/install.md b/docs/docs/getting-started/install.md index c89b7349f..fbb97dd73 100644 --- a/docs/docs/getting-started/install.md +++ b/docs/docs/getting-started/install.md @@ -25,7 +25,7 @@ Deployment with the Docker CLI can be done with `docker run` and specify the dat ```shell docker run \ - -e DB_TYPE='sqlite' \ + -e DB_URL='sqlite:///mealie.db' \ -p 9925:80 \ -v `pwd`:'/app/data/' \ hkotel/mealie:latest @@ -50,7 +50,7 @@ services: ports: - 9925:80 environment: - DB_TYPE: sqlite + DB_URL: sqlite:///mealie.db TZ: America/Anchorage volumes: - ./mealie/data/:/app/data @@ -59,19 +59,19 @@ services: ## Env Variables -| Variables | Default | Description | -| ---------------- | ------------------ | ----------------------------------------------------------------------------------- | -| DB_TYPE | sqlite | The database type to be used. Current Options 'sqlite' | -| DEFAULT_GROUP | Home | The default group for users | -| DEFAULT_EMAIL | changeme@email.com | The default username for the superuser | -| DEFAULT_PASSWORD | MyPassword | The default password for the superuser | -| TOKEN_TIME | 2 | The time in hours that a login/auth token is valid | -| API_PORT | 9000 | The port exposed by backend API. **do not change this if you're running in docker** | -| API_DOCS | True | Turns on/off access to the API documentation locally. | -| TZ | UTC | Must be set to get correct date/time on the server | - +| Variables | Default | Description | +| ---------------- | ------------------- | ----------------------------------------------------------------------------------- | +| DB_URL | sqlite:///mealie.db | The database url to be used. See [SQLAlchemy documentation][1] for more options | +| DEFAULT_GROUP | Home | The default group for users | +| DEFAULT_EMAIL | changeme@email.com | The default username for the superuser | +| DEFAULT_PASSWORD | MyPassword | The default password for the superuser | +| TOKEN_TIME | 2 | The time in hours that a login/auth token is valid | +| API_PORT | 9000 | The port exposed by backend API. **do not change this if you're running in docker** | +| API_DOCS | True | Turns on/off access to the API documentation locally. | +| TZ | UTC | Must be set to get correct date/time on the server | +[1]: https://docs.sqlalchemy.org/en/14/core/engines.html#database-urls ## Advanced !!! warning "Not Required" diff --git a/mealie/core/config.py b/mealie/core/config.py index be080dfb6..9fcfa90db 100644 --- a/mealie/core/config.py +++ b/mealie/core/config.py @@ -57,7 +57,6 @@ class AppDirectories: self.CHOWDOWN_DIR: Path = self.MIGRATION_DIR.joinpath("chowdown") self.TEMPLATE_DIR: Path = data_dir.joinpath("templates") self.USER_DIR: Path = data_dir.joinpath("users") - self.SQLITE_DIR: Path = data_dir.joinpath("db") self.RECIPE_DATA_DIR: Path = data_dir.joinpath("recipes") self.TEMP_DIR: Path = data_dir.joinpath(".temp") @@ -70,7 +69,6 @@ class AppDirectories: self.DEBUG_DIR, self.MIGRATION_DIR, self.TEMPLATE_DIR, - self.SQLITE_DIR, self.NEXTCLOUD_DIR, self.CHOWDOWN_DIR, self.RECIPE_DATA_DIR, @@ -100,21 +98,7 @@ class AppSettings(BaseSettings): return "/redoc" if self.API_DOCS else None SECRET: str = determine_secrets(DATA_DIR, PRODUCTION) - DATABASE_TYPE: str = Field("sqlite", env="DB_TYPE") - - @validator("DATABASE_TYPE", pre=True) - def validate_db_type(cls, v: str) -> Optional[str]: - if v != "sqlite": - raise ValueError("Unable to determine database type. Acceptible options are 'sqlite'") - else: - return v - - # Used to Set SQLite File Version - SQLITE_FILE: Optional[Union[str, Path]] - - @validator("SQLITE_FILE", pre=True) - def identify_sqlite_file(cls, v: str) -> Optional[str]: - return app_dirs.SQLITE_DIR.joinpath(f"mealie_{DB_VERSION}.sqlite") + DB_URL: str = Field("sqlite:///mealie.db", env="DB_URL") DEFAULT_GROUP: str = "Home" DEFAULT_EMAIL: str = "changeme@email.com" diff --git a/mealie/db/db_setup.py b/mealie/db/db_setup.py index c9c5d99cd..a49adbc95 100644 --- a/mealie/db/db_setup.py +++ b/mealie/db/db_setup.py @@ -5,8 +5,7 @@ from mealie.db.models.db_session import sql_global_init sql_exists = True -sql_exists = settings.SQLITE_FILE.is_file() -SessionLocal = sql_global_init(settings.SQLITE_FILE) +SessionLocal = sql_global_init(settings.DB_URL) def create_session() -> Session: diff --git a/mealie/db/models/db_session.py b/mealie/db/models/db_session.py index 78dcbc71b..5784fac18 100644 --- a/mealie/db/models/db_session.py +++ b/mealie/db/models/db_session.py @@ -5,15 +5,10 @@ from mealie.db.models.model_base import SqlAlchemyBase from sqlalchemy.orm import sessionmaker -def sql_global_init(db_file: Path, check_thread=False): - - SQLALCHEMY_DATABASE_URL = "sqlite:///" + str(db_file.absolute()) - # SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" - +def sql_global_init(db_url: str): engine = sa.create_engine( - SQLALCHEMY_DATABASE_URL, + db_url, echo=False, - connect_args={"check_same_thread": check_thread}, ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/mealie/db/models/mealplan.py b/mealie/db/models/mealplan.py index 84e70d9c2..95d73d957 100644 --- a/mealie/db/models/mealplan.py +++ b/mealie/db/models/mealplan.py @@ -9,7 +9,7 @@ from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase class Meal(SqlAlchemyBase): __tablename__ = "meal" id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("mealplan.uid")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("mealplan.uid")) slug = sa.Column(sa.String) name = sa.Column(sa.String) date = sa.Column(sa.Date) @@ -30,7 +30,7 @@ class MealPlanModel(SqlAlchemyBase, BaseMixins): startDate = sa.Column(sa.Date) endDate = sa.Column(sa.Date) meals: List[Meal] = orm.relationship(Meal, cascade="all, delete, delete-orphan") - group_id = sa.Column(sa.String, sa.ForeignKey("groups.id")) + group_id = sa.Column(sa.Integer, sa.ForeignKey("groups.id")) group = orm.relationship("Group", back_populates="mealplans") def __init__(self, startDate, endDate, meals, group: str, uid=None, session=None) -> None: diff --git a/mealie/db/models/recipe/api_extras.py b/mealie/db/models/recipe/api_extras.py index 222bb6c2c..c4172cb4c 100644 --- a/mealie/db/models/recipe/api_extras.py +++ b/mealie/db/models/recipe/api_extras.py @@ -5,7 +5,7 @@ from mealie.db.models.model_base import SqlAlchemyBase class ApiExtras(SqlAlchemyBase): __tablename__ = "api_extras" id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id")) key_name = sa.Column(sa.String, unique=True) value = sa.Column(sa.String) diff --git a/mealie/db/models/recipe/ingredient.py b/mealie/db/models/recipe/ingredient.py index 75c20d2d0..b00f4c60a 100644 --- a/mealie/db/models/recipe/ingredient.py +++ b/mealie/db/models/recipe/ingredient.py @@ -6,7 +6,7 @@ class RecipeIngredient(SqlAlchemyBase): __tablename__ = "recipes_ingredients" id = sa.Column(sa.Integer, primary_key=True) position = sa.Column(sa.Integer) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id")) ingredient = sa.Column(sa.String) def update(self, ingredient): diff --git a/mealie/db/models/recipe/instruction.py b/mealie/db/models/recipe/instruction.py index 2cb2e38a6..9b4a29733 100644 --- a/mealie/db/models/recipe/instruction.py +++ b/mealie/db/models/recipe/instruction.py @@ -5,7 +5,7 @@ from mealie.db.models.model_base import SqlAlchemyBase class RecipeInstruction(SqlAlchemyBase): __tablename__ = "recipe_instructions" id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id")) position = sa.Column(sa.Integer) type = sa.Column(sa.String, default="") text = sa.Column(sa.String) diff --git a/mealie/db/models/recipe/note.py b/mealie/db/models/recipe/note.py index 8d37db582..28ee46303 100644 --- a/mealie/db/models/recipe/note.py +++ b/mealie/db/models/recipe/note.py @@ -5,7 +5,7 @@ from mealie.db.models.model_base import SqlAlchemyBase class Note(SqlAlchemyBase): __tablename__ = "notes" id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id")) title = sa.Column(sa.String) text = sa.Column(sa.String) diff --git a/mealie/db/models/recipe/nutrition.py b/mealie/db/models/recipe/nutrition.py index 2ded5bdb2..6d03b6459 100644 --- a/mealie/db/models/recipe/nutrition.py +++ b/mealie/db/models/recipe/nutrition.py @@ -5,7 +5,7 @@ from mealie.db.models.model_base import SqlAlchemyBase class Nutrition(SqlAlchemyBase): __tablename__ = "recipe_nutrition" id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id")) calories = sa.Column(sa.String) fatContent = sa.Column(sa.String) fiberContent = sa.Column(sa.String) diff --git a/mealie/db/models/recipe/tag.py b/mealie/db/models/recipe/tag.py index 435f22ba4..e4c27b28f 100644 --- a/mealie/db/models/recipe/tag.py +++ b/mealie/db/models/recipe/tag.py @@ -11,7 +11,11 @@ recipes2tags = sa.Table( "recipes2tags", SqlAlchemyBase.metadata, sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")), +<<<<<<< HEAD sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id")), +======= + sa.Column("tag_slug", sa.String, sa.ForeignKey("tags.slug")), +>>>>>>> e2e2ad5 (feat: other databases support) ) diff --git a/mealie/db/models/recipe/tool.py b/mealie/db/models/recipe/tool.py index 6b65c9e5d..2406864f3 100644 --- a/mealie/db/models/recipe/tool.py +++ b/mealie/db/models/recipe/tool.py @@ -5,7 +5,7 @@ from mealie.db.models.model_base import SqlAlchemyBase class Tool(SqlAlchemyBase): __tablename__ = "tools" id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id")) tool = sa.Column(sa.String) def __init__(self, tool) -> None: diff --git a/mealie/db/models/users.py b/mealie/db/models/users.py index 970072122..fe62ddc0a 100644 --- a/mealie/db/models/users.py +++ b/mealie/db/models/users.py @@ -16,7 +16,7 @@ class User(SqlAlchemyBase, BaseMixins): full_name = Column(String, index=True) email = Column(String, unique=True, index=True) password = Column(String) - group_id = Column(String, ForeignKey("groups.id")) + group_id = Column(Integer, ForeignKey("groups.id")) group = orm.relationship("Group", back_populates="users") admin = Column(Boolean, default=False) diff --git a/mealie/routes/debug_routes.py b/mealie/routes/debug_routes.py index 8dc919683..18a4714b0 100644 --- a/mealie/routes/debug_routes.py +++ b/mealie/routes/debug_routes.py @@ -20,8 +20,7 @@ async def get_debug_info(current_user=Depends(get_current_user)): demo_status=settings.IS_DEMO, api_port=settings.API_PORT, api_docs=settings.API_DOCS, - db_type=settings.DATABASE_TYPE, - sqlite_file=settings.SQLITE_FILE, + db_url=settings.DB_URL, default_group=settings.DEFAULT_GROUP, ) diff --git a/mealie/schema/debug.py b/mealie/schema/debug.py index 0348af14b..cb7ec6a0c 100644 --- a/mealie/schema/debug.py +++ b/mealie/schema/debug.py @@ -11,6 +11,5 @@ class AppInfo(CamelModel): class DebugInfo(AppInfo): api_port: int api_docs: bool - db_type: str - sqlite_file: Path + db_url: str default_group: str diff --git a/template.env b/template.env index 0ec7acf1c..2fe26b3e0 100644 --- a/template.env +++ b/template.env @@ -14,13 +14,12 @@ API_PORT=9000 # Exposes /docs and /redoc on the server API_DOCS=True -# Sets the Database type to use. Currently the only supported options is 'sqlite' -DB_TYPE=sqlite +# Sets the Database URL to use. See https://docs.sqlalchemy.org/en/14/core/engines.html for options. +DB_URL=sqlite:///mealie.db # Sets the token expiration time in hours. TOKEN_TIME=24 -# NOT USED SFTP_USERNAME=None SFTP_PASSWORD=None diff --git a/tests/conftest.py b/tests/conftest.py index 36107adc6..2de17e9c5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,7 @@ SQLITE_FILE = app_dirs.SQLITE_DIR.joinpath("test.db") SQLITE_FILE.unlink(missing_ok=True) -TestSessionLocal = sql_global_init(SQLITE_FILE, check_thread=False) +TestSessionLocal = sql_global_init(SQLITE_FILE) init_db(TestSessionLocal()) diff --git a/tests/unit_tests/test_config.py b/tests/unit_tests/test_config.py index 895420a34..07cb287ba 100644 --- a/tests/unit_tests/test_config.py +++ b/tests/unit_tests/test_config.py @@ -9,14 +9,14 @@ def test_default_settings(monkeypatch): monkeypatch.delenv("DEFAULT_PASSWORD", raising=False) monkeypatch.delenv("API_PORT", raising=False) monkeypatch.delenv("API_DOCS", raising=False) - monkeypatch.delenv("DB_TYPE", raising=False) + monkeypatch.delenv("DB_URL", raising=False) monkeypatch.delenv("IS_DEMO", raising=False) app_settings = AppSettings() assert app_settings.DEFAULT_GROUP == "Home" assert app_settings.DEFAULT_PASSWORD == "MyPassword" - assert app_settings.DATABASE_TYPE == "sqlite" + assert app_settings.DB_URL == "sqlite:///mealie.db" assert app_settings.API_PORT == 9000 assert app_settings.API_DOCS is True assert app_settings.IS_DEMO is False @@ -42,13 +42,6 @@ def test_non_default_settings(monkeypatch): assert app_settings.DOCS_URL is None -def test_unknown_database(monkeypatch): - monkeypatch.setenv("DB_TYPE", "nonsense") - - with pytest.raises(ValueError, match="Unable to determine database type. Acceptible options are 'sqlite'"): - AppSettings() - - def test_secret_generation(tmp_path): app_dirs = AppDirectories(CWD, DATA_DIR) assert determine_secrets(app_dirs.DATA_DIR, False) == "shh-secret-test-key"