mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
food inherit attributes
This commit is contained in:
@@ -45,8 +45,7 @@ class UserPreferenceForm(forms.ModelForm):
|
||||
fields = (
|
||||
'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color',
|
||||
'sticky_navbar', 'default_page', 'show_recent', 'search_style',
|
||||
'plan_share', 'shopping_share', 'ingredient_decimals', 'shopping_auto_sync',
|
||||
'comments'
|
||||
'plan_share', 'ingredient_decimals', 'comments',
|
||||
)
|
||||
|
||||
labels = {
|
||||
@@ -93,7 +92,8 @@ class UserPreferenceForm(forms.ModelForm):
|
||||
|
||||
widgets = {
|
||||
'plan_share': MultiSelectWidget,
|
||||
'shopping_share': MultiSelectWidget
|
||||
'shopping_share': MultiSelectWidget,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# Generated by Django 3.2.7 on 2021-10-01 22:34
|
||||
|
||||
import datetime
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.utils.timezone import utc
|
||||
|
||||
from django_scopes import scopes_disabled
|
||||
from cookbook.models import ShoppingListEntry
|
||||
|
||||
from cookbook.models import FoodInheritField, ShoppingListEntry
|
||||
|
||||
|
||||
def delete_orphaned_sle(apps, schema_editor):
|
||||
@@ -16,6 +17,15 @@ def delete_orphaned_sle(apps, schema_editor):
|
||||
ShoppingListEntry.objects.filter(shoppinglist=None).delete()
|
||||
|
||||
|
||||
def create_inheritfields(apps, schema_editor):
|
||||
FoodInheritField.objects.create(name='Supermarket Category', field='supermarket_category')
|
||||
FoodInheritField.objects.create(name='Ignore Shopping', field='ignore_shopping')
|
||||
FoodInheritField.objects.create(name='Diet', field='diet')
|
||||
FoodInheritField.objects.create(name='Substitute', field='substitute')
|
||||
FoodInheritField.objects.create(name='Substitute Children', field='substitute_children')
|
||||
FoodInheritField.objects.create(name='Substitute Siblings', field='substitute_siblings')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
@@ -25,4 +35,5 @@ class Migration(migrations.Migration):
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(delete_orphaned_sle),
|
||||
migrations.RunPython(create_inheritfields),
|
||||
]
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# Generated by Django 3.2.7 on 2021-10-14 22:36
|
||||
|
||||
import cookbook.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0161_alter_shoppinglistentry_list_recipe'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FoodParentIgnore',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('field', models.CharField(max_length=32, unique=True)),
|
||||
('name', models.CharField(max_length=64, unique=True)),
|
||||
],
|
||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='food',
|
||||
name='child_inherit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userpreference',
|
||||
name='food_inherit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userpreference',
|
||||
name='mealplan_autoinclude_related',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='food',
|
||||
name='ignore_parent',
|
||||
field=models.ManyToManyField(blank=True, related_name='ignore_parent', to='cookbook.FoodParentIgnore'),
|
||||
),
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
from cookbook.models import FoodParentIgnore
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_ignorefields(apps, schema_editor):
|
||||
FoodParentIgnore.objects.create(name='Supermarket Category', field='name')
|
||||
FoodParentIgnore.objects.create(name='Ignore Shopping', field='ignore_shopping')
|
||||
FoodParentIgnore.objects.create(name='Diet', field='diet')
|
||||
FoodParentIgnore.objects.create(name='Substitute', field='substitute')
|
||||
FoodParentIgnore.objects.create(name='Substitute Children', field='substitute_children')
|
||||
FoodParentIgnore.objects.create(name='Substitute Siblings', field='substitute_siblings')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0162_food_inherit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
create_ignorefields
|
||||
),
|
||||
]
|
||||
@@ -15,9 +15,7 @@ from django.core.files.uploadedfile import InMemoryUploadedFile, UploadedFile
|
||||
from django.core.validators import MinLengthValidator
|
||||
from django.db import IntegrityError, models
|
||||
from django.db.models import Index, ProtectedError, Q, Subquery
|
||||
from django.db.models.fields.related import ManyToManyField
|
||||
from django.db.models.functions import Substr
|
||||
from django.db.transaction import atomic
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from django_prometheus.models import ExportModelOperationsMixin
|
||||
@@ -329,7 +327,6 @@ class UserPreference(models.Model, PermissionModelMixin):
|
||||
mealplan_autoadd_shopping = models.BooleanField(default=False)
|
||||
mealplan_autoexclude_onhand = models.BooleanField(default=True)
|
||||
mealplan_autoinclude_related = models.BooleanField(default=True)
|
||||
food_inherit = models.BooleanField(default=False)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE, null=True)
|
||||
@@ -473,18 +470,6 @@ class Unit(ExportModelOperationsMixin('unit'), models.Model, PermissionModelMixi
|
||||
]
|
||||
|
||||
|
||||
class FoodParentIgnore(models.Model, PermissionModelMixin):
|
||||
field = models.CharField(max_length=32, unique=True)
|
||||
name = models.CharField(max_length=64, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return _(self.name)
|
||||
|
||||
@staticmethod
|
||||
def get_name(self):
|
||||
return _(self.name)
|
||||
|
||||
|
||||
class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
|
||||
# exclude fields not implemented yet
|
||||
inherit_fields = FoodInheritField.objects.exclude(field__in=['diet', 'substitute', 'substitute_children', 'substitute_siblings'])
|
||||
@@ -498,8 +483,8 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
|
||||
ignore_shopping = models.BooleanField(default=False)
|
||||
description = models.TextField(default='', blank=True)
|
||||
on_hand = models.BooleanField(default=False)
|
||||
child_inherit = models.BooleanField(default=False)
|
||||
ignore_parent = models.ManyToManyField(FoodParentIgnore, related_name="ignore_parent", blank=True)
|
||||
inherit = models.BooleanField(default=False)
|
||||
ignore_inherit = models.ManyToManyField(FoodInheritField, blank=True) # is this better as inherit instead of ignore inherit? which is more intuitive?
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space', _manager_class=TreeManager)
|
||||
@@ -857,8 +842,17 @@ class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), model
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
@classmethod
|
||||
def list_from_recipe(self, recipe=None, mealplan=None, servings=None, ingredients=None, created_by=None, space=None):
|
||||
@ classmethod
|
||||
def list_from_recipe(self, list_recipe=None, recipe=None, mealplan=None, servings=None, ingredients=None, created_by=None, space=None):
|
||||
"""
|
||||
Creates ShoppingListRecipe and associated ShoppingListEntrys from a recipe or a meal plan with a recipe
|
||||
:param list_recipe: Modify an existing ShoppingListRecipe
|
||||
:param recipe: Recipe to use as list of ingredients. One of [recipe, mealplan] are required
|
||||
:param mealplan: alternatively use a mealplan recipe as source of ingredients
|
||||
:param servings: Optional: Number of servings to use to scale shoppinglist. If servings = 0 an existing recipe list will be deleted
|
||||
:param ingredients: Ingredients, list of ingredient IDs to include on the shopping list. When not provided all ingredients will be used
|
||||
"""
|
||||
# TODO cascade to associated recipes
|
||||
try:
|
||||
r = recipe or mealplan.recipe
|
||||
except AttributeError:
|
||||
|
||||
@@ -12,12 +12,13 @@ from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import NotFound, ValidationError
|
||||
|
||||
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food, ImportLog,
|
||||
Ingredient, Keyword, MealPlan, MealType, NutritionInformation, Recipe,
|
||||
RecipeBook, RecipeBookEntry, RecipeImport, ShareLink, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket,
|
||||
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit,
|
||||
UserFile, UserPreference, ViewLog)
|
||||
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food,
|
||||
FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType,
|
||||
NutritionInformation, Recipe, RecipeBook, RecipeBookEntry,
|
||||
RecipeImport, ShareLink, ShoppingList, ShoppingListEntry,
|
||||
ShoppingListRecipe, Step, Storage, Supermarket, SupermarketCategory,
|
||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UserFile,
|
||||
UserPreference, ViewLog)
|
||||
from cookbook.templatetags.custom_tags import markdown
|
||||
|
||||
|
||||
@@ -129,19 +130,12 @@ class UserNameSerializer(WritableNestedModelSerializer):
|
||||
fields = ('id', 'username')
|
||||
|
||||
|
||||
class FoodInheritFieldSerializer(UniqueFieldsMixin):
|
||||
|
||||
def create(self, validated_data):
|
||||
# don't allow writing to FoodInheritField via API
|
||||
return FoodInheritField.objects.get(**validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
# don't allow writing to FoodInheritField via API
|
||||
return FoodInheritField.objects.get(**validated_data)
|
||||
class FoodInheritFieldSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = FoodInheritField
|
||||
fields = ['id', 'name', 'field', ]
|
||||
read_only_fields = ('id', 'name', 'field', )
|
||||
|
||||
|
||||
class UserPreferenceSerializer(serializers.ModelSerializer):
|
||||
@@ -161,7 +155,7 @@ class UserPreferenceSerializer(serializers.ModelSerializer):
|
||||
fields = (
|
||||
'user', 'theme', 'nav_color', 'default_unit', 'default_page',
|
||||
'search_style', 'show_recent', 'plan_share', 'ingredient_decimals',
|
||||
'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping'
|
||||
'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'food_ignore_default'
|
||||
)
|
||||
|
||||
|
||||
@@ -367,7 +361,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
||||
supermarket_category = SupermarketCategorySerializer(allow_null=True, required=False)
|
||||
recipe = RecipeSimpleSerializer(allow_null=True, required=False)
|
||||
shopping = serializers.SerializerMethodField('get_shopping_status')
|
||||
ignore_inherit = FoodInheritFieldSerializer(allow_null=True, many=True, required=False)
|
||||
ignore_inherit = FoodInheritFieldSerializer(allow_null=True, required=False, many=True)
|
||||
|
||||
recipe_filter = 'steps__ingredients__food'
|
||||
|
||||
@@ -403,7 +397,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
||||
model = Food
|
||||
fields = (
|
||||
'id', 'name', 'description', 'shopping', 'recipe', 'ignore_shopping', 'supermarket_category',
|
||||
'image', 'parent', 'numchild', 'numrecipe', 'on_hand', 'child_inherit', 'ignore_parent'
|
||||
'image', 'parent', 'numchild', 'numrecipe', 'on_hand', 'inherit', 'ignore_inherit'
|
||||
)
|
||||
read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe')
|
||||
|
||||
@@ -930,13 +924,3 @@ class FoodShoppingUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = ['id', 'amount', 'unit', 'delete', ]
|
||||
|
||||
|
||||
class FoodParentIgnoreSerializer(serializers.ModelSerializer):
|
||||
field = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = ['id', 'name', 'field', ]
|
||||
read_only_fields = ('id', 'name', 'field', )
|
||||
|
||||
@@ -48,7 +48,6 @@ def update_step_search_vector(sender, instance=None, created=False, **kwargs):
|
||||
|
||||
|
||||
@receiver(post_save, sender=Food)
|
||||
@skip_signal
|
||||
def update_food_inheritance(sender, instance=None, created=False, **kwargs):
|
||||
if not instance:
|
||||
return
|
||||
|
||||
@@ -19,7 +19,7 @@ router.register(r'automation', api.AutomationViewSet)
|
||||
router.register(r'bookmarklet-import', api.BookmarkletImportViewSet)
|
||||
router.register(r'cook-log', api.CookLogViewSet)
|
||||
router.register(r'food', api.FoodViewSet)
|
||||
router.register(r'food-inherit-ignore', api.FoodParentIgnoreViewSet)
|
||||
router.register(r'food-inherit-field', api.FoodInheritFieldViewSet)
|
||||
router.register(r'import-log', api.ImportLogViewSet)
|
||||
router.register(r'ingredient', api.IngredientViewSet)
|
||||
router.register(r'keyword', api.KeywordViewSet)
|
||||
|
||||
@@ -39,7 +39,7 @@ from cookbook.helper.recipe_html_import import get_recipe_from_source
|
||||
from cookbook.helper.recipe_search import get_facet, old_search, search_recipes
|
||||
from cookbook.helper.recipe_url_import import get_from_scraper
|
||||
from cookbook.helper.shopping_helper import shopping_helper
|
||||
from cookbook.models import (Automation, BookmarkletImport, CookLog, Food, FoodParentIgnore,
|
||||
from cookbook.models import (Automation, BookmarkletImport, CookLog, Food, FoodInheritField,
|
||||
ImportLog, Ingredient, Keyword, MealPlan, MealType, Recipe, RecipeBook,
|
||||
RecipeBookEntry, ShareLink, ShoppingList, ShoppingListEntry,
|
||||
ShoppingListRecipe, Step, Storage, Supermarket, SupermarketCategory,
|
||||
@@ -50,7 +50,7 @@ from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema
|
||||
from cookbook.serializer import (AutomationSerializer, BookmarkletImportSerializer,
|
||||
CookLogSerializer, FoodParentIgnoreSerializer, FoodSerializer,
|
||||
CookLogSerializer, FoodInheritFieldSerializer, FoodSerializer,
|
||||
FoodShoppingUpdateSerializer, ImportLogSerializer,
|
||||
IngredientSerializer, KeywordSerializer, MealPlanSerializer,
|
||||
MealTypeSerializer, RecipeBookEntrySerializer,
|
||||
@@ -393,14 +393,14 @@ class UnitViewSet(viewsets.ModelViewSet, MergeMixin, FuzzyFilterMixin):
|
||||
pagination_class = DefaultPagination
|
||||
|
||||
|
||||
class FoodParentIgnoreViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = FoodParentIgnore.objects
|
||||
serializer_class = FoodParentIgnoreSerializer
|
||||
class FoodInheritFieldViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = FoodInheritField.objects
|
||||
serializer_class = FoodInheritFieldSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
# exclude fields not yet implemented
|
||||
return self.queryset.exclude(field__in=['diet', 'substitute', 'substitute_children', 'substitute_siblings'])
|
||||
return Food.inherit_fields
|
||||
|
||||
|
||||
class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
||||
|
||||
Reference in New Issue
Block a user