From 8a6c2b414dcf599dbee9a2a783ad1bf7836b0dac Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Wed, 25 Sep 2024 18:37:35 +0200 Subject: [PATCH] Split migrations - move create publication to core - use ivm entries for the nutrition publications --- ...18_create_publication_add_ivm_extension.py | 48 +++++++++++++ .../migrations/0025_create_ivm_entries.py | 69 +++++++++++++++++++ .../migrations/0025_create_publication.py | 35 ---------- wger/utils/db.py | 16 +++++ 4 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 wger/core/migrations/0018_create_publication_add_ivm_extension.py create mode 100644 wger/nutrition/migrations/0025_create_ivm_entries.py delete mode 100644 wger/nutrition/migrations/0025_create_publication.py diff --git a/wger/core/migrations/0018_create_publication_add_ivm_extension.py b/wger/core/migrations/0018_create_publication_add_ivm_extension.py new file mode 100644 index 000000000..959b6b35a --- /dev/null +++ b/wger/core/migrations/0018_create_publication_add_ivm_extension.py @@ -0,0 +1,48 @@ +# Generated by Django 4.2.13 on 2024-09-19 13:43 + +from django.db import migrations + +from wger.utils.db import postgres_only + + +@postgres_only +def add_publication(apps, schema_editor): + # Note that "FOR ALL TABLES" applies for all tables created in the future as well + schema_editor.execute( + """ + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_publication WHERE pubname = 'powersync' + ) THEN + CREATE PUBLICATION powersync FOR ALL TABLES; + END IF; + END $$; + """ + ) + + +@postgres_only +def remove_publication(apps, schema_editor): + schema_editor.execute('DROP PUBLICATION IF EXISTS powersync;') + + +@postgres_only +def add_ivm_extension(apps, schema_editor): + schema_editor.execute('CREATE EXTENSION IF NOT EXISTS pg_ivm;') + + +@postgres_only +def remove_ivm_extension(apps, schema_editor): + schema_editor.execute('DROP EXTENSION IF EXISTS pg_ivm;') + + +class Migration(migrations.Migration): + dependencies = [ + ('core', '0017_language_full_name_en'), + ] + + operations = [ + migrations.RunPython(add_publication, reverse_code=remove_publication), + migrations.RunPython(add_ivm_extension, reverse_code=remove_ivm_extension), + ] diff --git a/wger/nutrition/migrations/0025_create_ivm_entries.py b/wger/nutrition/migrations/0025_create_ivm_entries.py new file mode 100644 index 000000000..b1d2d290a --- /dev/null +++ b/wger/nutrition/migrations/0025_create_ivm_entries.py @@ -0,0 +1,69 @@ +from django.db import migrations + +from wger.utils.db import postgres_only + + +@postgres_only +def add_ivm_views(apps, schema_editor): + """ + Note: the select statements are written a bit weirdly because of this issue + https://github.com/sraoss/pg_ivm/issues/85 + + When this is resolved, we can remove the subqueries and write e.g. + + SELECT m.*, p.user_id + FROM nutrition_meal AS m + JOIN nutrition_nutritionplan AS p ON m.plan_id = p.id; + """ + + schema_editor.execute( + """ + SELECT create_immv( + 'ivm_nutrition_meal', + 'SELECT m.*, p.user_id + FROM (SELECT * FROM nutrition_meal) AS m + JOIN (SELECT id, user_id FROM nutrition_nutritionplan) AS p ON m.plan_id = p.id;' + ); + """ + ) + + schema_editor.execute( + """ + SELECT create_immv( + 'ivm_nutrition_mealitem', + 'SELECT mi.*, p.user_id + FROM (SELECT * FROM nutrition_mealitem) AS mi + JOIN (SELECT id, plan_id FROM nutrition_meal) AS m ON mi.meal_id = m.id + JOIN (SELECT id, user_id FROM nutrition_nutritionplan) AS p ON m.plan_id = p.id;' + ); + """ + ) + + schema_editor.execute( + """ + SELECT create_immv( + 'ivm_nutrition_logitem', + 'SELECT li.*, p.user_id + FROM (SELECT * FROM nutrition_logitem) AS li + JOIN (SELECT id, user_id FROM nutrition_nutritionplan) AS p ON li.plan_id = p.id;' + ); + """ + ) + + +@postgres_only +def remove_ivm_views(apps, schema_editor): + schema_editor.execute('DROP TABLE IF EXISTS ivm_nutrition_mealitem;') + schema_editor.execute('DROP TABLE IF EXISTS ivm_nutrition_meal;') + schema_editor.execute('DROP TABLE IF EXISTS ivm_nutrition_logitem;') + + +class Migration(migrations.Migration): + dependencies = [ + ('nutrition', '0024_remove_ingredient_status'), + ('core', '0018_create_publication_add_ivm_extension'), + ] + + operations = [ + migrations.RunPython(add_ivm_views, reverse_code=remove_ivm_views), + ] diff --git a/wger/nutrition/migrations/0025_create_publication.py b/wger/nutrition/migrations/0025_create_publication.py deleted file mode 100644 index 7a2196173..000000000 --- a/wger/nutrition/migrations/0025_create_publication.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.db import migrations - -from wger.utils.db import is_postgres_db - -# TODO move to more appropriate place - -def add_publication(apps, schema_editor): - if is_postgres_db(): - schema_editor.execute( - """ - DO $$ - BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_publication WHERE pubname = 'powersync' - ) THEN - CREATE PUBLICATION powersync FOR ALL TABLES; - END IF; - END $$; - """ - ) - - -def remove_publication(apps, schema_editor): - if is_postgres_db(): - schema_editor.execute('DROP PUBLICATION IF EXISTS powersync;') - - -class Migration(migrations.Migration): - dependencies = [ - ('nutrition', '0024_remove_ingredient_status'), - ] - - operations = [ - migrations.RunPython(add_publication, reverse_code=remove_publication), - ] diff --git a/wger/utils/db.py b/wger/utils/db.py index 2d15b5c74..3e12938b9 100644 --- a/wger/utils/db.py +++ b/wger/utils/db.py @@ -12,9 +12,25 @@ # # You should have received a copy of the GNU Affero General Public License +# Standard Library +from functools import wraps + # Django from django.conf import settings def is_postgres_db(): return 'postgres' in settings.DATABASES['default']['ENGINE'] + + +def postgres_only(func): + """Decorator that runs the decorated function only if the database is PostgreSQL.""" + + @wraps(func) + def wrapper(*args, **kwargs): + if is_postgres_db(): + return func(*args, **kwargs) + else: + return + + return wrapper