diff --git a/wger/nutrition/fixtures/test-nutrition-data.json b/wger/nutrition/fixtures/test-nutrition-data.json index 863f2d39e..0726db6b3 100644 --- a/wger/nutrition/fixtures/test-nutrition-data.json +++ b/wger/nutrition/fixtures/test-nutrition-data.json @@ -1,356 +1,388 @@ [ - { - "pk": 5, - "model": "nutrition.nutritionplan", - "fields": { - "description": "Description 1", - "user": 1, - "creation_date": "2012-11-19" - } - }, - { - "pk": 4, - "model": "nutrition.nutritionplan", - "fields": { - "description": "My plan to do xyz", - "user": 2, - "creation_date": "2012-11-12" - } - }, - { - "pk": 3, - "model": "nutrition.nutritionplan", - "fields": { - "description": "", - "user": 1, - "creation_date": "2012-11-09" - } - }, - { - "pk": 2, - "model": "nutrition.nutritionplan", - "fields": { - "description": "", - "user": 1, - "creation_date": "2012-10-29" - } - }, - { - "pk": 1, - "model": "nutrition.nutritionplan", - "fields": { - "description": "", - "user": 2, - "creation_date": "2012-09-22" - } - }, - { - "pk": 1, - "model": "nutrition.meal", - "fields": { - "time": null, - "plan": 1, - "order": 1 - } - }, - { - "pk": 2, - "model": "nutrition.meal", - "fields": { - "time": "07:00:00", - "plan": 4, - "order": 1 - } - }, - { - "pk": 3, - "model": "nutrition.meal", - "fields": { - "time": "08:00:00", - "plan": 2, - "order": 1 - } - }, - { - "pk": 4, - "model": "nutrition.meal", - "fields": { - "time": "08:00:00", - "plan": 3, - "order": 1 - } - }, - { - "pk": 5, - "model": "nutrition.meal", - "fields": { - "time": "08:00:00", - "plan": 2, - "order": 1 - } - }, - { - "pk": 6, - "model": "nutrition.meal", - "fields": { - "time": "08:00:00", - "plan": 5, - "order": 1 - } - }, - { - "pk": 7, - "model": "nutrition.meal", - "fields": { - "time": "08:02:00", - "plan": 4, - "order": 1 - } - }, - { - "pk": 8, - "model": "nutrition.meal", - "fields": { - "time": "10:00:00", - "plan": 2, - "order": 1 - } - }, - { - "pk": 9, - "model": "nutrition.meal", - "fields": { - "time": "10:00:00", - "plan": 4, - "order": 1 - } - }, - { - "pk": 10, - "model": "nutrition.meal", - "fields": { - "time": "19:02:00", - "plan": 4, - "order": 1 - } - }, - { - "pk": 11, - "model": "nutrition.meal", - "fields": { - "time": "22:00:00", - "plan": 4, - "order": 1 - } - }, - { - "pk": 1, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 1, - "amount": 200, - "meal": 1, - "order": 1 - } - }, - { - "pk": 2, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 1, - "amount": 200, - "meal": 1, - "order": 1 - } - }, - { - "pk": 3, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 1, - "amount": 200, - "meal": 1, - "order": 1 - } - }, - { - "pk": 4, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 4, - "amount": 300, - "meal": 3, - "order": 1 - } - }, - { - "pk": 5, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 2, - "amount": 100, - "meal": 8, - "order": 1 - } - }, - { - "pk": 6, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 5, - "amount": 100, - "meal": 8, - "order": 1 - } - }, - { - "pk": 7, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 3, - "amount": 300, - "meal": 7, - "order": 1 - } - }, - { - "pk": 8, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 1, - "amount": 101, - "meal": 9, - "order": 1 - } - }, - { - "pk": 9, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 2, - "amount": 100, - "meal": 9, - "order": 1 - } - }, - { - "pk": 10, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 3, - "amount": 100, - "meal": 2, - "order": 1 - } - }, - { - "pk": 11, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 4, - "amount": 200, - "meal": 10, - "order": 1 - } - }, - { - "pk": 12, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 4, - "amount": 200, - "meal": 10, - "order": 1 - } - }, - { - "pk": 13, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 3, - "amount": 100, - "meal": 7, - "order": 1 - } - }, - { - "pk": 14, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 6, - "amount": 30, - "meal": 7, - "order": 1 - } - }, - { - "pk": 15, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 3, - "amount": 150, - "meal": 10, - "order": 1 - } - }, - { - "pk": 16, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 1, - "amount": 500, - "meal": 11, - "order": 1 - } - }, - { - "pk": 17, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 6, - "amount": 100, - "meal": 6, - "order": 1 - } - }, - { - "pk": 18, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 4, - "amount": 100, - "meal": 6, - "order": 1 - } - }, - { - "pk": 19, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 3, - "amount": 100, - "meal": 6, - "order": 1 - } - }, - { - "pk": 20, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 2, - "amount": 100, - "meal": 6, - "order": 1 - } - }, - { - "pk": 21, - "model": "nutrition.mealitem", - "fields": { - "ingredient": 1, - "amount": 100, - "meal": 6, - "order": 1 - } + { + "pk": 5, + "model": "nutrition.nutritionplan", + "fields": { + "description": "Description 1", + "user": 1, + "creation_date": "2012-11-19" } + }, + { + "pk": 4, + "model": "nutrition.nutritionplan", + "fields": { + "description": "My plan to do xyz", + "user": 2, + "creation_date": "2012-11-12" + } + }, + { + "pk": 3, + "model": "nutrition.nutritionplan", + "fields": { + "description": "", + "user": 1, + "creation_date": "2012-11-09" + } + }, + { + "pk": 2, + "model": "nutrition.nutritionplan", + "fields": { + "description": "", + "user": 1, + "creation_date": "2012-10-29" + } + }, + { + "pk": 1, + "model": "nutrition.nutritionplan", + "fields": { + "description": "", + "user": 2, + "creation_date": "2012-09-22" + } + }, + { + "pk": 1, + "model": "nutrition.meal", + "fields": { + "time": null, + "plan": 1, + "user": 2, + "order": 1 + } + }, + { + "pk": 2, + "model": "nutrition.meal", + "fields": { + "time": "07:00:00", + "plan": 4, + "user": 2, + "order": 1 + } + }, + { + "pk": 3, + "model": "nutrition.meal", + "fields": { + "time": "08:00:00", + "plan": 2, + "user": 1, + "order": 1 + } + }, + { + "pk": 4, + "model": "nutrition.meal", + "fields": { + "time": "08:00:00", + "plan": 3, + "user": 1, + "order": 1 + } + }, + { + "pk": 5, + "model": "nutrition.meal", + "fields": { + "time": "08:00:00", + "plan": 2, + "user": 1, + "order": 1 + } + }, + { + "pk": 6, + "model": "nutrition.meal", + "fields": { + "time": "08:00:00", + "plan": 5, + "user": 1, + "order": 1 + } + }, + { + "pk": 7, + "model": "nutrition.meal", + "fields": { + "time": "08:02:00", + "plan": 4, + "user": 2, + "order": 1 + } + }, + { + "pk": 8, + "model": "nutrition.meal", + "fields": { + "time": "10:00:00", + "plan": 2, + "user": 1, + "order": 1 + } + }, + { + "pk": 9, + "model": "nutrition.meal", + "fields": { + "time": "10:00:00", + "plan": 4, + "user": 2, + "order": 1 + } + }, + { + "pk": 10, + "model": "nutrition.meal", + "fields": { + "time": "19:02:00", + "plan": 4, + "user": 2, + "order": 1 + } + }, + { + "pk": 11, + "model": "nutrition.meal", + "fields": { + "time": "22:00:00", + "plan": 4, + "user": 2, + "order": 1 + } + }, + { + "pk": 1, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 1, + "amount": 200, + "meal": 1, + "user": 2, + "order": 1 + } + }, + { + "pk": 2, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 1, + "amount": 200, + "meal": 1, + "user": 2, + "order": 1 + } + }, + { + "pk": 3, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 1, + "amount": 200, + "meal": 1, + "user": 2, + "order": 1 + } + }, + { + "pk": 4, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 4, + "amount": 300, + "meal": 3, + "user": 1, + "order": 1 + } + }, + { + "pk": 5, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 2, + "amount": 100, + "meal": 8, + "user": 1, + "order": 1 + } + }, + { + "pk": 6, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 5, + "amount": 100, + "meal": 8, + "user": 1, + "order": 1 + } + }, + { + "pk": 7, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 3, + "amount": 300, + "meal": 7, + "user": 2, + "order": 1 + } + }, + { + "pk": 8, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 1, + "amount": 101, + "meal": 9, + "user": 2, + "order": 1 + } + }, + { + "pk": 9, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 2, + "amount": 100, + "meal": 9, + "user": 2, + "order": 1 + } + }, + { + "pk": 10, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 3, + "amount": 100, + "meal": 2, + "user": 2, + "order": 1 + } + }, + { + "pk": 11, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 4, + "amount": 200, + "meal": 10, + "user": 2, + "order": 1 + } + }, + { + "pk": 12, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 4, + "amount": 200, + "meal": 10, + "user": 2, + "order": 1 + } + }, + { + "pk": 13, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 3, + "amount": 100, + "meal": 7, + "user": 2, + "order": 1 + } + }, + { + "pk": 14, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 6, + "amount": 30, + "meal": 7, + "user": 2, + "order": 1 + } + }, + { + "pk": 15, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 3, + "amount": 150, + "meal": 10, + "user": 2, + "order": 1 + } + }, + { + "pk": 16, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 1, + "amount": 500, + "meal": 11, + "user": 2, + "order": 1 + } + }, + { + "pk": 17, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 6, + "amount": 100, + "meal": 6, + "user": 1, + "order": 1 + } + }, + { + "pk": 18, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 4, + "amount": 100, + "meal": 6, + "user": 1, + "order": 1 + } + }, + { + "pk": 19, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 3, + "amount": 100, + "meal": 6, + "user": 1, + "order": 1 + } + }, + { + "pk": 20, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 2, + "amount": 100, + "meal": 6, + "user": 1, + "order": 1 + } + }, + { + "pk": 21, + "model": "nutrition.mealitem", + "fields": { + "ingredient": 1, + "amount": 100, + "meal": 6, + "user": 1, + "order": 1 + } + } ] diff --git a/wger/nutrition/fixtures/test-nutrition-diary.json b/wger/nutrition/fixtures/test-nutrition-diary.json index 274b01e56..27cb38e21 100644 --- a/wger/nutrition/fixtures/test-nutrition-diary.json +++ b/wger/nutrition/fixtures/test-nutrition-diary.json @@ -1,62 +1,67 @@ [ - { - "fields": { - "plan": 1, - "datetime": "2016-05-15T12:01:00Z", - "ingredient": 5, - "comment": "", - "amount": "2", - "weight_unit": 1 - }, - "pk": 1, - "model": "nutrition.logitem" + { + "fields": { + "plan": 1, + "user": 2, + "datetime": "2016-05-15T12:01:00Z", + "ingredient": 5, + "comment": "", + "amount": "2", + "weight_unit": 1 }, - { - "fields": { - "plan": 1, - "datetime": "2016-05-15T12:01:15Z", - "ingredient": 4, - "comment": "", - "amount": "100", - "weight_unit": null - }, - "pk": 2, - "model": "nutrition.logitem" + "pk": 1, + "model": "nutrition.logitem" + }, + { + "fields": { + "plan": 1, + "user": 2, + "datetime": "2016-05-15T12:01:15Z", + "ingredient": 4, + "comment": "", + "amount": "100", + "weight_unit": null }, - { - "fields": { - "plan": 1, - "datetime": "2016-05-15T12:01:00Z", - "ingredient": 1, - "comment": "", - "amount": "75", - "weight_unit": null - }, - "pk": 3, - "model": "nutrition.logitem" + "pk": 2, + "model": "nutrition.logitem" + }, + { + "fields": { + "plan": 1, + "user": 2, + "datetime": "2016-05-15T12:01:00Z", + "ingredient": 1, + "comment": "", + "amount": "75", + "weight_unit": null }, - { - "fields": { - "plan": 1, - "datetime": "2016-05-14T17:11:33Z", - "ingredient": 1, - "comment": "", - "amount": "75", - "weight_unit": null - }, - "pk": 4, - "model": "nutrition.logitem" + "pk": 3, + "model": "nutrition.logitem" + }, + { + "fields": { + "plan": 1, + "user": 2, + "datetime": "2016-05-14T17:11:33Z", + "ingredient": 1, + "comment": "", + "amount": "75", + "weight_unit": null }, - { - "fields": { - "plan": 2, - "datetime": "2016-05-14T17:11:33Z", - "ingredient": 1, - "comment": "", - "amount": "75", - "weight_unit": null - }, - "pk": 4, - "model": "nutrition.logitem" - } -] \ No newline at end of file + "pk": 4, + "model": "nutrition.logitem" + }, + { + "fields": { + "plan": 2, + "user": 1, + "datetime": "2016-05-14T17:11:33Z", + "ingredient": 1, + "comment": "", + "amount": "75", + "weight_unit": null + }, + "pk": 4, + "model": "nutrition.logitem" + } +] diff --git a/wger/nutrition/management/commands/dummy-generator-nutrition.py b/wger/nutrition/management/commands/dummy-generator-nutrition.py index 61eccc4bd..9a2b40f0b 100644 --- a/wger/nutrition/management/commands/dummy-generator-nutrition.py +++ b/wger/nutrition/management/commands/dummy-generator-nutrition.py @@ -138,6 +138,7 @@ def handle(self, **options): for _ in range(0, options['nr_diary_entries']): log = LogItem( plan=plan, + user=user, datetime=date, ingredient=choice(ingredients), weight_unit=None, diff --git a/wger/nutrition/migrations/0025_create_publication.py b/wger/nutrition/migrations/0025_create_publication.py index 7a2196173..217336256 100644 --- a/wger/nutrition/migrations/0025_create_publication.py +++ b/wger/nutrition/migrations/0025_create_publication.py @@ -4,6 +4,7 @@ # TODO move to more appropriate place + def add_publication(apps, schema_editor): if is_postgres_db(): schema_editor.execute( diff --git a/wger/nutrition/migrations/0026_add_user_foreign_key.py b/wger/nutrition/migrations/0026_add_user_foreign_key.py new file mode 100644 index 000000000..e1b9e6d26 --- /dev/null +++ b/wger/nutrition/migrations/0026_add_user_foreign_key.py @@ -0,0 +1,103 @@ +# Generated by Django 4.2.13 on 2024-09-18 13:26 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +def add_foreign_key(apps, schema_editor): + Meal = apps.get_model('nutrition', 'Meal') + MealItem = apps.get_model('nutrition', 'MealItem') + Log = apps.get_model('nutrition', 'LogItem') + + for meal in Meal.objects.all(): + meal.user = meal.plan.user + meal.save() + + for item in MealItem.objects.all(): + item.user = item.meal.plan.user + item.save() + + for log in Log.objects.all(): + log.user = log.plan.user + log.save() + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('nutrition', '0025_create_publication'), + ] + + operations = [ + # Foreign key nullable + migrations.AddField( + model_name='meal', + name='user', + field=models.ForeignKey( + null=True, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + migrations.AddField( + model_name='mealitem', + name='user', + field=models.ForeignKey( + null=True, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + migrations.AddField( + model_name='logitem', + name='user', + field=models.ForeignKey( + null=True, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + # Migrate + migrations.RunPython(add_foreign_key, reverse_code=migrations.RunPython.noop), + # Foreign key non nullable + migrations.AlterField( + model_name='meal', + name='user', + field=models.ForeignKey( + null=False, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + migrations.AlterField( + model_name='mealitem', + name='user', + field=models.ForeignKey( + null=False, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + migrations.AlterField( + model_name='logitem', + name='user', + field=models.ForeignKey( + null=False, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + ] diff --git a/wger/nutrition/models/log.py b/wger/nutrition/models/log.py index 0f51d3905..3f6e95801 100644 --- a/wger/nutrition/models/log.py +++ b/wger/nutrition/models/log.py @@ -18,6 +18,7 @@ from decimal import Decimal # Django +from django.contrib.auth.models import User from django.core.validators import ( MaxValueValidator, MinValueValidator, @@ -68,6 +69,18 @@ class Meta: The meal this log belongs to (optional) """ + user = models.ForeignKey( + User, + verbose_name=_('User'), + editable=False, + on_delete=models.CASCADE, + ) + """ + NOTE: this foreign key is only needed for powersync since it currently + can't use joins. Since this could change in the future, do not use + it if it can be avoided. + """ + datetime = models.DateTimeField(verbose_name=_('Date and Time (Approx.)'), default=timezone.now) """ Time and date when the log was added @@ -118,6 +131,10 @@ def __str__(self): """ return f'Diary entry for {self.datetime}, plan {self.plan.pk}' + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + self.user = self.plan.user + super().save(force_insert, force_update, using, update_fields) + def get_owner_object(self): """ Returns the object that has owner information diff --git a/wger/nutrition/models/meal.py b/wger/nutrition/models/meal.py index 3a386117b..b81128eb1 100644 --- a/wger/nutrition/models/meal.py +++ b/wger/nutrition/models/meal.py @@ -16,14 +16,13 @@ # Standard Library import logging -from decimal import Decimal # Django +from django.contrib.auth.models import User from django.db import models from django.utils.translation import gettext_lazy as _ # wger -from wger.utils.constants import TWOPLACES from wger.utils.fields import Html5TimeField # Local @@ -51,6 +50,19 @@ class Meta: editable=False, on_delete=models.CASCADE, ) + + user = models.ForeignKey( + User, + verbose_name=_('User'), + editable=False, + on_delete=models.CASCADE, + ) + """ + NOTE: this foreign key is only needed for powersync since it currently + can't use joins. Since this could change in the future, do not use + it if it can be avoided. + """ + order = models.IntegerField( verbose_name=_('Order'), blank=True, @@ -76,6 +88,10 @@ def __str__(self): """ return f'{self.order} Meal' + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + self.user = self.plan.user + super().save(force_insert, force_update, using, update_fields) + def get_owner_object(self): """ Returns the object that has owner information diff --git a/wger/nutrition/models/meal_item.py b/wger/nutrition/models/meal_item.py index a2b64147b..0bd84a5b1 100644 --- a/wger/nutrition/models/meal_item.py +++ b/wger/nutrition/models/meal_item.py @@ -19,6 +19,7 @@ from decimal import Decimal # Django +from django.contrib.auth.models import User from django.core.validators import ( MaxValueValidator, MinValueValidator, @@ -49,11 +50,25 @@ class MealItem(BaseMealItem, models.Model): editable=False, on_delete=models.CASCADE, ) + + user = models.ForeignKey( + User, + verbose_name=_('User'), + editable=False, + on_delete=models.CASCADE, + ) + """ + NOTE: this foreign key is only needed for powersync since it currently + can't use joins. Since this could change in the future, do not use + it if it can be avoided. + """ + ingredient = models.ForeignKey( Ingredient, verbose_name=_('Ingredient'), on_delete=models.CASCADE, ) + weight_unit = models.ForeignKey( IngredientWeightUnit, verbose_name=_('Weight unit'), @@ -80,6 +95,10 @@ def __str__(self): """ return f'{self.amount}g ingredient {self.ingredient_id}' + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + self.user = self.meal.plan.user + super().save(force_insert, force_update, using, update_fields) + def get_owner_object(self): """ Returns the object that has owner information diff --git a/wger/nutrition/models/plan.py b/wger/nutrition/models/plan.py index 8cea374fb..bebc4923c 100644 --- a/wger/nutrition/models/plan.py +++ b/wger/nutrition/models/plan.py @@ -17,7 +17,6 @@ # Standard Library import datetime import logging -from decimal import Decimal # Django from django.contrib.auth.models import User @@ -30,7 +29,6 @@ from wger.nutrition.consts import ENERGY_FACTOR from wger.nutrition.helpers import NutritionalValues from wger.utils.cache import cache_mapper -from wger.utils.constants import TWOPLACES from wger.weight.models import WeightEntry diff --git a/wger/utils/db.py b/wger/utils/db.py index 2d15b5c74..83a735676 100644 --- a/wger/utils/db.py +++ b/wger/utils/db.py @@ -16,5 +16,6 @@ from django.conf import settings -def is_postgres_db(): +def is_postgres_db() -> bool: + """Check if the application is using a postgres database""" return 'postgres' in settings.DATABASES['default']['ENGINE']