nested serializers and basic recipe editing working

This commit is contained in:
vabene1111 2020-06-26 13:41:41 +02:00
parent 5e20833b7e
commit c178aea363
4 changed files with 149 additions and 38 deletions

View File

@ -1,4 +1,5 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from drf_writable_nested import WritableNestedModelSerializer, UniqueFieldsMixin
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.fields import CurrentUserDefault from rest_framework.fields import CurrentUserDefault
@ -38,71 +39,50 @@ class SyncLogSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class KeywordSerializer(serializers.ModelSerializer): class KeywordSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
class Meta: class Meta:
model = Keyword model = Keyword
fields = '__all__' fields = '__all__'
validators = []
extra_kwargs = {
"name": {
"validators": [],
},
}
def validate(self, attrs):
return attrs
class UnitSerializer(serializers.ModelSerializer): class UnitSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
class Meta: class Meta:
model = Unit model = Unit
fields = '__all__' fields = '__all__'
class FoodSerializer(serializers.ModelSerializer): class FoodSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
class Meta: class Meta:
model = Food model = Food
fields = '__all__' fields = '__all__'
class IngredientSerializer(serializers.ModelSerializer): class IngredientSerializer(WritableNestedModelSerializer):
food = FoodSerializer(read_only=True) food = FoodSerializer()
unit = UnitSerializer(read_only=True) unit = UnitSerializer()
class Meta: class Meta:
model = Ingredient model = Ingredient
fields = '__all__' fields = '__all__'
class StepSerializer(serializers.ModelSerializer): class StepSerializer(WritableNestedModelSerializer):
ingredients = IngredientSerializer(many=True, read_only=True) ingredients = IngredientSerializer(many=True)
class Meta: class Meta:
model = Step model = Step
fields = '__all__' fields = '__all__'
class RecipeSerializer(serializers.ModelSerializer): class RecipeSerializer(WritableNestedModelSerializer):
steps = StepSerializer(many=True, read_only=True) steps = StepSerializer(many=True)
keywords = KeywordSerializer(many=True, read_only=False, validators=[]) keywords = KeywordSerializer(many=True)
class Meta: class Meta:
model = Recipe model = Recipe
fields = '__all__' fields = '__all__'
validators = [] validators = []
def update(self, instance, validated_data):
for k in validated_data['keyword']:
pass
return instance
def create(self, validated_data):
print('test')
pass
def validate(self, attrs):
return attrs
class RecipeImportSerializer(serializers.ModelSerializer): class RecipeImportSerializer(serializers.ModelSerializer):
class Meta: class Meta:

View File

@ -61,7 +61,82 @@
</div> </div>
</div> </div>
<button type="button" @click="updateRecipe()">Save</button> <template v-for="step, index in recipe.steps">
<div class="row">
<div class="col-md-12">
<h3>Step [[index + 1]]</h3>
</div>
</div>
<div class="row">
<div class="col-md-12">
<template v-for="ingredient, index in step.ingredients">
<div class="card">
<div class="card-body" style="padding: 12px">
<div class="row">
<div class="col-md-3">
<input class="form-control" v-model="ingredient.amount">
</div>
<div class="col-md-3">
<multiselect v-tabindex
ref="unit"
v-model="ingredient.unit"
:options="units"
:close-on-select="true"
:clear-on-select="true"
:allow-empty="true"
:preserve-search="true"
placeholder="{% trans 'Select one' %}"
tag-placeholder="{% trans 'Select' %}"
label="name"
:id="'unit_' + index"
track-by="id"
:multiple="false"
:loading="units_loading"
@search-change="searchUnits">
</multiselect>
</div>
<div class="col-md-3">
<multiselect v-tabindex
ref="food"
v-model="ingredient.food"
:options="foods"
:close-on-select="true"
:clear-on-select="true"
:allow-empty="true"
:preserve-search="true"
placeholder="{% trans 'Select one' %}"
tag-placeholder="{% trans 'Select' %}"
label="name"
:id="'food_' + index"
track-by="id"
:multiple="false"
:loading="foods_loading"
@search-change="searchFoods">
</multiselect>
</div>
<div class="col-md-3">
<input class="form-control" v-model="ingredient.note">
</div>
</div>
</div>
</div>
</template>
</div>
</div>
<div class="row">
<div class="col-md-12">
<textarea class="form-control" rows="8" v-model="step.instruction"
:id="'id_instruction_' + step.id"></textarea>
</div>
</div>
<br/>
</template>
<button type="button" @click="updateRecipe()" class="btn">Save</button>
<button type="button" @click="addStep()" class="btn">Add Step</button>
</template> </template>
</div> </div>
@ -81,6 +156,10 @@
recipe: undefined, recipe: undefined,
keywords: [], keywords: [],
keywords_loading: false, keywords_loading: false,
foods: [],
foods_loading: false,
units: [],
units_loading: false,
}, },
directives: { directives: {
tabindex: { tabindex: {
@ -91,6 +170,9 @@
}, },
mounted: function () { mounted: function () {
this.loadRecipe() this.loadRecipe()
this.searchUnits('')
this.searchFoods('')
this.searchKeywords('')
}, },
methods: { methods: {
loadRecipe: function () { loadRecipe: function () {
@ -110,15 +192,41 @@
console.log(err) console.log(err)
}) })
}, },
addStep: function () { //TODO see if default can be generated from options request
this.recipe.steps.push(
{'instruction': '', ingredients: []}
)
},
searchKeywords: function (query) { searchKeywords: function (query) {
this.keywords_loading = true this.keywords_loading = true
this.$http.get("{% url 'api:keyword-list' %}" + '?query=' + query).then((response) => { this.$http.get("{% url 'api:keyword-list' %}" + '?query=' + query + '&limit=10').then((response) => {
this.keywords = response.data; this.keywords = response.data;
this.keywords_loading = false this.keywords_loading = false
}).catch((err) => { }).catch((err) => {
console.log(err) console.log(err)
}) })
}, },
searchUnits: function (query) {
this.units_loading = true
this.$http.get("{% url 'api:unit-list' %}" + '?query=' + query + '&limit=10').then((response) => {
this.units = response.data;
//TODO add back code to include custom created ingredients
this.units_loading = false
}).catch((err) => {
console.log(err)
})
},
searchFoods: function (query) {
this.foods_loading = true
this.$http.get("{% url 'api:food-list' %}" + '?query=' + query + '&limit=10').then((response) => {
this.foods = response.data
//TODO add back code to include custom created ingredients
this.foods_loading = false
}).catch((err) => {
console.log(err)
})
},
} }
}); });
</script> </script>

View File

@ -18,7 +18,7 @@ from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, ListMode
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser
from cookbook.helper.recipe_url_import import get_from_html from cookbook.helper.recipe_url_import import get_from_html
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit
from cookbook.provider.dropbox import Dropbox from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud from cookbook.provider.nextcloud import Nextcloud
from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \ from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \
@ -112,16 +112,38 @@ class MealTypeViewSet(viewsets.ModelViewSet):
class UnitViewSet(viewsets.ModelViewSet): class UnitViewSet(viewsets.ModelViewSet):
queryset = Food.objects.all() queryset = Unit.objects.all()
serializer_class = FoodSerializer serializer_class = FoodSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser]
def get_queryset(self): # TODO create standard filter/limit mixin
queryset = self.queryset
query = self.request.query_params.get('query', None)
if query is not None:
queryset = queryset.filter(name__icontains=query)
limit = self.request.query_params.get('limit', None)
if limit is not None:
queryset = queryset[:int(limit)]
return queryset
class FoodViewSet(viewsets.ModelViewSet): class FoodViewSet(viewsets.ModelViewSet):
queryset = Food.objects.all() queryset = Food.objects.all()
serializer_class = FoodSerializer serializer_class = FoodSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser]
def get_queryset(self): # TODO create standard filter/limit mixin
queryset = self.queryset
query = self.request.query_params.get('query', None)
if query is not None:
queryset = queryset.filter(name__icontains=query)
limit = self.request.query_params.get('limit', None)
if limit is not None:
queryset = queryset[:int(limit)]
return queryset
class IngredientViewSet(viewsets.ModelViewSet): class IngredientViewSet(viewsets.ModelViewSet):
queryset = Ingredient.objects.all() queryset = Ingredient.objects.all()
@ -147,7 +169,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
serializer_class = RecipeSerializer serializer_class = RecipeSerializer
permission_classes = [permissions.IsAuthenticated] # TODO split read and write permission for meal plan guest permission_classes = [permissions.IsAuthenticated] # TODO split read and write permission for meal plan guest
def get_queryset(self): def get_queryset(self): # TODO create standard filter/limit mixin
queryset = Recipe.objects.all() queryset = Recipe.objects.all()
query = self.request.query_params.get('query', None) query = self.request.query_params.get('query', None)
if query is not None: if query is not None:
@ -171,7 +193,7 @@ class KeywordViewSet(viewsets.ModelViewSet):
serializer_class = KeywordSerializer serializer_class = KeywordSerializer
permission_classes = [CustomIsUser] permission_classes = [CustomIsUser]
def get_queryset(self): def get_queryset(self): # TODO create standard filter/limit mixin
queryset = Keyword.objects.all() queryset = Keyword.objects.all()
query = self.request.query_params.get('query', None) query = self.request.query_params.get('query', None)
if query is not None: if query is not None:

View File

@ -9,6 +9,7 @@ django-emoji-picker==0.0.6
django-filter==2.2.0 django-filter==2.2.0
django-tables2==2.3.1 django-tables2==2.3.1
djangorestframework==3.11.0 djangorestframework==3.11.0
drf-writable-nested==0.6.0
gunicorn==20.0.4 gunicorn==20.0.4
lxml==4.5.1 lxml==4.5.1
Markdown==3.2.2 Markdown==3.2.2