nested serializers and basic recipe editing working
This commit is contained in:
parent
5e20833b7e
commit
c178aea363
@ -1,4 +1,5 @@
|
||||
from django.contrib.auth.models import User
|
||||
from drf_writable_nested import WritableNestedModelSerializer, UniqueFieldsMixin
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.fields import CurrentUserDefault
|
||||
@ -38,71 +39,50 @@ class SyncLogSerializer(serializers.ModelSerializer):
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class KeywordSerializer(serializers.ModelSerializer):
|
||||
class KeywordSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Keyword
|
||||
fields = '__all__'
|
||||
validators = []
|
||||
extra_kwargs = {
|
||||
"name": {
|
||||
"validators": [],
|
||||
},
|
||||
}
|
||||
|
||||
def validate(self, attrs):
|
||||
return attrs
|
||||
|
||||
|
||||
class UnitSerializer(serializers.ModelSerializer):
|
||||
class UnitSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Unit
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class FoodSerializer(serializers.ModelSerializer):
|
||||
class FoodSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Food
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class IngredientSerializer(serializers.ModelSerializer):
|
||||
food = FoodSerializer(read_only=True)
|
||||
unit = UnitSerializer(read_only=True)
|
||||
class IngredientSerializer(WritableNestedModelSerializer):
|
||||
food = FoodSerializer()
|
||||
unit = UnitSerializer()
|
||||
|
||||
class Meta:
|
||||
model = Ingredient
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class StepSerializer(serializers.ModelSerializer):
|
||||
ingredients = IngredientSerializer(many=True, read_only=True)
|
||||
class StepSerializer(WritableNestedModelSerializer):
|
||||
ingredients = IngredientSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Step
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class RecipeSerializer(serializers.ModelSerializer):
|
||||
steps = StepSerializer(many=True, read_only=True)
|
||||
keywords = KeywordSerializer(many=True, read_only=False, validators=[])
|
||||
class RecipeSerializer(WritableNestedModelSerializer):
|
||||
steps = StepSerializer(many=True)
|
||||
keywords = KeywordSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = '__all__'
|
||||
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 Meta:
|
||||
|
@ -61,7 +61,82 @@
|
||||
</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>
|
||||
</div>
|
||||
|
||||
@ -81,6 +156,10 @@
|
||||
recipe: undefined,
|
||||
keywords: [],
|
||||
keywords_loading: false,
|
||||
foods: [],
|
||||
foods_loading: false,
|
||||
units: [],
|
||||
units_loading: false,
|
||||
},
|
||||
directives: {
|
||||
tabindex: {
|
||||
@ -91,6 +170,9 @@
|
||||
},
|
||||
mounted: function () {
|
||||
this.loadRecipe()
|
||||
this.searchUnits('')
|
||||
this.searchFoods('')
|
||||
this.searchKeywords('')
|
||||
},
|
||||
methods: {
|
||||
loadRecipe: function () {
|
||||
@ -110,15 +192,41 @@
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
addStep: function () { //TODO see if default can be generated from options request
|
||||
this.recipe.steps.push(
|
||||
{'instruction': '', ingredients: []}
|
||||
)
|
||||
},
|
||||
searchKeywords: function (query) {
|
||||
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_loading = false
|
||||
}).catch((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>
|
||||
|
@ -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.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.nextcloud import Nextcloud
|
||||
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):
|
||||
queryset = Food.objects.all()
|
||||
queryset = Unit.objects.all()
|
||||
serializer_class = FoodSerializer
|
||||
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):
|
||||
queryset = Food.objects.all()
|
||||
serializer_class = FoodSerializer
|
||||
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):
|
||||
queryset = Ingredient.objects.all()
|
||||
@ -147,7 +169,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = RecipeSerializer
|
||||
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()
|
||||
query = self.request.query_params.get('query', None)
|
||||
if query is not None:
|
||||
@ -171,7 +193,7 @@ class KeywordViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = KeywordSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self): # TODO create standard filter/limit mixin
|
||||
queryset = Keyword.objects.all()
|
||||
query = self.request.query_params.get('query', None)
|
||||
if query is not None:
|
||||
|
@ -9,6 +9,7 @@ django-emoji-picker==0.0.6
|
||||
django-filter==2.2.0
|
||||
django-tables2==2.3.1
|
||||
djangorestframework==3.11.0
|
||||
drf-writable-nested==0.6.0
|
||||
gunicorn==20.0.4
|
||||
lxml==4.5.1
|
||||
Markdown==3.2.2
|
||||
|
Loading…
Reference in New Issue
Block a user