Merge branch 'release/r2022-02-13'

# Conflicts:
#	recipe/views.py
This commit is contained in:
Ching 2022-02-13 20:13:08 +08:00
commit 056e18d294
14 changed files with 454 additions and 59 deletions

View File

@ -33,3 +33,4 @@ zipp==3.5.0
redis==4.1.0 redis==4.1.0
black black
instagram_private_api instagram_private_api
drf-nested-routers

View File

@ -27,7 +27,6 @@ DEBUG = True
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [

View File

@ -18,8 +18,8 @@ Including another URLconf
from django.urls import include, path from django.urls import include, path
from django.conf.urls import url from django.conf.urls import url
from rest_framework import routers
from rest_framework.authtoken import views from rest_framework.authtoken import views
from rest_framework_nested import routers
router = routers.DefaultRouter() router = routers.DefaultRouter()
# Wire up our API using automatic URL routing. # Wire up our API using automatic URL routing.

View File

@ -138,7 +138,7 @@ export default {
}); });
} else { } else {
axios axios
.put(config.publicPath + '/recipe/recipe/' + recipe_id, data) .put(config.publicPath + '/recipe/recipe/' + recipe_id + '/', data)
.then(function () { .then(function () {
ElMessage({ ElMessage({
message: '修改成功.', message: '修改成功.',
@ -152,7 +152,7 @@ export default {
}, },
onSubmitDelete(recipe_id) { onSubmitDelete(recipe_id) {
axios axios
.delete(config.publicPath + '/recipe/recipe/' + recipe_id) .delete(config.publicPath + '/recipe/recipe/' + recipe_id + '/')
.then(function () { .then(function () {
ElMessage.error('删除成功.'); ElMessage.error('删除成功.');
location.reload(); location.reload();

View File

@ -42,6 +42,94 @@
type="textarea" type="textarea"
/> />
</van-cell-group> </van-cell-group>
<van-cell-group
class="recipe-create"
inset
border
v-if="form.recipe_ingredients && form.recipe_ingredients.length > 0"
title="数量为 0 的食材将被删除"
>
<van-cell
v-bind:title="recipe_ingredient.ingredient.name"
v-for="recipe_ingredient in form.recipe_ingredients"
:key="recipe_ingredient.id"
>
<template #value>
<van-stepper
v-model.number="recipe_ingredient.quantity"
default-value="recipe_ingredient.quantity"
:decimal-length="1"
allow-empty="true"
min="0"
/>
{{ recipe_ingredient.ingredient.unit }}
</template>
</van-cell>
</van-cell-group>
<div class="recipe-create">
<van-row gutter="20">
<van-col span="8">
<van-button
icon="plus"
type="warning"
plain
round
hairline
class="submit-button"
@click="showIngredientDialog = true"
>创建食材</van-button
>
</van-col>
<van-col span="16">
<van-button
icon="plus"
type="primary"
plain
round
hairline
class="submit-button"
@click="showIngredientPicker = true"
>添加食材</van-button
>
</van-col>
</van-row>
</div>
<van-popup v-model:show="showIngredientPicker" round position="bottom">
<van-picker
:columns="ingredient_columns"
:columns-field-names="customFieldName"
@cancel="showIngredientPicker = false"
@confirm="onConfirmIngredient"
/>
</van-popup>
<van-dialog
v-model:show="showIngredientDialog"
title="创建食材"
show-cancel-button
confirm-button-text="创建"
@confirm="onCreateIngredient"
>
<van-cell-group inset border>
<van-field
v-model="ingredient_name"
label="名称"
name="ingredient_name"
placeholder="葱、蒜、姜..."
:rules="[{ required: true, message: '请填写食材名称' }]"
/>
<van-field
v-model="ingredient_unit"
label="单位"
name="ingredient_unit"
placeholder="个、斤、千克..."
:rules="[{ required: true, message: '请填写单位' }]"
/>
</van-cell-group>
</van-dialog>
<div class="recipe-create"> <div class="recipe-create">
<van-row gutter="20"> <van-row gutter="20">
<van-col span="8" v-if="recipe_id"> <van-col span="8" v-if="recipe_id">
@ -49,7 +137,6 @@
class="submit-button" class="submit-button"
round round
type="danger" type="danger"
plain
hairline hairline
:disabled="disable_submit" :disabled="disable_submit"
@click="onSubmitDelete(recipe_id)" @click="onSubmitDelete(recipe_id)"
@ -62,7 +149,6 @@
class="submit-button" class="submit-button"
round round
type="primary" type="primary"
plain
hairline hairline
:disabled="disable_submit" :disabled="disable_submit"
@click="onSubmit(recipe_id)" @click="onSubmit(recipe_id)"
@ -75,7 +161,6 @@
class="submit-button" class="submit-button"
round round
type="primary" type="primary"
plain
hairline hairline
:disabled="disable_submit" :disabled="disable_submit"
@click="onSubmit(recipe_id)" @click="onSubmit(recipe_id)"
@ -90,21 +175,27 @@
<script> <script>
import { import {
Form, Button,
Field, Cell,
CellGroup, CellGroup,
Col,
Dialog,
Field,
Form,
Picker,
Popup,
Radio, Radio,
RadioGroup, RadioGroup,
Rate, Rate,
Button,
Toast,
Col,
Row, Row,
Stepper,
Toast,
} from 'vant'; } from 'vant';
import axios from 'axios'; import axios from 'axios';
import config from '@/config/index'; import config from '@/config/index';
import router from '@/router/index';
import constants from '@/utils/constants.js'; import constants from '@/utils/constants.js';
import router from '@/router/index';
import { ref } from 'vue';
export default { export default {
props: ['recipe_'], props: ['recipe_'],
@ -115,17 +206,29 @@ export default {
}, },
}, },
components: { components: {
[Form.name]: Form, [Button.name]: Button,
[Field.name]: Field, [Cell.name]: Cell,
[CellGroup.name]: CellGroup, [CellGroup.name]: CellGroup,
[Col.name]: Col,
[Dialog.Component.name]: Dialog.Component,
[Field.name]: Field,
[Form.name]: Form,
[Picker.name]: Picker,
[Popup.name]: Popup,
[Radio.name]: Radio, [Radio.name]: Radio,
[RadioGroup.name]: RadioGroup, [RadioGroup.name]: RadioGroup,
[Rate.name]: Rate, [Rate.name]: Rate,
[Button.name]: Button,
[Col.name]: Col,
[Row.name]: Row, [Row.name]: Row,
[Stepper.name]: Stepper,
}, },
data() { data() {
const customFieldName = {
text: 'name',
};
const showIngredientPicker = ref(false);
const showIngredientDialog = ref(false);
const disable_submit = ref(false);
return { return {
form: { form: {
name: null, name: null,
@ -133,12 +236,22 @@ export default {
rate: 0, rate: 0,
difficulty: 0, difficulty: 0,
note: null, note: null,
recipe_ingredients: null,
}, },
loading: false, loading: false,
disable_submit,
recipe_id: null, recipe_id: null,
ingredient_columns: null,
customFieldName,
showIngredientPicker,
showIngredientDialog,
ingredient_name: null,
ingredient_unit: null,
}; };
}, },
mounted() {}, mounted() {
this.getIngredients();
},
methods: { methods: {
onSubmit(recipe_id) { onSubmit(recipe_id) {
if (!this.form.name) { if (!this.form.name) {
@ -153,6 +266,9 @@ export default {
difficulty: this.form.difficulty, difficulty: this.form.difficulty,
rate: this.form.rate, rate: this.form.rate,
note: this.form.note ? this.form.note : null, note: this.form.note ? this.form.note : null,
recipe_ingredients: this.form.recipe_ingredients
? this.form.recipe_ingredients
: [],
}; };
if (!recipe_id) { if (!recipe_id) {
axios axios
@ -161,14 +277,19 @@ export default {
(response) => (response, router.push({ name: 'RecipeMobileHome' })) (response) => (response, router.push({ name: 'RecipeMobileHome' }))
); );
} else { } else {
axios.put(config.publicPath + '/recipe/recipe/' + recipe_id, data).then( axios
(Toast.success({ .put(config.publicPath + '/recipe/recipe/' + recipe_id + '/', data)
message: '修改成功', .then(
forbidClick: true, (response) => (
duration: 500, response,
}), Toast.success({
(this.loading = false)) message: '修改成功',
); forbidClick: true,
duration: 500,
}),
(this.loading = false)
)
);
} }
}, },
onSubmitDelete(recipe_id) { onSubmitDelete(recipe_id) {
@ -176,14 +297,99 @@ export default {
return; return;
} }
this.loading = true; this.loading = true;
axios.delete(config.publicPath + '/recipe/recipe/' + recipe_id).then( axios
(Toast.success({ .delete(config.publicPath + '/recipe/recipe/' + recipe_id + '/')
message: '删除成功', .then(
(Toast.success({
message: '删除成功',
forbidClick: true,
duration: 500,
}),
(this.loading = false))
);
},
getIngredients() {
axios.get(config.publicPath + '/recipe/ingredient/').then((response) => {
this.ingredient_columns = response.data['results'];
});
},
onConfirmIngredient(value) {
var exists = false;
this.showIngredientPicker = false;
if (this.form.recipe_ingredients === null) {
this.form.recipe_ingredients = [];
}
for (let i = 0; i < this.form.recipe_ingredients.length; i++) {
if (this.form.recipe_ingredients[i].ingredient.id == value.id) {
exists = true;
break;
}
}
if (exists) {
Toast.fail({
message: '该食材已存在',
forbidClick: true, forbidClick: true,
duration: 500, duration: 800,
}), });
(this.loading = false)) } else {
this.form.recipe_ingredients.push({
id: 0,
ingredient: {
id: value.id,
name: value.name,
unit: value.unit,
}, //
});
}
},
onCreateIngredient() {
console.log(
'onCreateIngredient',
this.ingredient_name,
this.ingredient_unit
); );
if (!this.ingredient_name || !this.ingredient_unit) {
Toast.fail({
message: '请输入食材名称和单位',
forbidClick: true,
duration: 800,
});
return;
}
this.showIngredientDialog = false;
for (let i = 0; i < this.ingredient_columns.length; i++) {
if (this.ingredient_columns[i].name == this.ingredient_name) {
Toast.fail({
message: '该食材已存在',
forbidClick: true,
duration: 800,
});
break;
}
}
axios
.post(config.publicPath + '/recipe/ingredient/', {
name: this.ingredient_name,
unit: this.ingredient_unit,
})
.then(
(response) => (
response,
this.ingredient_columns.unshift({
id: response.data.id,
name: this.ingredient_name,
unit: this.ingredient_unit,
}),
Toast.success({
message: '创建成功',
forbidClick: true,
duration: 800,
})
)
);
// this.ingredient_name = value.name;
// this.ingredient_unit = value.unit;
}, },
}, },
}; };

View File

@ -21,7 +21,7 @@ export default {
}, },
mounted() { mounted() {
axios axios
.get(config.publicPath + '/recipe/recipe/' + this.$route.params.id) .get(config.publicPath + '/recipe/recipe/' + this.$route.params.id + '/')
.then((response) => (this.recipe = response.data)); .then((response) => (this.recipe = response.data));
}, },
}; };

View File

@ -2,12 +2,8 @@
<el-container> <el-container>
<el-header> <el-header>
<el-menu mode="horizontal" default-active="#" router> <el-menu mode="horizontal" default-active="#" router>
<el-menu-item index="/"> <el-menu-item index="/"> 首页 </el-menu-item>
首页 <el-menu-item index="/week-recipe/"> 每周菜谱 </el-menu-item>
</el-menu-item>
<el-menu-item index="/week-recipe/">
每周菜谱
</el-menu-item>
<el-menu-item index="#"> <el-menu-item index="#">
{{ recipe.name }} {{ recipe.name }}
</el-menu-item> </el-menu-item>
@ -33,7 +29,7 @@ import config from '@/config/index';
export default { export default {
name: 'RecipeDetail', name: 'RecipeDetail',
components: { input_recipe }, components: { input_recipe },
data: function() { data: function () {
return { return {
recipe: {}, recipe: {},
constants: constants, constants: constants,
@ -41,7 +37,7 @@ export default {
}, },
mounted() { mounted() {
axios axios
.get(config.publicPath + '/recipe/recipe/' + this.$route.params.id) .get(config.publicPath + '/recipe/recipe/' + this.$route.params.id + '/')
.then((response) => (this.recipe = response.data)); .then((response) => (this.recipe = response.data));
}, },
methods: { methods: {

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.6 on 2022-02-12 13:43
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [('recipe', '0003_recipe_status')]
operations = [
migrations.CreateModel(
name='Ingredient',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128)),
('unit', models.CharField(max_length=32)),
('status', models.CharField(default='active', max_length=32)),
],
),
migrations.CreateModel(
name='RecipeIngredient',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.FloatField()),
(
'ingredient',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='recipe_ingredients',
to='recipe.ingredient',
),
),
(
'recipe',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='recipe_ingredients',
to='recipe.recipe',
),
),
('status', models.CharField(default='active', max_length=32)),
],
),
]

View File

@ -6,6 +6,27 @@ from utils import const
import utils import utils
class Ingredient(models.Model):
name = models.CharField(max_length=128)
unit = models.CharField(max_length=32)
status = models.CharField(max_length=32, default=const.INGREDIENT_STATUS_ACTIVE)
class RecipeIngredient(models.Model):
recipe = models.ForeignKey('Recipe', on_delete=models.CASCADE, related_name='recipe_ingredients')
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE, related_name='recipe_ingredients')
status = models.CharField(max_length=32, default=const.INGREDIENT_STATUS_ACTIVE)
quantity = models.FloatField()
def serialize(self):
return {
'id': self.id,
'recipe': {'id': self.recipe.id, 'name': self.recipe.name},
'ingredient': {'id': self.ingredient.id, 'name': self.ingredient.name, 'unit': self.ingredient.unit},
'quantity': self.quantity,
}
class Recipe(models.Model): class Recipe(models.Model):
name = models.CharField(max_length=128) name = models.CharField(max_length=128)
recipe_type = models.CharField(max_length=32, default=const.RECIPE_TYPE_MEAT) recipe_type = models.CharField(max_length=32, default=const.RECIPE_TYPE_MEAT)
@ -17,10 +38,20 @@ class Recipe(models.Model):
def serialize(self, verbose=False): def serialize(self, verbose=False):
data = {'id': self.id, 'name': self.name, 'recipe_type': self.recipe_type} data = {'id': self.id, 'name': self.name, 'recipe_type': self.recipe_type}
if verbose: if verbose:
data.update({'difficulty': self.difficulty, 'rate': self.rate, 'note': self.note}) data.update(
{
'difficulty': self.difficulty,
'rate': self.rate,
'note': self.note,
'recipe_ingredients': self.get_ingredients(),
}
)
return data return data
def get_ingredients(self):
return [i.serialize() for i in self.recipeingredient_set.order_by('id')]
@classmethod @classmethod
def create_from_str(cls, content): def create_from_str(cls, content):
content = content.strip() content = content.strip()

View File

@ -1,16 +1,61 @@
from os import read
from django.contrib.auth.models import User, Group
from rest_framework import serializers from rest_framework import serializers
import recipe.models import recipe.models
from utils import const
class IngredientSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
class Meta:
model = recipe.models.Ingredient
fields = '__all__'
class RecipeIngredientSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
ingredient = IngredientSerializer()
def update(self, instance, validated_data):
if 'ingredient' in validated_data:
validated_data.pop('ingredient')
if 'recipe' in validated_data:
validated_data.pop('recipe')
return super().update(instance, validated_data)
class Meta:
model = recipe.models.RecipeIngredient
fields = '__all__'
class RecipeSerializer(serializers.ModelSerializer): class RecipeSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True) id = serializers.IntegerField(read_only=True)
recipe_ingredients = RecipeIngredientSerializer(many=True, read_only=True)
def create(self, validated_data):
if 'recipe_ingredients' in validated_data:
recipe_ingredients = validated_data.pop('recipe_ingredients')
instance = super().create(validated_data)
if recipe_ingredients:
for recipe_ingredient in recipe_ingredients:
recipe_ingredient['recipe'] = instance
RecipeIngredientSerializer.create(recipe_ingredient)
return instance
@property
def data(self):
# exclude deleted recipe_ingredients
data_ = super().data
data_['recipe_ingredients'] = [
ingredient
for ingredient in data_['recipe_ingredients']
if ingredient['status'] != const.INGREDIENT_STATUS_DELETED
]
return data_
class Meta: class Meta:
model = recipe.models.Recipe model = recipe.models.Recipe
fields = '__all__' fields = ('recipe_ingredients', 'id', 'name', 'recipe_type', 'status', 'note', 'rate', 'difficulty')
class WeekRecipeSerializer(serializers.ModelSerializer): class WeekRecipeSerializer(serializers.ModelSerializer):

View File

@ -1,16 +1,22 @@
from django.conf.urls import include, url from django.conf.urls import include, url
# from django.core.urlresolvers import reverse
from django.urls import path from django.urls import path
from rest_framework import routers
from recipe import views from recipe import views
# Wire up our API using automatic URL routing. # Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API. # Additionally, we include login URLs for the browsable API.
from rest_framework_nested import routers
router = routers.DefaultRouter()
router.register(r'recipe', views.RecipeAPI)
router.register(r'ingredient', views.IngredientAPI)
recipe_nested_router = routers.NestedSimpleRouter(router, r'recipe', lookup='recipe')
recipe_nested_router.register(r'recipe-ingredient', views.RecipeIngredientAPI)
urlpatterns = [ urlpatterns = [
url(r'^recipe/(?P<pk>\d+)$', views.RecipeAPI.as_view(), name='recipe-detail'),
url(r'^recipe/$', views.RecipeListAPI.as_view()),
url(r'^week-recipe/$', views.WeekRecipeListAPI.as_view()), url(r'^week-recipe/$', views.WeekRecipeListAPI.as_view()),
url(r'^daily-recipe/(?P<pk>\d+)$', views.DailyRecipeAPI.as_view(), name='dailyrecipe-detail'), url(r'^daily-recipe/(?P<pk>\d+)$', views.DailyRecipeAPI.as_view(), name='dailyrecipe-detail'),
path(r'', include(router.urls)),
path(r'', include(recipe_nested_router.urls)),
] ]

View File

@ -12,7 +12,7 @@ import recipe.serializers
from utils import const from utils import const
class RecipeAPI(rest_framework.generics.RetrieveUpdateDestroyAPIView): class RecipeAPI(viewsets.ModelViewSet):
# authentication_classes = (authentication.TokenAuthentication, # authentication_classes = (authentication.TokenAuthentication,
# authentication.SessionAuthentication, # authentication.SessionAuthentication,
@ -25,15 +25,38 @@ class RecipeAPI(rest_framework.generics.RetrieveUpdateDestroyAPIView):
instance.status = const.RECIPE_STATUS_DELETED instance.status = const.RECIPE_STATUS_DELETED
instance.save(update_fields=['status']) instance.save(update_fields=['status'])
def perform_update(self, serializer):
instance = self.get_object()
if 'recipe_ingredients' in self.request.data:
recipe_ingredients = self.request.data.pop('recipe_ingredients')
if recipe_ingredients:
for recipe_ingredient in recipe_ingredients:
# 没有 id 则新增食材
if not recipe_ingredient['id'] and recipe_ingredient['quantity']:
ingredient = recipe.models.Ingredient.objects.get(id=recipe_ingredient['ingredient']['id'])
del recipe_ingredient['ingredient']
del recipe_ingredient['id']
recipe.models.RecipeIngredient.objects.create(
recipe=instance, ingredient=ingredient, **recipe_ingredient
)
# 有 id 没数量则删除食材
elif recipe_ingredient['id'] and not recipe_ingredient['quantity']:
recipe.models.RecipeIngredient.objects.filter(id=recipe_ingredient['id']).update(
status=const.INGREDIENT_STATUS_DELETED
)
# 有 id 有数量则更新食材
elif recipe_ingredient['id'] and recipe_ingredient['quantity']:
del recipe_ingredient['ingredient']
recipe.models.RecipeIngredient.objects.filter(id=recipe_ingredient['id']).update(
**recipe_ingredient
)
class RecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics.CreateAPIView): return super().perform_update(serializer)
def list(self, request, *args, **kwargs):
self.queryset = recipe.models.Recipe.objects.exclude(status=const.RECIPE_STATUS_DELETED)
return super().list(request, *args, **kwargs)
# authentication_classes = (authentication.TokenAuthentication,
# authentication.SessionAuthentication,
# authentication.BasicAuthentication)
# permission_classes = (permissions.IsAuthenticated,)
queryset = recipe.models.Recipe.objects.exclude(status=const.RECIPE_STATUS_DELETED).order_by('-id')
serializer_class = recipe.serializers.RecipeSerializer
filterset_fields = { filterset_fields = {
'recipe_type': const.FILTER_EXACT, 'recipe_type': const.FILTER_EXACT,
'difficulty': const.FILTER_GTE_LTE, 'difficulty': const.FILTER_GTE_LTE,
@ -41,7 +64,7 @@ class RecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics
} }
class WeekRecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics.CreateAPIView): class WeekRecipeListAPI(rest_framework.generics.ListCreateAPIView):
queryset = recipe.models.DailyRecipe.objects.all() queryset = recipe.models.DailyRecipe.objects.all()
serializer_class = recipe.serializers.WeekRecipeSerializer serializer_class = recipe.serializers.WeekRecipeSerializer
@ -78,3 +101,44 @@ class DailyRecipeAPI(rest_framework.generics.RetrieveUpdateAPIView):
recipes.extend(request.data.get('soup', [])) recipes.extend(request.data.get('soup', []))
daily_recipe.recipes.set(recipe.models.Recipe.objects.filter(id__in=recipes)) daily_recipe.recipes.set(recipe.models.Recipe.objects.filter(id__in=recipes))
return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, headers={}) return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, headers={})
class IngredientAPI(viewsets.ModelViewSet):
# authentication_classes = (authentication.TokenAuthentication,
# authentication.SessionAuthentication,
# authentication.BasicAuthentication)
# permission_classes = (permissions.IsAuthenticated,)
queryset = recipe.models.Ingredient.objects.all()
serializer_class = recipe.serializers.IngredientSerializer
def list(self, request, *args, **kwargs):
self.queryset = recipe.models.Ingredient.objects.exclude(status=const.INGREDIENT_STATUS_DELETED).order_by('-id')
return super().list(request, *args, **kwargs)
def perform_destroy(self, instance):
instance.status = const.INGREDIENT_STATUS_DELETED
instance.save(update_fields=['status'])
class RecipeIngredientAPI(viewsets.ModelViewSet):
# authentication_classes = (authentication.TokenAuthentication,
# authentication.SessionAuthentication,
# authentication.BasicAuthentication)
# permission_classes = (permissions.IsAuthenticated,)
queryset = recipe.models.RecipeIngredient.objects.exclude(status=const.INGREDIENT_STATUS_DELETED)
serializer_class = recipe.serializers.RecipeIngredientSerializer
def get_queryset(self):
return self.queryset.filter(recipe=self.kwargs['recipe_pk'])
def perform_destroy(self, instance):
instance.status = const.INGREDIENT_STATUS_DELETED
instance.save(update_fields=['status'])
def create(self, request, *args, **kwargs):
recipe_ingredient = recipe.models.RecipeIngredient.objects.create(
recipe_id=kwargs['recipe_pk'], ingredient_id=request.data['ingredient'], quantity=request.data['quantity']
)
return Response(recipe_ingredient.serialize(), status=status.HTTP_201_CREATED, headers={})

View File

@ -14,6 +14,9 @@ RECIPE_TYPE_SOUP = 'soup'
RECIPE_STATUS_ACTIVE = 'active' RECIPE_STATUS_ACTIVE = 'active'
RECIPE_STATUS_DELETED = 'deleted' RECIPE_STATUS_DELETED = 'deleted'
INGREDIENT_STATUS_ACTIVE = 'active'
INGREDIENT_STATUS_DELETED = 'deleted'
RECIPE_TYPE_CHOICE = [RECIPE_TYPE_MEAT, RECIPE_TYPE_VEGETABLE, RECIPE_TYPE_SOUP] RECIPE_TYPE_CHOICE = [RECIPE_TYPE_MEAT, RECIPE_TYPE_VEGETABLE, RECIPE_TYPE_SOUP]
FILTER_EXACT = ['exact'] FILTER_EXACT = ['exact']

View File

@ -10,7 +10,6 @@ reload(sys)
import utils.lark import utils.lark
import git import git
import ipdb
if __name__ == '__main__': if __name__ == '__main__':
# def notify(): # def notify():