mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-15 23:05:23 -05:00
feat: Add Households to Mealie (#3970)
This commit is contained in:
@@ -26,7 +26,7 @@ SessionLocal, engine = sql_global_init(settings.DB_URL) # type: ignore
|
||||
|
||||
|
||||
@contextmanager
|
||||
def session_context() -> Session:
|
||||
def session_context() -> Generator[Session, None, None]:
|
||||
"""
|
||||
session_context() provides a managed session to the database that is automatically
|
||||
closed when the context is exited. This is the preferred method of accessing the
|
||||
|
||||
@@ -5,7 +5,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.core import root_logger
|
||||
from mealie.db.models.group.group import Group
|
||||
from mealie.db.models.group.shopping_list import ShoppingList, ShoppingListMultiPurposeLabel
|
||||
from mealie.db.models.household.shopping_list import ShoppingList, ShoppingListMultiPurposeLabel
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
|
||||
@@ -17,25 +17,38 @@ from mealie.db.fixes.fix_slug_foods import fix_slug_food_names
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.repos.seed.init_users import default_user_init
|
||||
from mealie.schema.user.user import GroupBase
|
||||
from mealie.schema.household.household import HouseholdCreate, HouseholdInDB
|
||||
from mealie.schema.user.user import GroupBase, GroupInDB
|
||||
from mealie.services.group_services.group_service import GroupService
|
||||
from mealie.services.household_services.household_service import HouseholdService
|
||||
|
||||
PROJECT_DIR = Path(__file__).parent.parent.parent
|
||||
|
||||
logger = root_logger.get_logger()
|
||||
|
||||
|
||||
def init_db(db: AllRepositories) -> None:
|
||||
default_group_init(db)
|
||||
default_user_init(db)
|
||||
|
||||
|
||||
def default_group_init(db: AllRepositories):
|
||||
def init_db(session: orm.Session) -> None:
|
||||
settings = get_app_settings()
|
||||
|
||||
logger.info("Generating Default Group")
|
||||
instance_repos = get_repositories(session)
|
||||
default_group = default_group_init(instance_repos, settings.DEFAULT_GROUP)
|
||||
|
||||
GroupService.create_group(db, GroupBase(name=settings.DEFAULT_GROUP))
|
||||
group_repos = get_repositories(session, group_id=default_group.id, household_id=None)
|
||||
default_household = group_repos.households.get_by_name(settings.DEFAULT_HOUSEHOLD)
|
||||
if default_household is None:
|
||||
default_household = default_household_init(group_repos, settings.DEFAULT_HOUSEHOLD)
|
||||
household_repos = get_repositories(session, group_id=default_group.id, household_id=default_household.id)
|
||||
default_user_init(household_repos)
|
||||
|
||||
|
||||
def default_group_init(repos: AllRepositories, name: str) -> GroupInDB:
|
||||
logger.info("Generating Default Group and Household")
|
||||
return GroupService.create_group(repos, GroupBase(name=name))
|
||||
|
||||
|
||||
def default_household_init(repos: AllRepositories, name: str) -> HouseholdInDB:
|
||||
logger.info("Generating Default Household")
|
||||
return HouseholdService.create_household(repos, HouseholdCreate(name=name))
|
||||
|
||||
|
||||
# Adapted from https://alembic.sqlalchemy.org/en/latest/cookbook.html#test-current-database-revision-is-at-head-s
|
||||
@@ -103,7 +116,7 @@ def main():
|
||||
if session.get_bind().name == "postgresql": # needed for fuzzy search and fast GIN text indices
|
||||
session.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm;"))
|
||||
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
safe_try(lambda: fix_migration_data(session))
|
||||
safe_try(lambda: fix_slug_food_names(db))
|
||||
safe_try(lambda: fix_group_with_no_name(session))
|
||||
@@ -112,7 +125,7 @@ def main():
|
||||
logger.debug("Database exists")
|
||||
else:
|
||||
logger.info("Database contains no users, initializing...")
|
||||
init_db(db)
|
||||
init_db(session)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, Integer
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, declared_attr, mapped_column, synonym
|
||||
from text_unidecode import unidecode
|
||||
|
||||
from ._model_utils.datetime import get_utc_now
|
||||
@@ -12,6 +12,10 @@ class SqlAlchemyBase(DeclarativeBase):
|
||||
created_at: Mapped[datetime | None] = mapped_column(DateTime, default=get_utc_now, index=True)
|
||||
update_at: Mapped[datetime | None] = mapped_column(DateTime, default=get_utc_now, onupdate=get_utc_now)
|
||||
|
||||
@declared_attr
|
||||
def updated_at(cls) -> Mapped[datetime | None]:
|
||||
return synonym("update_at")
|
||||
|
||||
@classmethod
|
||||
def normalize(cls, val: str) -> str:
|
||||
return unidecode(val).lower().strip()
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
from .cookbook import *
|
||||
from .events import *
|
||||
from .exports import *
|
||||
from .group import *
|
||||
from .invite_tokens import *
|
||||
from .mealplan import *
|
||||
from .preferences import *
|
||||
from .recipe_action import *
|
||||
from .report import *
|
||||
from .shopping_list import *
|
||||
from .webhooks import *
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from pydantic import ConfigDict
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
from ..group.invite_tokens import GroupInviteToken
|
||||
from ..group.webhooks import GroupWebhooksModel
|
||||
from ..household.cookbook import CookBook
|
||||
from ..household.invite_tokens import GroupInviteToken
|
||||
from ..household.mealplan import GroupMealPlan
|
||||
from ..household.webhooks import GroupWebhooksModel
|
||||
from ..recipe.category import Category, group_to_categories
|
||||
from ..server.task import ServerTaskModel
|
||||
from .cookbook import CookBook
|
||||
from .mealplan import GroupMealPlan
|
||||
from .preferences import GroupPreferencesModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..recipe import (
|
||||
IngredientFoodModel,
|
||||
IngredientUnitModel,
|
||||
RecipeModel,
|
||||
Tag,
|
||||
Tool,
|
||||
)
|
||||
from ..household import Household
|
||||
from ..household.events import GroupEventNotifierModel
|
||||
from ..household.recipe_action import GroupRecipeAction
|
||||
from ..household.shopping_list import ShoppingList
|
||||
from ..recipe import IngredientFoodModel, IngredientUnitModel, RecipeModel, Tag, Tool
|
||||
from ..users import User
|
||||
from .events import GroupEventNotifierModel
|
||||
from .exports import GroupDataExportsModel
|
||||
from .recipe_action import GroupRecipeAction
|
||||
from .report import ReportModel
|
||||
from .shopping_list import ShoppingList
|
||||
|
||||
|
||||
class Group(SqlAlchemyBase, BaseMixins):
|
||||
@@ -42,6 +34,7 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
name: Mapped[str] = mapped_column(sa.String, index=True, nullable=False, unique=True)
|
||||
slug: Mapped[str | None] = mapped_column(sa.String, index=True, unique=True)
|
||||
households: Mapped[list["Household"]] = orm.relationship("Household", back_populates="group")
|
||||
users: Mapped[list["User"]] = orm.relationship("User", back_populates="group")
|
||||
categories: Mapped[list[Category]] = orm.relationship(Category, secondary=group_to_categories, single_parent=True)
|
||||
|
||||
@@ -89,6 +82,7 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
tags: Mapped[list["Tag"]] = orm.relationship("Tag", **common_args)
|
||||
model_config = ConfigDict(
|
||||
exclude={
|
||||
"households",
|
||||
"users",
|
||||
"webhooks",
|
||||
"recipe_actions",
|
||||
@@ -104,12 +98,3 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
|
||||
@staticmethod # TODO: Remove this
|
||||
def get_by_name(session: Session, name: str) -> Optional["Group"]:
|
||||
settings = get_app_settings()
|
||||
|
||||
item = session.execute(select(Group).filter(Group.name == name)).scalars().one_or_none()
|
||||
if item is None:
|
||||
item = session.execute(select(Group).filter(Group.name == settings.DEFAULT_GROUP)).scalars().one_or_none()
|
||||
return item
|
||||
|
||||
@@ -20,9 +20,9 @@ class GroupPreferencesModel(SqlAlchemyBase, BaseMixins):
|
||||
group: Mapped[Optional["Group"]] = orm.relationship("Group", back_populates="preferences")
|
||||
|
||||
private_group: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
first_day_of_week: Mapped[int | None] = mapped_column(sa.Integer, default=0)
|
||||
|
||||
# Recipe Defaults
|
||||
# Deprecated (see household preferences)
|
||||
first_day_of_week: Mapped[int | None] = mapped_column(sa.Integer, default=0)
|
||||
recipe_public: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
recipe_show_nutrition: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_show_assets: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
|
||||
35
mealie/db/models/household/__init__.py
Normal file
35
mealie/db/models/household/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from .cookbook import CookBook
|
||||
from .events import GroupEventNotifierModel, GroupEventNotifierOptionsModel
|
||||
from .household import Household
|
||||
from .invite_tokens import GroupInviteToken
|
||||
from .mealplan import GroupMealPlan, GroupMealPlanRules
|
||||
from .preferences import HouseholdPreferencesModel
|
||||
from .recipe_action import GroupRecipeAction
|
||||
from .shopping_list import (
|
||||
ShoppingList,
|
||||
ShoppingListExtras,
|
||||
ShoppingListItem,
|
||||
ShoppingListItemRecipeReference,
|
||||
ShoppingListMultiPurposeLabel,
|
||||
ShoppingListRecipeReference,
|
||||
)
|
||||
from .webhooks import GroupWebhooksModel
|
||||
|
||||
__all__ = [
|
||||
"CookBook",
|
||||
"GroupEventNotifierModel",
|
||||
"GroupEventNotifierOptionsModel",
|
||||
"GroupInviteToken",
|
||||
"GroupMealPlan",
|
||||
"GroupMealPlanRules",
|
||||
"Household",
|
||||
"HouseholdPreferencesModel",
|
||||
"GroupRecipeAction",
|
||||
"ShoppingList",
|
||||
"ShoppingListExtras",
|
||||
"ShoppingListItem",
|
||||
"ShoppingListItemRecipeReference",
|
||||
"ShoppingListMultiPurposeLabel",
|
||||
"ShoppingListRecipeReference",
|
||||
"GroupWebhooksModel",
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import Boolean, ForeignKey, Integer, String, orm
|
||||
from sqlalchemy import Boolean, ForeignKey, Integer, String, UniqueConstraint, orm
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
@@ -11,16 +11,23 @@ from ..recipe.tag import Tag, cookbooks_to_tags
|
||||
from ..recipe.tool import Tool, cookbooks_to_tools
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .group import Group
|
||||
from ..group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class CookBook(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "cookbooks"
|
||||
__table_args__: tuple[UniqueConstraint, ...] = (
|
||||
UniqueConstraint("slug", "group_id", name="cookbook_slug_group_id_key"),
|
||||
)
|
||||
|
||||
id: Mapped[guid.GUID] = mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
position: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
||||
|
||||
group_id: Mapped[guid.GUID | None] = mapped_column(guid.GUID, ForeignKey("groups.id"), index=True)
|
||||
group: Mapped[Optional["Group"]] = orm.relationship("Group", back_populates="cookbooks")
|
||||
household_id: Mapped[guid.GUID | None] = mapped_column(guid.GUID, ForeignKey("households.id"), index=True)
|
||||
household: Mapped[Optional["Household"]] = orm.relationship("Household", back_populates="cookbooks")
|
||||
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
slug: Mapped[str] = mapped_column(String, nullable=False, index=True)
|
||||
@@ -8,7 +8,8 @@ from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .group import Group
|
||||
from ..group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class GroupEventNotifierOptionsModel(SqlAlchemyBase, BaseMixins):
|
||||
@@ -62,6 +63,10 @@ class GroupEventNotifierModel(SqlAlchemyBase, BaseMixins):
|
||||
"Group", back_populates="group_event_notifiers", single_parent=True
|
||||
)
|
||||
group_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("groups.id"), index=True)
|
||||
household: Mapped[Optional["Household"]] = orm.relationship(
|
||||
"Household", back_populates="group_event_notifiers", single_parent=True
|
||||
)
|
||||
household_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("households.id"), index=True)
|
||||
|
||||
options: Mapped[GroupEventNotifierOptionsModel] = orm.relationship(
|
||||
GroupEventNotifierOptionsModel, uselist=False, cascade="all, delete-orphan"
|
||||
80
mealie/db/models/household/household.py
Normal file
80
mealie/db/models/household/household.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from pydantic import ConfigDict
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..group import Group
|
||||
from ..users import User
|
||||
from . import (
|
||||
CookBook,
|
||||
GroupEventNotifierModel,
|
||||
GroupInviteToken,
|
||||
GroupRecipeAction,
|
||||
GroupWebhooksModel,
|
||||
HouseholdPreferencesModel,
|
||||
)
|
||||
|
||||
|
||||
class Household(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "households"
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint("group_id", "name", name="household_name_group_id_key"),
|
||||
sa.UniqueConstraint("group_id", "slug", name="household_slug_group_id_key"),
|
||||
)
|
||||
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
name: Mapped[str] = mapped_column(sa.String, index=True, nullable=False)
|
||||
slug: Mapped[str | None] = mapped_column(sa.String, index=True)
|
||||
|
||||
invite_tokens: Mapped[list["GroupInviteToken"]] = orm.relationship(
|
||||
"GroupInviteToken", back_populates="household", cascade="all, delete-orphan"
|
||||
)
|
||||
preferences: Mapped["HouseholdPreferencesModel"] = orm.relationship(
|
||||
"HouseholdPreferencesModel",
|
||||
back_populates="household",
|
||||
uselist=False,
|
||||
single_parent=True,
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
group_id: Mapped[GUID] = mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
group: Mapped["Group"] = orm.relationship("Group", back_populates="households")
|
||||
users: Mapped[list["User"]] = orm.relationship("User", back_populates="household")
|
||||
|
||||
COMMON_ARGS = {
|
||||
"back_populates": "household",
|
||||
"cascade": "all, delete-orphan",
|
||||
"single_parent": True,
|
||||
}
|
||||
|
||||
recipe_actions: Mapped[list["GroupRecipeAction"]] = orm.relationship("GroupRecipeAction", **COMMON_ARGS)
|
||||
cookbooks: Mapped[list["CookBook"]] = orm.relationship("CookBook", **COMMON_ARGS)
|
||||
|
||||
webhooks: Mapped[list["GroupWebhooksModel"]] = orm.relationship("GroupWebhooksModel", **COMMON_ARGS)
|
||||
group_event_notifiers: Mapped[list["GroupEventNotifierModel"]] = orm.relationship(
|
||||
"GroupEventNotifierModel", **COMMON_ARGS
|
||||
)
|
||||
|
||||
model_config = ConfigDict(
|
||||
exclude={
|
||||
"users",
|
||||
"webhooks",
|
||||
"recipe_actions",
|
||||
"cookbooks",
|
||||
"preferences",
|
||||
"invite_tokens",
|
||||
"group_event_notifiers",
|
||||
"group",
|
||||
}
|
||||
)
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
@@ -8,7 +8,8 @@ from .._model_utils import guid
|
||||
from .._model_utils.auto_init import auto_init
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .group import Group
|
||||
from ..group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class GroupInviteToken(SqlAlchemyBase, BaseMixins):
|
||||
@@ -18,6 +19,8 @@ class GroupInviteToken(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
group_id: Mapped[guid.GUID | None] = mapped_column(guid.GUID, ForeignKey("groups.id"), index=True)
|
||||
group: Mapped[Optional["Group"]] = orm.relationship("Group", back_populates="invite_tokens")
|
||||
household_id: Mapped[guid.GUID | None] = mapped_column(guid.GUID, ForeignKey("households.id"), index=True)
|
||||
household: Mapped[Optional["Household"]] = orm.relationship("Household", back_populates="invite_tokens")
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_):
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import date
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import Date, ForeignKey, String, orm
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from mealie.db.models.recipe.tag import Tag, plan_rules_to_tags
|
||||
@@ -12,9 +13,10 @@ from .._model_utils.guid import GUID
|
||||
from ..recipe.category import Category, plan_rules_to_categories
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..group import Group
|
||||
from ..recipe import RecipeModel
|
||||
from ..users import User
|
||||
from .group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class GroupMealPlanRules(BaseMixins, SqlAlchemyBase):
|
||||
@@ -22,6 +24,7 @@ class GroupMealPlanRules(BaseMixins, SqlAlchemyBase):
|
||||
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("groups.id"), nullable=False, index=True)
|
||||
household_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("households.id"), index=True)
|
||||
|
||||
day: Mapped[str] = mapped_column(
|
||||
String, nullable=False, default="unset"
|
||||
@@ -41,13 +44,15 @@ class GroupMealPlanRules(BaseMixins, SqlAlchemyBase):
|
||||
class GroupMealPlan(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "group_meal_plans"
|
||||
|
||||
date: Mapped[date] = mapped_column(Date, index=True, nullable=False)
|
||||
date: Mapped[datetime.date] = mapped_column(Date, index=True, nullable=False)
|
||||
entry_type: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
||||
title: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
||||
text: Mapped[str] = mapped_column(String, nullable=False)
|
||||
|
||||
group_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("groups.id"), index=True)
|
||||
group: Mapped[Optional["Group"]] = orm.relationship("Group", back_populates="mealplans")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("user", "household_id")
|
||||
household: AssociationProxy["Household"] = association_proxy("user", "household")
|
||||
user_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("users.id"), index=True)
|
||||
user: Mapped[Optional["User"]] = orm.relationship("User", back_populates="mealplans")
|
||||
|
||||
37
mealie/db/models/household/preferences.py
Normal file
37
mealie/db/models/household/preferences.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .household import Household
|
||||
|
||||
|
||||
class HouseholdPreferencesModel(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "household_preferences"
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
|
||||
household_id: Mapped[GUID | None] = mapped_column(GUID, sa.ForeignKey("households.id"), nullable=False, index=True)
|
||||
household: Mapped[Optional["Household"]] = orm.relationship("Household", back_populates="preferences")
|
||||
group_id: AssociationProxy[GUID] = association_proxy("household", "group_id")
|
||||
|
||||
private_household: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
first_day_of_week: Mapped[int | None] = mapped_column(sa.Integer, default=0)
|
||||
|
||||
# Recipe Defaults
|
||||
recipe_public: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
recipe_show_nutrition: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_show_assets: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_landscape_view: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_disable_comments: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_disable_amount: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
@@ -8,7 +8,8 @@ from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .group import Group
|
||||
from ..group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class GroupRecipeAction(SqlAlchemyBase, BaseMixins):
|
||||
@@ -16,6 +17,8 @@ class GroupRecipeAction(SqlAlchemyBase, BaseMixins):
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("groups.id"), index=True)
|
||||
group: Mapped["Group"] = relationship("Group", back_populates="recipe_actions", single_parent=True)
|
||||
household_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("households.id"), index=True)
|
||||
household: Mapped["Household"] = relationship("Household", back_populates="recipe_actions")
|
||||
|
||||
action_type: Mapped[str] = mapped_column(String, index=True)
|
||||
title: Mapped[str] = mapped_column(String, index=True)
|
||||
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from sqlalchemy import Boolean, Float, ForeignKey, Integer, String, UniqueConstraint, event, orm
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
@@ -16,22 +17,30 @@ from .._model_utils.guid import GUID
|
||||
from ..recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..group import Group
|
||||
from ..recipe import RecipeModel
|
||||
from ..users import User
|
||||
from .group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class ShoppingListItemRecipeReference(BaseMixins, SqlAlchemyBase):
|
||||
__tablename__ = "shopping_list_item_recipe_reference"
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
|
||||
shopping_list_item: Mapped["ShoppingListItem"] = orm.relationship(
|
||||
"ShoppingListItem", back_populates="recipe_references"
|
||||
)
|
||||
shopping_list_item_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("shopping_list_items.id"), primary_key=True)
|
||||
|
||||
recipe_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("recipes.id"), index=True)
|
||||
recipe: Mapped[Optional["RecipeModel"]] = orm.relationship("RecipeModel", back_populates="shopping_list_item_refs")
|
||||
recipe_quantity: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
recipe_scale: Mapped[float] = mapped_column(Float, default=1)
|
||||
recipe_note: Mapped[str | None] = mapped_column(String)
|
||||
|
||||
group_id: AssociationProxy[GUID] = association_proxy("shopping_list_item", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("shopping_list_item", "household_id")
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
@@ -42,8 +51,12 @@ class ShoppingListItem(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
# Id's
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
shopping_list: Mapped["ShoppingList"] = orm.relationship("ShoppingList", back_populates="list_items")
|
||||
shopping_list_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("shopping_lists.id"), index=True)
|
||||
|
||||
group_id: AssociationProxy[GUID] = association_proxy("shopping_list", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("shopping_list", "household_id")
|
||||
|
||||
# Meta
|
||||
is_ingredient: Mapped[bool | None] = mapped_column(Boolean, default=True)
|
||||
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0, index=True)
|
||||
@@ -85,7 +98,10 @@ class ShoppingListRecipeReference(BaseMixins, SqlAlchemyBase):
|
||||
__tablename__ = "shopping_list_recipe_reference"
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
|
||||
shopping_list: Mapped["ShoppingList"] = orm.relationship("ShoppingList", back_populates="recipe_references")
|
||||
shopping_list_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("shopping_lists.id"), primary_key=True)
|
||||
group_id: AssociationProxy[GUID] = association_proxy("shopping_list", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("shopping_list", "household_id")
|
||||
|
||||
recipe_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("recipes.id"), index=True)
|
||||
recipe: Mapped[Optional["RecipeModel"]] = orm.relationship(
|
||||
@@ -107,10 +123,15 @@ class ShoppingListMultiPurposeLabel(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
shopping_list_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("shopping_lists.id"), primary_key=True)
|
||||
shopping_list: Mapped["ShoppingList"] = orm.relationship("ShoppingList", back_populates="label_settings")
|
||||
|
||||
label_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("multi_purpose_labels.id"), primary_key=True)
|
||||
label: Mapped["MultiPurposeLabel"] = orm.relationship(
|
||||
"MultiPurposeLabel", back_populates="shopping_lists_label_settings"
|
||||
)
|
||||
|
||||
group_id: AssociationProxy[GUID] = association_proxy("shopping_list", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("shopping_list", "household_id")
|
||||
|
||||
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
model_config = ConfigDict(exclude={"label"})
|
||||
|
||||
@@ -125,6 +146,8 @@ class ShoppingList(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
group_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("groups.id"), nullable=False, index=True)
|
||||
group: Mapped["Group"] = orm.relationship("Group", back_populates="shopping_lists")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("user", "household_id")
|
||||
household: AssociationProxy["Household"] = association_proxy("user", "household")
|
||||
user_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("users.id"), nullable=False, index=True)
|
||||
user: Mapped["User"] = orm.relationship("User", back_populates="shopping_lists")
|
||||
|
||||
@@ -178,7 +201,7 @@ session_buffer_context = ContextVar("session_buffer", default=SessionBuffer())
|
||||
@event.listens_for(ShoppingListItem, "after_update")
|
||||
@event.listens_for(ShoppingListItem, "after_delete")
|
||||
def buffer_shopping_list_updates(_, connection, target: ShoppingListItem):
|
||||
"""Adds the shopping list id to the session buffer so its `update_at` property can be updated later"""
|
||||
"""Adds the shopping list id to the session buffer so its `updated_at` property can be updated later"""
|
||||
|
||||
session_buffer = session_buffer_context.get()
|
||||
session_buffer.add(target.shopping_list_id)
|
||||
@@ -186,7 +209,7 @@ def buffer_shopping_list_updates(_, connection, target: ShoppingListItem):
|
||||
|
||||
@event.listens_for(orm.Session, "after_flush")
|
||||
def update_shopping_lists(session: orm.Session, _):
|
||||
"""Pulls all pending shopping list updates from the buffer and updates their `update_at` property"""
|
||||
"""Pulls all pending shopping list updates from the buffer and updates their `updated_at` property"""
|
||||
|
||||
session_buffer = session_buffer_context.get()
|
||||
if not session_buffer.shopping_list_ids:
|
||||
@@ -204,7 +227,7 @@ def update_shopping_lists(session: orm.Session, _):
|
||||
if not shopping_list:
|
||||
continue
|
||||
|
||||
shopping_list.update_at = datetime.now(timezone.utc)
|
||||
shopping_list.updated_at = datetime.now(timezone.utc)
|
||||
local_session.commit()
|
||||
except Exception:
|
||||
local_session.rollback()
|
||||
@@ -9,7 +9,8 @@ from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .group import Group
|
||||
from ..group import Group
|
||||
from .household import Household
|
||||
|
||||
|
||||
class GroupWebhooksModel(SqlAlchemyBase, BaseMixins):
|
||||
@@ -18,6 +19,10 @@ class GroupWebhooksModel(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
group: Mapped[Optional["Group"]] = orm.relationship("Group", back_populates="webhooks", single_parent=True)
|
||||
group_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("groups.id"), index=True)
|
||||
household: Mapped[Optional["Household"]] = orm.relationship(
|
||||
"Household", back_populates="webhooks", single_parent=True
|
||||
)
|
||||
household_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("households.id"), index=True)
|
||||
|
||||
enabled: Mapped[bool | None] = mapped_column(Boolean, default=False)
|
||||
name: Mapped[str | None] = mapped_column(String)
|
||||
@@ -27,7 +32,7 @@ class GroupWebhooksModel(SqlAlchemyBase, BaseMixins):
|
||||
webhook_type: Mapped[str | None] = mapped_column(String, default="") # Future use for different types of webhooks
|
||||
scheduled_time: Mapped[time | None] = mapped_column(Time, default=lambda: datetime.now(timezone.utc).time())
|
||||
|
||||
# Columne is no longer used but is kept for since it's super annoying to
|
||||
# Column is no longer used but is kept for since it's super annoying to
|
||||
# delete a column in SQLite and it's not a big deal to keep it around
|
||||
time: Mapped[str | None] = mapped_column(String, default="00:00")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import ForeignKey, String, orm
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
|
||||
@@ -21,6 +22,9 @@ class RecipeComment(SqlAlchemyBase, BaseMixins):
|
||||
recipe_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("recipes.id"), nullable=False, index=True)
|
||||
recipe: Mapped["RecipeModel"] = orm.relationship("RecipeModel", back_populates="comments")
|
||||
|
||||
group_id: AssociationProxy[GUID] = association_proxy("recipe", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("recipe", "household_id")
|
||||
|
||||
# User Link
|
||||
user_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("users.id"), nullable=False, index=True)
|
||||
user: Mapped["User"] = orm.relationship(
|
||||
|
||||
@@ -5,6 +5,7 @@ import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from pydantic import ConfigDict
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
from sqlalchemy.orm import Mapped, mapped_column, validates
|
||||
from sqlalchemy.orm.attributes import get_history
|
||||
@@ -31,7 +32,8 @@ from .tag import recipes_to_tags
|
||||
from .tool import recipes_to_tools
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..group import Group, GroupMealPlan, ShoppingListItemRecipeReference, ShoppingListRecipeReference
|
||||
from ..group import Group, GroupMealPlan
|
||||
from ..household import Household, ShoppingListItemRecipeReference, ShoppingListRecipeReference
|
||||
from ..users import User
|
||||
from . import Category, Tag, Tool
|
||||
|
||||
@@ -49,19 +51,25 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
|
||||
group_id: Mapped[GUID] = mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
group: Mapped["Group"] = orm.relationship("Group", back_populates="recipes", foreign_keys=[group_id])
|
||||
|
||||
household_id: AssociationProxy[GUID] = association_proxy("user", "household_id")
|
||||
household: AssociationProxy["Household"] = association_proxy("user", "household")
|
||||
|
||||
user_id: Mapped[GUID | None] = mapped_column(GUID, sa.ForeignKey("users.id", use_alter=True), index=True)
|
||||
user: Mapped["User"] = orm.relationship("User", uselist=False, foreign_keys=[user_id])
|
||||
|
||||
rating: Mapped[float | None] = mapped_column(sa.Float, index=True, nullable=True)
|
||||
rated_by: Mapped[list["User"]] = orm.relationship(
|
||||
"User", secondary=UserToRecipe.__tablename__, back_populates="rated_recipes"
|
||||
"User",
|
||||
secondary=UserToRecipe.__tablename__,
|
||||
back_populates="rated_recipes",
|
||||
overlaps="recipe,favorited_by,favorited_recipes",
|
||||
)
|
||||
favorited_by: Mapped[list["User"]] = orm.relationship(
|
||||
"User",
|
||||
secondary=UserToRecipe.__tablename__,
|
||||
primaryjoin="and_(RecipeModel.id==UserToRecipe.recipe_id, UserToRecipe.is_favorite==True)",
|
||||
back_populates="favorite_recipes",
|
||||
viewonly=True,
|
||||
overlaps="recipe,rated_by,rated_recipes",
|
||||
)
|
||||
|
||||
meal_entries: Mapped[list["GroupMealPlan"]] = orm.relationship(
|
||||
|
||||
@@ -2,6 +2,7 @@ from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
@@ -21,6 +22,9 @@ class RecipeTimelineEvent(SqlAlchemyBase, BaseMixins):
|
||||
recipe_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("recipes.id"), nullable=False, index=True)
|
||||
recipe: Mapped["RecipeModel"] = relationship("RecipeModel", back_populates="timeline_events")
|
||||
|
||||
group_id: AssociationProxy[GUID] = association_proxy("recipe", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("recipe", "household_id")
|
||||
|
||||
# Related User (Actor)
|
||||
user_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("users.id"), nullable=False, index=True)
|
||||
user: Mapped["User"] = relationship(
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import Boolean, Column, Float, ForeignKey, UniqueConstraint, event
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils.auto_init import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..recipe import RecipeModel
|
||||
|
||||
|
||||
class UserToRecipe(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "users_to_recipes"
|
||||
@@ -14,7 +20,11 @@ class UserToRecipe(SqlAlchemyBase, BaseMixins):
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
|
||||
user_id = Column(GUID, ForeignKey("users.id"), index=True, primary_key=True)
|
||||
recipe: Mapped["RecipeModel"] = relationship("RecipeModel")
|
||||
recipe_id = Column(GUID, ForeignKey("recipes.id"), index=True, primary_key=True)
|
||||
group_id: AssociationProxy[GUID] = association_proxy("recipe", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("recipe", "household_id")
|
||||
|
||||
rating = Column(Float, index=True, nullable=True)
|
||||
is_favorite = Column(Boolean, index=True, nullable=False)
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, Integer, String, orm
|
||||
from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, Integer, String, orm, select
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.orm import Mapped, Session, mapped_column
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.db.models._model_utils.auto_init import auto_init
|
||||
@@ -16,8 +17,9 @@ from .user_to_recipe import UserToRecipe
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..group import Group
|
||||
from ..group.mealplan import GroupMealPlan
|
||||
from ..group.shopping_list import ShoppingList
|
||||
from ..household import Household
|
||||
from ..household.mealplan import GroupMealPlan
|
||||
from ..household.shopping_list import ShoppingList
|
||||
from ..recipe import RecipeComment, RecipeModel, RecipeTimelineEvent
|
||||
from .password_reset import PasswordResetModel
|
||||
|
||||
@@ -30,6 +32,9 @@ class LongLiveToken(SqlAlchemyBase, BaseMixins):
|
||||
user_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("users.id"), index=True)
|
||||
user: Mapped[Optional["User"]] = orm.relationship("User")
|
||||
|
||||
group_id: AssociationProxy[GUID] = association_proxy("user", "group_id")
|
||||
household_id: AssociationProxy[GUID] = association_proxy("user", "household_id")
|
||||
|
||||
def __init__(self, name, token, user_id, **_) -> None:
|
||||
self.name = name
|
||||
self.token = token
|
||||
@@ -55,6 +60,8 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
group_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("groups.id"), nullable=False, index=True)
|
||||
group: Mapped["Group"] = orm.relationship("Group", back_populates="users")
|
||||
household_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("households.id"), nullable=True, index=True)
|
||||
household: Mapped["Household"] = orm.relationship("Household", back_populates="users")
|
||||
|
||||
cache_key: Mapped[str | None] = mapped_column(String, default="1234")
|
||||
login_attemps: Mapped[int | None] = mapped_column(Integer, default=0)
|
||||
@@ -85,14 +92,17 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||
)
|
||||
shopping_lists: Mapped[Optional["ShoppingList"]] = orm.relationship("ShoppingList", **sp_args)
|
||||
rated_recipes: Mapped[list["RecipeModel"]] = orm.relationship(
|
||||
"RecipeModel", secondary=UserToRecipe.__tablename__, back_populates="rated_by"
|
||||
"RecipeModel",
|
||||
secondary=UserToRecipe.__tablename__,
|
||||
back_populates="rated_by",
|
||||
overlaps="recipe,favorited_by,favorited_recipes",
|
||||
)
|
||||
favorite_recipes: Mapped[list["RecipeModel"]] = orm.relationship(
|
||||
"RecipeModel",
|
||||
secondary=UserToRecipe.__tablename__,
|
||||
primaryjoin="and_(User.id==UserToRecipe.user_id, UserToRecipe.is_favorite==True)",
|
||||
back_populates="favorited_by",
|
||||
viewonly=True,
|
||||
overlaps="recipe,rated_by,rated_recipes",
|
||||
)
|
||||
model_config = ConfigDict(
|
||||
exclude={
|
||||
@@ -102,6 +112,7 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||
"can_invite",
|
||||
"can_organize",
|
||||
"group",
|
||||
"household",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -109,15 +120,33 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||
def group_slug(self) -> str:
|
||||
return self.group.slug
|
||||
|
||||
@hybrid_property
|
||||
def household_slug(self) -> str:
|
||||
return self.household.slug
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, session, full_name, password, group: str | None = None, **kwargs) -> None:
|
||||
if group is None:
|
||||
def __init__(
|
||||
self, session: Session, full_name, password, group: str | None = None, household: str | None = None, **kwargs
|
||||
) -> None:
|
||||
if group is None or household is None:
|
||||
settings = get_app_settings()
|
||||
group = settings.DEFAULT_GROUP
|
||||
group = group or settings.DEFAULT_GROUP
|
||||
household = household or settings.DEFAULT_HOUSEHOLD
|
||||
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.household import Household
|
||||
|
||||
self.group = Group.get_by_name(session, group)
|
||||
self.group = session.execute(select(Group).filter(Group.name == group)).scalars().one_or_none()
|
||||
if self.group:
|
||||
self.household = (
|
||||
session.execute(
|
||||
select(Household).filter(Household.name == household, Household.group_id == self.group.id)
|
||||
)
|
||||
.scalars()
|
||||
.one_or_none()
|
||||
)
|
||||
else:
|
||||
self.household = None
|
||||
|
||||
self.rated_recipes = []
|
||||
|
||||
@@ -129,14 +158,25 @@ class User(SqlAlchemyBase, BaseMixins):
|
||||
self._set_permissions(**kwargs)
|
||||
|
||||
@auto_init()
|
||||
def update(self, full_name, email, group, username, session=None, **kwargs):
|
||||
def update(self, session: Session, full_name, email, group, household, username, **kwargs):
|
||||
self.username = username
|
||||
self.full_name = full_name
|
||||
self.email = email
|
||||
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.household import Household
|
||||
|
||||
self.group = Group.get_by_name(session, group)
|
||||
self.group = session.execute(select(Group).filter(Group.name == group)).scalars().one_or_none()
|
||||
if self.group:
|
||||
self.household = (
|
||||
session.execute(
|
||||
select(Household).filter(Household.name == household, Household.group_id == self.group.id)
|
||||
)
|
||||
.scalars()
|
||||
.one_or_none()
|
||||
)
|
||||
else:
|
||||
self.household = None
|
||||
|
||||
if self.username is None:
|
||||
self.username = full_name
|
||||
|
||||
Reference in New Issue
Block a user