Для каждого продукта питания необходимо хранить калорийность в пересчете на грамм, а для единиц измерения указывать вес в граммах:
Единицы измерения не имеют смысла безотносительно продуктов питания, т.к. между десятком яиц, столовой ложкой муки, и одним бананом среднего размера (200 гр с кожурой, 150 гр мякоти) нет ничего общего.
Пример реализации на Django:
models.pyimport decimal
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
class Foodstuff(models.Model):
name = models.CharField(max_length=200)
kcal_per_gram = models.DecimalField(
# the most calorie-dense food is fat cattle - 925 kcal per 100 gram (9.25 kcal/gram)
max_digits=3,
decimal_places=2,
validators=[
MaxValueValidator(decimal.Decimal('9.99')),
MinValueValidator(decimal.Decimal('0.00')),
],
)
@property
def kcal_per_100_gram(self):
return int(self.kcal_per_gram * 100)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
class FoodstuffUnit(models.Model):
foodstuff = models.ForeignKey(
Foodstuff,
models.CASCADE,
)
name = models.CharField(max_length=200)
net_weight = models.DecimalField(
max_digits=6,
decimal_places=2,
validators=[
MinValueValidator(decimal.Decimal('0.01')),
],
)
@property
def kcal(self):
return int(self.foodstuff.kcal_per_gram * self.net_weight)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
unique_together = ('foodstuff', 'name')
class Dish(models.Model):
name = models.CharField(max_length=200)
@property
def kcal(self):
return sum(i.kcal for i in self.dishingredient_set.all())
def __str__(self):
return self.name
class Meta:
ordering = ['name']
verbose_name_plural = 'dishes'
class DishIngredient(models.Model):
dish = models.ForeignKey(
Dish,
models.CASCADE,
)
foodstuff = models.ForeignKey(
Foodstuff,
models.PROTECT,
)
foodstuff_unit = models.ForeignKey(
FoodstuffUnit,
models.PROTECT,
blank=True,
null=True,
)
amount = models.DecimalField(
max_digits=6,
decimal_places=2,
validators=[
MinValueValidator(decimal.Decimal('0.00')),
],
)
@property
def kcal(self):
weight = self.amount
if self.foodstuff_unit is not None:
weight *= self.foodstuff_unit.net_weight
return int(self.foodstuff.kcal_per_gram * weight)
def __str__(self):
unit = 'гр' if self.foodstuff_unit is None else f'{self.foodstuff_unit}'
return f'{self.dish}: {self.foodstuff}, {unit} - {self.amount}'
class Meta:
ordering = ['id']
admin.pyfrom django.contrib import admin
from . import models as app_models
class FoodstuffUnitInline(admin.TabularInline):
model = app_models.FoodstuffUnit
extra = 0
readonly_fields = (
'kcal',
)
@admin.register(app_models.Foodstuff)
class FoodstuffAdmin(admin.ModelAdmin):
list_display = (
'name',
'kcal_per_100_gram',
)
inlines = (
FoodstuffUnitInline,
)
readonly_fields = (
'kcal_per_100_gram',
)
class DishIngredientInline(admin.TabularInline):
model = app_models.DishIngredient
extra = 0
readonly_fields = (
'kcal',
)
@admin.register(app_models.Dish)
class DishAdmin(admin.ModelAdmin):
list_display = (
'name',
'kcal',
)
inlines = (
DishIngredientInline,
)
readonly_fields = (
'kcal',
)