mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	Set up alembic migration usage (#954)
* Set up alembic migration usage * Fix import order, add isort as alembic hook * Only run migrations if needed * Include date as part of migration file name for better sorting * Skip initial migrations if tables already exist This eases the transition from a pre-alembic state * Fix deprecation warning in alembic revision * Add alembic test stubs
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							a897e180ed
						
					
				
				
					commit
					fdfb5b1a5e
				
			
							
								
								
									
										72
									
								
								alembic.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								alembic.ini
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | # A generic, single database configuration. | ||||||
|  |  | ||||||
|  | [alembic] | ||||||
|  | # path to migration scripts | ||||||
|  | script_location = alembic | ||||||
|  |  | ||||||
|  | # template used to generate migration files | ||||||
|  | file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d:%%(minute).2d:%%(second).2d_%%(rev)s_%%(slug)s | ||||||
|  |  | ||||||
|  | # sys.path path, will be prepended to sys.path if present. | ||||||
|  | # defaults to the current working directory. | ||||||
|  | prepend_sys_path = . | ||||||
|  |  | ||||||
|  | # timezone to use when rendering the date within the migration file | ||||||
|  | # as well as the filename. | ||||||
|  | # If specified, requires the python-dateutil library that can be | ||||||
|  | # installed by adding `alembic[tz]` to the pip requirements | ||||||
|  | # string value is passed to dateutil.tz.gettz() | ||||||
|  | # leave blank for localtime | ||||||
|  | # timezone = | ||||||
|  |  | ||||||
|  | # max length of characters to apply to the | ||||||
|  | # "slug" field | ||||||
|  | # truncate_slug_length = 40 | ||||||
|  |  | ||||||
|  | # set to 'true' to run the environment during | ||||||
|  | # the 'revision' command, regardless of autogenerate | ||||||
|  | # revision_environment = false | ||||||
|  |  | ||||||
|  | # set to 'true' to allow .pyc and .pyo files without | ||||||
|  | # a source .py file to be detected as revisions in the | ||||||
|  | # versions/ directory | ||||||
|  | # sourceless = false | ||||||
|  |  | ||||||
|  | # version location specification; This defaults | ||||||
|  | # to alembic/versions.  When using multiple version | ||||||
|  | # directories, initial revisions must be specified with --version-path. | ||||||
|  | # The path separator used here should be the separator specified by "version_path_separator" | ||||||
|  | # version_locations = %(here)s/bar:%(here)s/bat:alembic/versions | ||||||
|  |  | ||||||
|  | # version path separator; As mentioned above, this is the character used to split | ||||||
|  | # version_locations. Valid values are: | ||||||
|  | # | ||||||
|  | # version_path_separator = : | ||||||
|  | # version_path_separator = ; | ||||||
|  | # version_path_separator = space | ||||||
|  | version_path_separator = os  # default: use os.pathsep | ||||||
|  |  | ||||||
|  | # the output encoding used when revision files | ||||||
|  | # are written from script.py.mako | ||||||
|  | # output_encoding = utf-8 | ||||||
|  |  | ||||||
|  | # Will be replaced in alembic/env.py with the actual url | ||||||
|  | sqlalchemy.url = | ||||||
|  |  | ||||||
|  |  | ||||||
|  | [post_write_hooks] | ||||||
|  | # post_write_hooks defines scripts or Python functions that are run | ||||||
|  | # on newly generated revision scripts.  See the documentation for further | ||||||
|  | # detail and examples | ||||||
|  |  | ||||||
|  | hooks = isort, black | ||||||
|  |  | ||||||
|  | # format using "isort" - use the console_scripts runner, against the "isort" entrypoint | ||||||
|  | isort.type = console_scripts | ||||||
|  | isort.entrypoint = isort | ||||||
|  | isort.options = REVISION_SCRIPT_FILENAME | ||||||
|  |  | ||||||
|  | # format using "black" - use the console_scripts runner, against the "black" entrypoint | ||||||
|  | black.type = console_scripts | ||||||
|  | black.entrypoint = black | ||||||
|  | black.options = REVISION_SCRIPT_FILENAME | ||||||
							
								
								
									
										1
									
								
								alembic/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								alembic/README
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | Generic single-database configuration. | ||||||
							
								
								
									
										77
									
								
								alembic/env.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								alembic/env.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | from sqlalchemy import engine_from_config, pool | ||||||
|  |  | ||||||
|  | import mealie.db.models._all_models  # noqa: F401 | ||||||
|  | from alembic import context | ||||||
|  | from mealie.core.config import get_app_settings | ||||||
|  | from mealie.db.models._model_base import SqlAlchemyBase | ||||||
|  |  | ||||||
|  | # this is the Alembic Config object, which provides | ||||||
|  | # access to the values within the .ini file in use. | ||||||
|  | config = context.config | ||||||
|  |  | ||||||
|  | # add your model's MetaData object here | ||||||
|  | # for 'autogenerate' support | ||||||
|  | # from myapp import mymodel | ||||||
|  | # target_metadata = mymodel.Base.metadata | ||||||
|  | target_metadata = SqlAlchemyBase.metadata | ||||||
|  |  | ||||||
|  | # other values from the config, defined by the needs of env.py, | ||||||
|  | # can be acquired: | ||||||
|  | # my_important_option = config.get_main_option("my_important_option") | ||||||
|  | # ... etc. | ||||||
|  |  | ||||||
|  | # Set DB url from config | ||||||
|  | settings = get_app_settings() | ||||||
|  | config.set_main_option("sqlalchemy.url", settings.DB_URL) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def run_migrations_offline(): | ||||||
|  |     """Run migrations in 'offline' mode. | ||||||
|  |  | ||||||
|  |     This configures the context with just a URL | ||||||
|  |     and not an Engine, though an Engine is acceptable | ||||||
|  |     here as well.  By skipping the Engine creation | ||||||
|  |     we don't even need a DBAPI to be available. | ||||||
|  |  | ||||||
|  |     Calls to context.execute() here emit the given string to the | ||||||
|  |     script output. | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     url = config.get_main_option("sqlalchemy.url") | ||||||
|  |     context.configure( | ||||||
|  |         url=url, | ||||||
|  |         target_metadata=target_metadata, | ||||||
|  |         literal_binds=True, | ||||||
|  |         dialect_opts={"paramstyle": "named"}, | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     with context.begin_transaction(): | ||||||
|  |         context.run_migrations() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def run_migrations_online(): | ||||||
|  |     """Run migrations in 'online' mode. | ||||||
|  |  | ||||||
|  |     In this scenario we need to create an Engine | ||||||
|  |     and associate a connection with the context. | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     connectable = engine_from_config( | ||||||
|  |         config.get_section(config.config_ini_section), | ||||||
|  |         prefix="sqlalchemy.", | ||||||
|  |         poolclass=pool.NullPool, | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     with connectable.connect() as connection: | ||||||
|  |         context.configure( | ||||||
|  |             connection=connection, target_metadata=target_metadata, user_module_prefix="mealie.db.migration_types." | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         with context.begin_transaction(): | ||||||
|  |             context.run_migrations() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if context.is_offline_mode(): | ||||||
|  |     run_migrations_offline() | ||||||
|  | else: | ||||||
|  |     run_migrations_online() | ||||||
							
								
								
									
										26
									
								
								alembic/script.py.mako
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								alembic/script.py.mako
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | """${message} | ||||||
|  |  | ||||||
|  | Revision ID: ${up_revision} | ||||||
|  | Revises: ${down_revision | comma,n} | ||||||
|  | Create Date: ${create_date} | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | import sqlalchemy as sa | ||||||
|  |  | ||||||
|  | import mealie.db.migration_types | ||||||
|  | from alembic import op | ||||||
|  | ${imports if imports else ""} | ||||||
|  |  | ||||||
|  | # revision identifiers, used by Alembic. | ||||||
|  | revision = ${repr(up_revision)} | ||||||
|  | down_revision = ${repr(down_revision)} | ||||||
|  | branch_labels = ${repr(branch_labels)} | ||||||
|  | depends_on = ${repr(depends_on)} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def upgrade(): | ||||||
|  |     ${upgrades if upgrades else "pass"} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def downgrade(): | ||||||
|  |     ${downgrades if downgrades else "pass"} | ||||||
| @@ -0,0 +1,920 @@ | |||||||
|  | """Initial tables | ||||||
|  |  | ||||||
|  | Revision ID: 6b0f5f32d602 | ||||||
|  | Revises:  | ||||||
|  | Create Date: 2022-02-21 19:56:24.351115 | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | import sqlalchemy as sa | ||||||
|  | from sqlalchemy import engine_from_config | ||||||
|  |  | ||||||
|  | import mealie.db.migration_types | ||||||
|  | from alembic import op | ||||||
|  |  | ||||||
|  | # revision identifiers, used by Alembic. | ||||||
|  | revision = "6b0f5f32d602" | ||||||
|  | down_revision = None | ||||||
|  | branch_labels = None | ||||||
|  | depends_on = None | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Adapted from https://improveandrepeat.com/2021/09/python-friday-87-handling-pre-existing-tables-with-alembic-and-sqlalchemy/ | ||||||
|  | def table_exists(table, schema=None): | ||||||
|  |     config = op.get_context().config | ||||||
|  |     engine = engine_from_config(config.get_section(config.config_ini_section), prefix="sqlalchemy.") | ||||||
|  |     insp = sa.inspect(engine) | ||||||
|  |     return insp.has_table(table, schema) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def upgrade(): | ||||||
|  |     # Only create initial tables if they don't exist yet, to ease transition from pre-alembic state | ||||||
|  |     if table_exists("users"): | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     op.create_table( | ||||||
|  |         "groups", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_groups_name"), "groups", ["name"], unique=True) | ||||||
|  |     op.create_table( | ||||||
|  |         "categories", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("slug", sa.String(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |         sa.UniqueConstraint("slug", "group_id", name="category_slug_group_id_key"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_categories_group_id"), "categories", ["group_id"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_categories_name"), "categories", ["name"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_categories_slug"), "categories", ["slug"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "cookbooks", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("position", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("slug", sa.String(), nullable=False), | ||||||
|  |         sa.Column("description", sa.String(), nullable=True), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_data_exports", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("filename", sa.String(), nullable=False), | ||||||
|  |         sa.Column("path", sa.String(), nullable=False), | ||||||
|  |         sa.Column("size", sa.String(), nullable=False), | ||||||
|  |         sa.Column("expires", sa.String(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_group_data_exports_group_id"), "group_data_exports", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_events_notifiers", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("enabled", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("apprise_url", sa.String(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_group_events_notifiers_group_id"), "group_events_notifiers", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_meal_plan_rules", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("day", sa.String(), nullable=False), | ||||||
|  |         sa.Column("entry_type", sa.String(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_preferences", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("private_group", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("first_day_of_week", sa.Integer(), nullable=True), | ||||||
|  |         sa.Column("recipe_public", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("recipe_show_nutrition", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("recipe_show_assets", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("recipe_landscape_view", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("recipe_disable_comments", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("recipe_disable_amount", sa.Boolean(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_group_preferences_group_id"), "group_preferences", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_reports", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("status", sa.String(), nullable=False), | ||||||
|  |         sa.Column("category", sa.String(), nullable=False), | ||||||
|  |         sa.Column("timestamp", sa.DateTime(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_group_reports_category"), "group_reports", ["category"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_group_reports_group_id"), "group_reports", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "ingredient_units", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=True), | ||||||
|  |         sa.Column("description", sa.String(), nullable=True), | ||||||
|  |         sa.Column("abbreviation", sa.String(), nullable=True), | ||||||
|  |         sa.Column("fraction", sa.Boolean(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "invite_tokens", | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("token", sa.String(), nullable=False), | ||||||
|  |         sa.Column("uses_left", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_invite_tokens_token"), "invite_tokens", ["token"], unique=True) | ||||||
|  |     op.create_table( | ||||||
|  |         "multi_purpose_labels", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(length=255), nullable=False), | ||||||
|  |         sa.Column("color", sa.String(length=10), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_multi_purpose_labels_group_id"), "multi_purpose_labels", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipes", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("slug", sa.String(), nullable=True), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("description", sa.String(), nullable=True), | ||||||
|  |         sa.Column("image", sa.String(), nullable=True), | ||||||
|  |         sa.Column("total_time", sa.String(), nullable=True), | ||||||
|  |         sa.Column("prep_time", sa.String(), nullable=True), | ||||||
|  |         sa.Column("perform_time", sa.String(), nullable=True), | ||||||
|  |         sa.Column("cook_time", sa.String(), nullable=True), | ||||||
|  |         sa.Column("recipe_yield", sa.String(), nullable=True), | ||||||
|  |         sa.Column("recipeCuisine", sa.String(), nullable=True), | ||||||
|  |         sa.Column("rating", sa.Integer(), nullable=True), | ||||||
|  |         sa.Column("org_url", sa.String(), nullable=True), | ||||||
|  |         sa.Column("date_added", sa.Date(), nullable=True), | ||||||
|  |         sa.Column("date_updated", sa.DateTime(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint(["user_id"], ["users.id"], use_alter=True), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |         sa.UniqueConstraint("slug", "group_id", name="recipe_slug_group_id_key"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_recipes_group_id"), "recipes", ["group_id"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_recipes_slug"), "recipes", ["slug"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_recipes_user_id"), "recipes", ["user_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "server_tasks", | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("completed_date", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("status", sa.String(), nullable=False), | ||||||
|  |         sa.Column("log", sa.String(), nullable=True), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_server_tasks_group_id"), "server_tasks", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "shopping_lists", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_shopping_lists_group_id"), "shopping_lists", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "tags", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("slug", sa.String(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |         sa.UniqueConstraint("slug", "group_id", name="tags_slug_group_id_key"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_tags_group_id"), "tags", ["group_id"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_tags_name"), "tags", ["name"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_tags_slug"), "tags", ["slug"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "tools", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("slug", sa.String(), nullable=False), | ||||||
|  |         sa.Column("on_hand", sa.Boolean(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |         sa.UniqueConstraint("slug", "group_id", name="tools_slug_group_id_key"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_tools_name"), "tools", ["name"], unique=True) | ||||||
|  |     op.create_index(op.f("ix_tools_slug"), "tools", ["slug"], unique=True) | ||||||
|  |     op.create_table( | ||||||
|  |         "webhook_urls", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("enabled", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("name", sa.String(), nullable=True), | ||||||
|  |         sa.Column("url", sa.String(), nullable=True), | ||||||
|  |         sa.Column("time", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_webhook_urls_group_id"), "webhook_urls", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "api_extras", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("recipee_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("key_name", sa.String(), nullable=True), | ||||||
|  |         sa.Column("value", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipee_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "cookbooks_to_categories", | ||||||
|  |         sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["category_id"], | ||||||
|  |             ["categories.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["cookbook_id"], | ||||||
|  |             ["cookbooks.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_events_notifier_options", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("event_notifier_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("recipe_created", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("recipe_updated", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("recipe_deleted", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("user_signup", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("data_migrations", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("data_export", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("data_import", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("mealplan_entry_created", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("shopping_list_created", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("shopping_list_updated", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("shopping_list_deleted", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("cookbook_created", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("cookbook_updated", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("cookbook_deleted", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("tag_created", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("tag_updated", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("tag_deleted", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("category_created", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("category_updated", sa.Boolean(), nullable=False), | ||||||
|  |         sa.Column("category_deleted", sa.Boolean(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["event_notifier_id"], | ||||||
|  |             ["group_events_notifiers.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_meal_plans", | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("date", sa.Date(), nullable=False), | ||||||
|  |         sa.Column("entry_type", sa.String(), nullable=False), | ||||||
|  |         sa.Column("title", sa.String(), nullable=False), | ||||||
|  |         sa.Column("text", sa.String(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_group_meal_plans_date"), "group_meal_plans", ["date"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_group_meal_plans_entry_type"), "group_meal_plans", ["entry_type"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_group_meal_plans_group_id"), "group_meal_plans", ["group_id"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_group_meal_plans_recipe_id"), "group_meal_plans", ["recipe_id"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_group_meal_plans_title"), "group_meal_plans", ["title"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "group_to_categories", | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["category_id"], | ||||||
|  |             ["categories.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "ingredient_foods", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("name", sa.String(), nullable=True), | ||||||
|  |         sa.Column("description", sa.String(), nullable=True), | ||||||
|  |         sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["label_id"], | ||||||
|  |             ["multi_purpose_labels.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "notes", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("title", sa.String(), nullable=True), | ||||||
|  |         sa.Column("text", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "plan_rules_to_categories", | ||||||
|  |         sa.Column("group_plan_rule_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["category_id"], | ||||||
|  |             ["categories.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_plan_rule_id"], | ||||||
|  |             ["group_meal_plan_rules.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "plan_rules_to_tags", | ||||||
|  |         sa.Column("plan_rule_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["plan_rule_id"], | ||||||
|  |             ["group_meal_plan_rules.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["tag_id"], | ||||||
|  |             ["tags.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_assets", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("name", sa.String(), nullable=True), | ||||||
|  |         sa.Column("icon", sa.String(), nullable=True), | ||||||
|  |         sa.Column("file_name", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_instructions", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("position", sa.Integer(), nullable=True), | ||||||
|  |         sa.Column("type", sa.String(), nullable=True), | ||||||
|  |         sa.Column("title", sa.String(), nullable=True), | ||||||
|  |         sa.Column("text", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_nutrition", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("calories", sa.String(), nullable=True), | ||||||
|  |         sa.Column("fat_content", sa.String(), nullable=True), | ||||||
|  |         sa.Column("fiber_content", sa.String(), nullable=True), | ||||||
|  |         sa.Column("protein_content", sa.String(), nullable=True), | ||||||
|  |         sa.Column("carbohydrate_content", sa.String(), nullable=True), | ||||||
|  |         sa.Column("sodium_content", sa.String(), nullable=True), | ||||||
|  |         sa.Column("sugar_content", sa.String(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_settings", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("public", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("show_nutrition", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("show_assets", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("landscape_view", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("disable_amount", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("disable_comments", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("locked", sa.Boolean(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_share_tokens", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("expires_at", sa.DateTime(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_recipe_share_tokens_group_id"), "recipe_share_tokens", ["group_id"], unique=False) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipes_to_categories", | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["category_id"], | ||||||
|  |             ["categories.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipes_to_tags", | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["tag_id"], | ||||||
|  |             ["tags.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipes_to_tools", | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("tool_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["tool_id"], | ||||||
|  |             ["tools.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "report_entries", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("success", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("message", sa.String(), nullable=True), | ||||||
|  |         sa.Column("exception", sa.String(), nullable=True), | ||||||
|  |         sa.Column("timestamp", sa.DateTime(), nullable=False), | ||||||
|  |         sa.Column("report_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["report_id"], | ||||||
|  |             ["group_reports.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "shopping_list_recipe_reference", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("recipe_quantity", sa.Float(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["shopping_list_id"], | ||||||
|  |             ["shopping_lists.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id", "shopping_list_id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index( | ||||||
|  |         op.f("ix_shopping_list_recipe_reference_recipe_id"), | ||||||
|  |         "shopping_list_recipe_reference", | ||||||
|  |         ["recipe_id"], | ||||||
|  |         unique=False, | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "users", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("full_name", sa.String(), nullable=True), | ||||||
|  |         sa.Column("username", sa.String(), nullable=True), | ||||||
|  |         sa.Column("email", sa.String(), nullable=True), | ||||||
|  |         sa.Column("password", sa.String(), nullable=True), | ||||||
|  |         sa.Column("admin", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("advanced", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("cache_key", sa.String(), nullable=True), | ||||||
|  |         sa.Column("can_manage", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("can_invite", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("can_organize", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("owned_recipes_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["group_id"], | ||||||
|  |             ["groups.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["owned_recipes_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True) | ||||||
|  |     op.create_index(op.f("ix_users_full_name"), "users", ["full_name"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_users_group_id"), "users", ["group_id"], unique=False) | ||||||
|  |     op.create_index(op.f("ix_users_username"), "users", ["username"], unique=True) | ||||||
|  |     op.create_table( | ||||||
|  |         "long_live_tokens", | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("name", sa.String(), nullable=False), | ||||||
|  |         sa.Column("token", sa.String(), nullable=False), | ||||||
|  |         sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["user_id"], | ||||||
|  |             ["users.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "password_reset_tokens", | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("token", sa.String(length=64), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["user_id"], | ||||||
|  |             ["users.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |         sa.UniqueConstraint("token"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_comments", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("text", sa.String(), nullable=True), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["user_id"], | ||||||
|  |             ["users.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipe_ingredient_ref_link", | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("instruction_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("reference_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["instruction_id"], | ||||||
|  |             ["recipe_instructions.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "recipes_ingredients", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("position", sa.Integer(), nullable=True), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("title", sa.String(), nullable=True), | ||||||
|  |         sa.Column("note", sa.String(), nullable=True), | ||||||
|  |         sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("quantity", sa.Integer(), nullable=True), | ||||||
|  |         sa.Column("reference_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["food_id"], | ||||||
|  |             ["ingredient_foods.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["unit_id"], | ||||||
|  |             ["ingredient_units.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "shopping_list_items", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("is_ingredient", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("position", sa.Integer(), nullable=False), | ||||||
|  |         sa.Column("checked", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("quantity", sa.Float(), nullable=True), | ||||||
|  |         sa.Column("note", sa.String(), nullable=True), | ||||||
|  |         sa.Column("is_food", sa.Boolean(), nullable=True), | ||||||
|  |         sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["food_id"], | ||||||
|  |             ["ingredient_foods.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["label_id"], | ||||||
|  |             ["multi_purpose_labels.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["shopping_list_id"], | ||||||
|  |             ["shopping_lists.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["unit_id"], | ||||||
|  |             ["ingredient_units.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id"), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "users_to_favorites", | ||||||
|  |         sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["user_id"], | ||||||
|  |             ["users.id"], | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
|  |     op.create_table( | ||||||
|  |         "shopping_list_item_recipe_reference", | ||||||
|  |         sa.Column("created_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("update_at", sa.DateTime(), nullable=True), | ||||||
|  |         sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("shopping_list_item_id", mealie.db.migration_types.GUID(), nullable=False), | ||||||
|  |         sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True), | ||||||
|  |         sa.Column("recipe_quantity", sa.Float(), nullable=False), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["recipe_id"], | ||||||
|  |             ["recipes.id"], | ||||||
|  |         ), | ||||||
|  |         sa.ForeignKeyConstraint( | ||||||
|  |             ["shopping_list_item_id"], | ||||||
|  |             ["shopping_list_items.id"], | ||||||
|  |         ), | ||||||
|  |         sa.PrimaryKeyConstraint("id", "shopping_list_item_id"), | ||||||
|  |     ) | ||||||
|  |     op.create_index( | ||||||
|  |         op.f("ix_shopping_list_item_recipe_reference_recipe_id"), | ||||||
|  |         "shopping_list_item_recipe_reference", | ||||||
|  |         ["recipe_id"], | ||||||
|  |         unique=False, | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def downgrade(): | ||||||
|  |     op.drop_index( | ||||||
|  |         op.f("ix_shopping_list_item_recipe_reference_recipe_id"), table_name="shopping_list_item_recipe_reference" | ||||||
|  |     ) | ||||||
|  |     op.drop_table("shopping_list_item_recipe_reference") | ||||||
|  |     op.drop_table("users_to_favorites") | ||||||
|  |     op.drop_table("shopping_list_items") | ||||||
|  |     op.drop_table("recipes_ingredients") | ||||||
|  |     op.drop_table("recipe_ingredient_ref_link") | ||||||
|  |     op.drop_table("recipe_comments") | ||||||
|  |     op.drop_table("password_reset_tokens") | ||||||
|  |     op.drop_table("long_live_tokens") | ||||||
|  |     op.drop_index(op.f("ix_users_username"), table_name="users") | ||||||
|  |     op.drop_index(op.f("ix_users_group_id"), table_name="users") | ||||||
|  |     op.drop_index(op.f("ix_users_full_name"), table_name="users") | ||||||
|  |     op.drop_index(op.f("ix_users_email"), table_name="users") | ||||||
|  |     op.drop_table("users") | ||||||
|  |     op.drop_index(op.f("ix_shopping_list_recipe_reference_recipe_id"), table_name="shopping_list_recipe_reference") | ||||||
|  |     op.drop_table("shopping_list_recipe_reference") | ||||||
|  |     op.drop_table("report_entries") | ||||||
|  |     op.drop_table("recipes_to_tools") | ||||||
|  |     op.drop_table("recipes_to_tags") | ||||||
|  |     op.drop_table("recipes_to_categories") | ||||||
|  |     op.drop_index(op.f("ix_recipe_share_tokens_group_id"), table_name="recipe_share_tokens") | ||||||
|  |     op.drop_table("recipe_share_tokens") | ||||||
|  |     op.drop_table("recipe_settings") | ||||||
|  |     op.drop_table("recipe_nutrition") | ||||||
|  |     op.drop_table("recipe_instructions") | ||||||
|  |     op.drop_table("recipe_assets") | ||||||
|  |     op.drop_table("plan_rules_to_tags") | ||||||
|  |     op.drop_table("plan_rules_to_categories") | ||||||
|  |     op.drop_table("notes") | ||||||
|  |     op.drop_table("ingredient_foods") | ||||||
|  |     op.drop_table("group_to_categories") | ||||||
|  |     op.drop_index(op.f("ix_group_meal_plans_title"), table_name="group_meal_plans") | ||||||
|  |     op.drop_index(op.f("ix_group_meal_plans_recipe_id"), table_name="group_meal_plans") | ||||||
|  |     op.drop_index(op.f("ix_group_meal_plans_group_id"), table_name="group_meal_plans") | ||||||
|  |     op.drop_index(op.f("ix_group_meal_plans_entry_type"), table_name="group_meal_plans") | ||||||
|  |     op.drop_index(op.f("ix_group_meal_plans_date"), table_name="group_meal_plans") | ||||||
|  |     op.drop_table("group_meal_plans") | ||||||
|  |     op.drop_table("group_events_notifier_options") | ||||||
|  |     op.drop_table("cookbooks_to_categories") | ||||||
|  |     op.drop_table("api_extras") | ||||||
|  |     op.drop_index(op.f("ix_webhook_urls_group_id"), table_name="webhook_urls") | ||||||
|  |     op.drop_table("webhook_urls") | ||||||
|  |     op.drop_index(op.f("ix_tools_slug"), table_name="tools") | ||||||
|  |     op.drop_index(op.f("ix_tools_name"), table_name="tools") | ||||||
|  |     op.drop_table("tools") | ||||||
|  |     op.drop_index(op.f("ix_tags_slug"), table_name="tags") | ||||||
|  |     op.drop_index(op.f("ix_tags_name"), table_name="tags") | ||||||
|  |     op.drop_index(op.f("ix_tags_group_id"), table_name="tags") | ||||||
|  |     op.drop_table("tags") | ||||||
|  |     op.drop_index(op.f("ix_shopping_lists_group_id"), table_name="shopping_lists") | ||||||
|  |     op.drop_table("shopping_lists") | ||||||
|  |     op.drop_index(op.f("ix_server_tasks_group_id"), table_name="server_tasks") | ||||||
|  |     op.drop_table("server_tasks") | ||||||
|  |     op.drop_index(op.f("ix_recipes_user_id"), table_name="recipes") | ||||||
|  |     op.drop_index(op.f("ix_recipes_slug"), table_name="recipes") | ||||||
|  |     op.drop_index(op.f("ix_recipes_group_id"), table_name="recipes") | ||||||
|  |     op.drop_table("recipes") | ||||||
|  |     op.drop_index(op.f("ix_multi_purpose_labels_group_id"), table_name="multi_purpose_labels") | ||||||
|  |     op.drop_table("multi_purpose_labels") | ||||||
|  |     op.drop_index(op.f("ix_invite_tokens_token"), table_name="invite_tokens") | ||||||
|  |     op.drop_table("invite_tokens") | ||||||
|  |     op.drop_table("ingredient_units") | ||||||
|  |     op.drop_index(op.f("ix_group_reports_group_id"), table_name="group_reports") | ||||||
|  |     op.drop_index(op.f("ix_group_reports_category"), table_name="group_reports") | ||||||
|  |     op.drop_table("group_reports") | ||||||
|  |     op.drop_index(op.f("ix_group_preferences_group_id"), table_name="group_preferences") | ||||||
|  |     op.drop_table("group_preferences") | ||||||
|  |     op.drop_table("group_meal_plan_rules") | ||||||
|  |     op.drop_index(op.f("ix_group_events_notifiers_group_id"), table_name="group_events_notifiers") | ||||||
|  |     op.drop_table("group_events_notifiers") | ||||||
|  |     op.drop_index(op.f("ix_group_data_exports_group_id"), table_name="group_data_exports") | ||||||
|  |     op.drop_table("group_data_exports") | ||||||
|  |     op.drop_table("cookbooks") | ||||||
|  |     op.drop_index(op.f("ix_categories_slug"), table_name="categories") | ||||||
|  |     op.drop_index(op.f("ix_categories_name"), table_name="categories") | ||||||
|  |     op.drop_index(op.f("ix_categories_group_id"), table_name="categories") | ||||||
|  |     op.drop_table("categories") | ||||||
|  |     op.drop_index(op.f("ix_groups_name"), table_name="groups") | ||||||
|  |     op.drop_table("groups") | ||||||
| @@ -18,7 +18,7 @@ ENV = BASE_DIR.joinpath(".env") | |||||||
|  |  | ||||||
| dotenv.load_dotenv(ENV) | dotenv.load_dotenv(ENV) | ||||||
| PRODUCTION = os.getenv("PRODUCTION", "True").lower() in ["true", "1"] | PRODUCTION = os.getenv("PRODUCTION", "True").lower() in ["true", "1"] | ||||||
| TESTING = os.getenv("TESTING", "True").lower() in ["true", "1"] | TESTING = os.getenv("TESTING", "False").lower() in ["true", "1"] | ||||||
|  |  | ||||||
|  |  | ||||||
| def determine_data_dir() -> Path: | def determine_data_dir() -> Path: | ||||||
|   | |||||||
| @@ -1,7 +1,13 @@ | |||||||
|  | from pathlib import Path | ||||||
|  |  | ||||||
|  | from sqlalchemy import engine | ||||||
|  |  | ||||||
|  | from alembic import command, config, script | ||||||
|  | from alembic.config import Config | ||||||
|  | from alembic.runtime import migration | ||||||
| from mealie.core import root_logger | from mealie.core import root_logger | ||||||
| from mealie.core.config import get_app_settings | from mealie.core.config import get_app_settings | ||||||
| from mealie.db.db_setup import create_session, engine | from mealie.db.db_setup import create_session | ||||||
| from mealie.db.models._model_base import SqlAlchemyBase |  | ||||||
| from mealie.repos.all_repositories import get_repositories | from mealie.repos.all_repositories import get_repositories | ||||||
| from mealie.repos.repository_factory import AllRepositories | from mealie.repos.repository_factory import AllRepositories | ||||||
| from mealie.repos.seed.init_users import default_user_init | from mealie.repos.seed.init_users import default_user_init | ||||||
| @@ -9,15 +15,11 @@ from mealie.repos.seed.seeders import IngredientFoodsSeeder, IngredientUnitsSeed | |||||||
| from mealie.schema.user.user import GroupBase | from mealie.schema.user.user import GroupBase | ||||||
| from mealie.services.group_services.group_utils import create_new_group | from mealie.services.group_services.group_utils import create_new_group | ||||||
|  |  | ||||||
|  | PROJECT_DIR = Path(__file__).parent.parent.parent | ||||||
|  |  | ||||||
| logger = root_logger.get_logger("init_db") | logger = root_logger.get_logger("init_db") | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_all_models(): |  | ||||||
|     import mealie.db.models._all_models  # noqa: F401 |  | ||||||
|  |  | ||||||
|     SqlAlchemyBase.metadata.create_all(engine) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def init_db(db: AllRepositories) -> None: | def init_db(db: AllRepositories) -> None: | ||||||
|     # TODO: Port other seed data to use abstract seeder class |     # TODO: Port other seed data to use abstract seeder class | ||||||
|     default_group_init(db) |     default_group_init(db) | ||||||
| @@ -42,24 +44,33 @@ def default_group_init(db: AllRepositories): | |||||||
|     create_new_group(db, GroupBase(name=settings.DEFAULT_GROUP)) |     create_new_group(db, GroupBase(name=settings.DEFAULT_GROUP)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Adapted from https://alembic.sqlalchemy.org/en/latest/cookbook.html#test-current-database-revision-is-at-head-s | ||||||
|  | def db_is_at_head(alembic_cfg: config.Config) -> bool: | ||||||
|  |     settings = get_app_settings() | ||||||
|  |     url = settings.DB_URL | ||||||
|  |     connectable = engine.create_engine(url) | ||||||
|  |     directory = script.ScriptDirectory.from_config(alembic_cfg) | ||||||
|  |     with connectable.begin() as connection: | ||||||
|  |         context = migration.MigrationContext.configure(connection) | ||||||
|  |         return set(context.get_current_heads()) == set(directory.get_heads()) | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
|     create_all_models() |     alembic_cfg = Config(str(PROJECT_DIR / "alembic.ini")) | ||||||
|  |     if db_is_at_head(alembic_cfg): | ||||||
|  |         logger.info("Migration not needed.") | ||||||
|  |     else: | ||||||
|  |         logger.info("Migration needed. Performing migration...") | ||||||
|  |         command.upgrade(alembic_cfg, "head") | ||||||
|  |  | ||||||
|     session = create_session() |     session = create_session() | ||||||
|     db = get_repositories(session) |     db = get_repositories(session) | ||||||
|  |  | ||||||
|     try: |     init_user = db.users.get_all() | ||||||
|         init_user = db.users.get_all() |  | ||||||
|         if not init_user: |  | ||||||
|             raise Exception("No users found in database") |  | ||||||
|     except Exception: |  | ||||||
|         init_db(db) |  | ||||||
|         return |  | ||||||
|  |  | ||||||
|     if init_user: |     if init_user: | ||||||
|         logger.info("Database Exists") |         logger.info("Database exists") | ||||||
|     else: |     else: | ||||||
|         logger.info("Database Doesn't Exists, Initializing...") |         logger.info("Database contains no users, initializing...") | ||||||
|         init_db(db) |         init_db(db) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								mealie/db/migration_types.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								mealie/db/migration_types.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | from mealie.db.models._model_utils.guid import GUID  # noqa: F401 | ||||||
| @@ -10,10 +10,11 @@ from mealie.db.models._model_utils.guid import GUID | |||||||
|  |  | ||||||
| from .._model_base import BaseMixins, SqlAlchemyBase | from .._model_base import BaseMixins, SqlAlchemyBase | ||||||
| from .._model_utils import auto_init | from .._model_utils import auto_init | ||||||
| from ..users import users_to_favorites | from ..users.user_to_favorite import users_to_favorites | ||||||
| from .api_extras import ApiExtras | from .api_extras import ApiExtras | ||||||
| from .assets import RecipeAsset | from .assets import RecipeAsset | ||||||
| from .category import recipes_to_categories | from .category import recipes_to_categories | ||||||
|  | from .comment import RecipeComment | ||||||
| from .ingredient import RecipeIngredient | from .ingredient import RecipeIngredient | ||||||
| from .instruction import RecipeInstruction | from .instruction import RecipeInstruction | ||||||
| from .note import Note | from .note import Note | ||||||
| @@ -50,7 +51,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): | |||||||
|     group_id = sa.Column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True) |     group_id = sa.Column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True) | ||||||
|     group = orm.relationship("Group", back_populates="recipes", foreign_keys=[group_id]) |     group = orm.relationship("Group", back_populates="recipes", foreign_keys=[group_id]) | ||||||
|  |  | ||||||
|     user_id = sa.Column(GUID, sa.ForeignKey("users.id"), index=True) |     user_id = sa.Column(GUID, sa.ForeignKey("users.id", use_alter=True), index=True) | ||||||
|     user = orm.relationship("User", uselist=False, foreign_keys=[user_id]) |     user = orm.relationship("User", uselist=False, foreign_keys=[user_id]) | ||||||
|  |  | ||||||
|     meal_entries = orm.relationship("GroupMealPlan", back_populates="recipe", cascade="all, delete-orphan") |     meal_entries = orm.relationship("GroupMealPlan", back_populates="recipe", cascade="all, delete-orphan") | ||||||
| @@ -93,7 +94,9 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): | |||||||
|         RecipeShareTokenModel, back_populates="recipe", cascade="all, delete, delete-orphan" |         RecipeShareTokenModel, back_populates="recipe", cascade="all, delete, delete-orphan" | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     comments: list = orm.relationship("RecipeComment", back_populates="recipe", cascade="all, delete, delete-orphan") |     comments: list[RecipeComment] = orm.relationship( | ||||||
|  |         "RecipeComment", back_populates="recipe", cascade="all, delete, delete-orphan" | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     # Mealie Specific |     # Mealie Specific | ||||||
|     settings = orm.relationship("RecipeSettings", uselist=False, cascade="all, delete-orphan") |     settings = orm.relationship("RecipeSettings", uselist=False, cascade="all, delete-orphan") | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ from mealie.db.models._model_utils.guid import GUID | |||||||
|  |  | ||||||
| from .._model_base import BaseMixins, SqlAlchemyBase | from .._model_base import BaseMixins, SqlAlchemyBase | ||||||
| from .._model_utils import auto_init | from .._model_utils import auto_init | ||||||
| from ..group import Group |  | ||||||
| from .user_to_favorite import users_to_favorites | from .user_to_favorite import users_to_favorites | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -74,6 +73,8 @@ class User(SqlAlchemyBase, BaseMixins): | |||||||
|             settings = get_app_settings() |             settings = get_app_settings() | ||||||
|             group = settings.DEFAULT_GROUP |             group = settings.DEFAULT_GROUP | ||||||
|  |  | ||||||
|  |         from mealie.db.models.group import Group | ||||||
|  |  | ||||||
|         self.group = Group.get_ref(session, group) |         self.group = Group.get_ref(session, group) | ||||||
|  |  | ||||||
|         self.favorite_recipes = [] |         self.favorite_recipes = [] | ||||||
| @@ -91,6 +92,8 @@ class User(SqlAlchemyBase, BaseMixins): | |||||||
|         self.full_name = full_name |         self.full_name = full_name | ||||||
|         self.email = email |         self.email = email | ||||||
|  |  | ||||||
|  |         from mealie.db.models.group import Group | ||||||
|  |  | ||||||
|         self.group = Group.get_ref(session, group) |         self.group = Group.get_ref(session, group) | ||||||
|  |  | ||||||
|         if self.username is None: |         if self.username is None: | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										45
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @@ -6,6 +6,21 @@ category = "main" | |||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "alembic" | ||||||
|  | version = "1.7.5" | ||||||
|  | description = "A database migration tool for SQLAlchemy." | ||||||
|  | category = "main" | ||||||
|  | optional = false | ||||||
|  | python-versions = ">=3.6" | ||||||
|  |  | ||||||
|  | [package.dependencies] | ||||||
|  | Mako = "*" | ||||||
|  | SQLAlchemy = ">=1.3.0" | ||||||
|  |  | ||||||
|  | [package.extras] | ||||||
|  | tz = ["python-dateutil"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "aniso8601" | name = "aniso8601" | ||||||
| version = "7.0.0" | version = "7.0.0" | ||||||
| @@ -585,6 +600,21 @@ html5 = ["html5lib"] | |||||||
| htmlsoup = ["beautifulsoup4"] | htmlsoup = ["beautifulsoup4"] | ||||||
| source = ["Cython (>=0.29.7)"] | source = ["Cython (>=0.29.7)"] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "mako" | ||||||
|  | version = "1.1.6" | ||||||
|  | description = "A super-fast templating language that borrows the  best ideas from the existing templating languages." | ||||||
|  | category = "main" | ||||||
|  | optional = false | ||||||
|  | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||||||
|  |  | ||||||
|  | [package.dependencies] | ||||||
|  | MarkupSafe = ">=0.9.2" | ||||||
|  |  | ||||||
|  | [package.extras] | ||||||
|  | babel = ["babel"] | ||||||
|  | lingua = ["lingua"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "markdown" | name = "markdown" | ||||||
| version = "3.3.6" | version = "3.3.6" | ||||||
| @@ -1458,13 +1488,17 @@ pgsql = ["psycopg2-binary"] | |||||||
| [metadata] | [metadata] | ||||||
| lock-version = "1.1" | lock-version = "1.1" | ||||||
| python-versions = "^3.10" | python-versions = "^3.10" | ||||||
| content-hash = "869d87ae4fb7dff15f0b53dddde737ce8a6c2b13217d6690f4e2b90d3198a18c" | content-hash = "94bda5d85d0fd3f5fb0c3ce59ff7cf515eaa3210c4533862e1c8bd22eecec8dd" | ||||||
|  |  | ||||||
| [metadata.files] | [metadata.files] | ||||||
| aiofiles = [ | aiofiles = [ | ||||||
|     {file = "aiofiles-0.5.0-py3-none-any.whl", hash = "sha256:377fdf7815cc611870c59cbd07b68b180841d2a2b79812d8c218be02448c2acb"}, |     {file = "aiofiles-0.5.0-py3-none-any.whl", hash = "sha256:377fdf7815cc611870c59cbd07b68b180841d2a2b79812d8c218be02448c2acb"}, | ||||||
|     {file = "aiofiles-0.5.0.tar.gz", hash = "sha256:98e6bcfd1b50f97db4980e182ddd509b7cc35909e903a8fe50d8849e02d815af"}, |     {file = "aiofiles-0.5.0.tar.gz", hash = "sha256:98e6bcfd1b50f97db4980e182ddd509b7cc35909e903a8fe50d8849e02d815af"}, | ||||||
| ] | ] | ||||||
|  | alembic = [ | ||||||
|  |     {file = "alembic-1.7.5-py3-none-any.whl", hash = "sha256:a9dde941534e3d7573d9644e8ea62a2953541e27bc1793e166f60b777ae098b4"}, | ||||||
|  |     {file = "alembic-1.7.5.tar.gz", hash = "sha256:7c328694a2e68f03ee971e63c3bd885846470373a5b532cf2c9f1601c413b153"}, | ||||||
|  | ] | ||||||
| aniso8601 = [ | aniso8601 = [ | ||||||
|     {file = "aniso8601-7.0.0-py2.py3-none-any.whl", hash = "sha256:d10a4bf949f619f719b227ef5386e31f49a2b6d453004b21f02661ccc8670c7b"}, |     {file = "aniso8601-7.0.0-py2.py3-none-any.whl", hash = "sha256:d10a4bf949f619f719b227ef5386e31f49a2b6d453004b21f02661ccc8670c7b"}, | ||||||
|     {file = "aniso8601-7.0.0.tar.gz", hash = "sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e"}, |     {file = "aniso8601-7.0.0.tar.gz", hash = "sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e"}, | ||||||
| @@ -1703,6 +1737,7 @@ greenlet = [ | |||||||
|     {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, |     {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, | ||||||
|     {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, |     {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, | ||||||
|     {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, |     {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, | ||||||
|  |     {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, | ||||||
|     {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, |     {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, | ||||||
|     {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, |     {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, | ||||||
|     {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, |     {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, | ||||||
| @@ -1715,6 +1750,7 @@ greenlet = [ | |||||||
|     {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, |     {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, | ||||||
|     {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, |     {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, | ||||||
|     {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, |     {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, | ||||||
|  |     {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, | ||||||
|     {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, |     {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, | ||||||
|     {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, |     {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, | ||||||
|     {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, |     {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, | ||||||
| @@ -1723,6 +1759,7 @@ greenlet = [ | |||||||
|     {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, |     {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, | ||||||
|     {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, |     {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, | ||||||
|     {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, |     {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, | ||||||
|  |     {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, | ||||||
|     {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, |     {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, | ||||||
|     {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, |     {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, | ||||||
|     {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, |     {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, | ||||||
| @@ -1731,6 +1768,7 @@ greenlet = [ | |||||||
|     {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, |     {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, | ||||||
|     {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, |     {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, | ||||||
|     {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, |     {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, | ||||||
|  |     {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, | ||||||
|     {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, |     {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, | ||||||
|     {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, |     {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, | ||||||
|     {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, |     {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, | ||||||
| @@ -1739,6 +1777,7 @@ greenlet = [ | |||||||
|     {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, |     {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, | ||||||
|     {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, |     {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, | ||||||
|     {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, |     {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, | ||||||
|  |     {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, | ||||||
|     {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, |     {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, | ||||||
|     {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, |     {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, | ||||||
|     {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, |     {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, | ||||||
| @@ -1904,6 +1943,10 @@ lxml = [ | |||||||
|     {file = "lxml-4.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:447d5009d6b5447b2f237395d0018901dcc673f7d9f82ba26c1b9f9c3b444b60"}, |     {file = "lxml-4.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:447d5009d6b5447b2f237395d0018901dcc673f7d9f82ba26c1b9f9c3b444b60"}, | ||||||
|     {file = "lxml-4.7.1.tar.gz", hash = "sha256:a1613838aa6b89af4ba10a0f3a972836128801ed008078f8c1244e65958f1b24"}, |     {file = "lxml-4.7.1.tar.gz", hash = "sha256:a1613838aa6b89af4ba10a0f3a972836128801ed008078f8c1244e65958f1b24"}, | ||||||
| ] | ] | ||||||
|  | mako = [ | ||||||
|  |     {file = "Mako-1.1.6-py2.py3-none-any.whl", hash = "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57"}, | ||||||
|  |     {file = "Mako-1.1.6.tar.gz", hash = "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2"}, | ||||||
|  | ] | ||||||
| markdown = [ | markdown = [ | ||||||
|     {file = "Markdown-3.3.6-py3-none-any.whl", hash = "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"}, |     {file = "Markdown-3.3.6-py3-none-any.whl", hash = "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"}, | ||||||
|     {file = "Markdown-3.3.6.tar.gz", hash = "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006"}, |     {file = "Markdown-3.3.6.tar.gz", hash = "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006"}, | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ fastapi = "^0.71.0" | |||||||
| uvicorn = {extras = ["standard"], version = "^0.13.0"} | uvicorn = {extras = ["standard"], version = "^0.13.0"} | ||||||
| APScheduler = "^3.8.1" | APScheduler = "^3.8.1" | ||||||
| SQLAlchemy = "^1.4.29" | SQLAlchemy = "^1.4.29" | ||||||
|  | alembic = "^1.7.5" | ||||||
| Jinja2 = "^2.11.2" | Jinja2 = "^2.11.2" | ||||||
| python-dotenv = "^0.15.0" | python-dotenv = "^0.15.0" | ||||||
| python-slugify = "^4.0.1" | python-slugify = "^4.0.1" | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								tests/unit_tests/test_alembic.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/unit_tests/test_alembic.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | import pytest | ||||||
|  |  | ||||||
|  | # Test that alembic revisions are applicable and result in the current database | ||||||
|  | # See https://github.com/sqlalchemy/alembic/issues/724 for inspiration | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.skip("TODO: Implement") | ||||||
|  | def test_alembic_revisions_are_applicable(): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.skip("TODO: Implement") | ||||||
|  | def test_alembic_revisions_are_up_to_date(): | ||||||
|  |     pass | ||||||
		Reference in New Issue
	
	Block a user