This commit is contained in:
Arthur
2019-04-20 15:15:32 +02:00
parent d8ac11921f
commit 2e7553b70c
46 changed files with 1871 additions and 721 deletions

0
recipe_book/__init__.py Normal file
View File

13
recipe_book/admin.py Normal file
View File

@@ -0,0 +1,13 @@
from django.contrib import admin
from .models import Recipe,IngredientWithAmount, Ingredient
# Register your models here.
class IngredientWithAmountInline(admin.TabularInline):
model=IngredientWithAmount
class RecipeAdmin(admin.ModelAdmin):
inlines = [ IngredientWithAmountInline, ]
admin.site.register(Recipe, RecipeAdmin)
admin.site.register(Ingredient)

5
recipe_book/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class RecipeBookConfig(AppConfig):
name = 'recipe_book'

View File

@@ -0,0 +1,45 @@
# Generated by Django 2.2 on 2019-04-11 13:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Ingredient',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256)),
],
),
migrations.CreateModel(
name='IngredientWithAmount',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, max_digits=6)),
('unit', models.CharField(choices=[('gr', 'Grammes'), ('u', 'Units'), ('l', 'Litres')], max_length=2)),
('ingredients', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='recipe_book.Ingredient')),
],
),
migrations.CreateModel(
name='Recipe',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=512)),
('category', models.CharField(choices=[('0', 'Petit-déjeuner'), ('1', 'Entrée'), ('2', 'Plat'), ('3', 'Dessert')], max_length=2)),
('ingredients', models.ManyToManyField(through='recipe_book.IngredientWithAmount', to='recipe_book.Ingredient')),
],
),
migrations.AddField(
model_name='ingredientwithamount',
name='recipe',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='recipe_book.Recipe'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2019-04-11 13:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('recipe_book', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='ingredientwithamount',
old_name='ingredients',
new_name='ingredient',
),
]

View File

103
recipe_book/models.py Normal file
View File

@@ -0,0 +1,103 @@
from django.db import models
# Create your models here.
"""
Recipe's creation workflow :
1. Create recipe with required info, optional preparation
2. Create recipe's list of ingredients
a. Create new ingredients instances
b. Create IngredientWithAmount instances
3. Add preparation steps if necessary
"""
class Recipe(models.Model):
name = models.CharField(max_length=512)
category = models.CharField(
max_length=2,
choices=(
('0', 'Petit-déjeuner'),
('1', 'Entrée'),
('2', 'Plat'),
('3', 'Dessert'),
))
ingredients = models.ManyToManyField(
'Ingredient',
through='IngredientWithAmount'
)
def __str__(self):
return "{}".format(self.name)
class Ingredient(models.Model):
name = models.CharField(max_length=256)
def __str__(self):
return "{}".format(self.name)
class IngredientWithAmount(models.Model):
""" A recipe-ingredient relation associated with amounts
"""
recipe = models.ForeignKey(
'Recipe',
models.CASCADE)
ingredient = models.ForeignKey(
'Ingredient',
models.CASCADE )
amount = models.DecimalField(
max_digits=6,
decimal_places=2,
)
unit = models.CharField(
max_length=2,
choices=(
('gr', 'Grammes'),
('u', 'Units'),
('l', 'Litres'),
))
def __repr__(self):
return "<{}>".format(self.display())
def display(self):
""" Print info in a human friendly way.
* Correct grammar
* Apropriate units according to amount
"""
# Note: amount has a .is_integer() method :)
amount = self.amount
name = self.ingredient.name.lower()
particule = ""
unit = ""
if self.unit == 'u':
integer = int(amount)
fraction = amount - integer
# Print subfractions of one as ratio
if fraction: #TODO: if is not integer...
fraction = "{}/{}".format(
*amount.as_integer_ratio()
)
else:
fraction = ""
if integer:
# Plural
if integer >= 2:
name += "s"
integer = str(integer)
else:
integer = ""
amount = "{}{}".format(integer, fraction)
else:
particule = "de "
unit = self.unit
return "{}{} {}{}".format(
amount, unit, particule, name
)

View File

@@ -0,0 +1,56 @@
from rest_framework import serializers as ser
from .models import Recipe, Ingredient, IngredientWithAmount
class RecipeSerializer(ser.ModelSerializer):
class Meta:
model = Recipe
fields = ('id', 'name', 'category', 'ingredients')
class IngredientSerializer(ser.ModelSerializer):
class Meta:
model = Ingredient
fields = ('id', 'name')
class IngredientWithAmountSerializer(ser.ModelSerializer):
ingredient = IngredientSerializer()
class Meta:
model = IngredientWithAmount
fields = ('recipe', 'ingredient', 'amount', 'unit', 'display')
extra_kwargs = {
'recipe': { 'write_only': True },
}
def create(self, validated_data):
# TODO: better management of ingredient finding
ingredient, created = Ingredient.objects.get_or_create(
**validated_data.pop('ingredient')
)
if created:
ingredient.save()
validated_data['ingredient'] = ingredient
return IngredientWithAmount.objects.create(
**validated_data
)
def update(self, instance, validated_data):
for attr in ('recipe', 'amount', 'unit'):
setattr(
instance,
attr,
validated_data.get(attr,
getattr(instance, attr))
)
if 'ingredient' in validated_data:
ingdt, created = Ingredient.objects.get_or_create(
**validated_data['ingredient']
)
if created:
ingdt.save()
instance.ingredient = ingdt
instance.save()
return instance

3
recipe_book/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

33
recipe_book/views.py Normal file
View File

@@ -0,0 +1,33 @@
from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from recipe_book.serializers import (
RecipeSerializer,
IngredientSerializer,
IngredientWithAmountSerializer,
)
from recipe_book.models import (
Recipe,
Ingredient,
IngredientWithAmount,
)
class RecipesViewSet(viewsets.ModelViewSet):
queryset = Recipe.objects.all()
serializer_class = RecipeSerializer
class IngredientViewSet(viewsets.ModelViewSet):
queryset = Ingredient.objects.all()
serializer_class = IngredientSerializer
class IngredientWAmountViewSet(viewsets.ModelViewSet):
serializer_class = IngredientWithAmountSerializer
lookup_field = 'ingredient'
def get_queryset(self):
return IngredientWithAmount.objects.filter(
recipe=self.kwargs['recipe_pk']
)