Reputation: 64
I've got some models which relations like this:
class Conversion(models.Model):
class Unit(models.IntegerChoices):
g = 10, 'gramy',
ml = 20, 'mililitry',
qty = 30, 'sztuki'
name = models.CharField(max_length=128)
g = models.FloatField()
ml = models.FloatField(null=True, blank=True)
qty = models.FloatField(null=True, blank=True)
default = models.IntegerField(choices=Unit.choices, default=Unit.g)
class Ingredient(models.Model):
class Unit(models.IntegerChoices):
g = 10, 'gramy',
dkg = 11, 'dekagramy',
kg = 12, 'kilogramy',
ml = 20, 'mililitry',
l = 21, 'litry',
qty = 30, 'sztuki'
Conversion = models.ForeignKey(Conversion, on_delete=models.CASCADE, related_name='ingredients')
amount = models.FloatField()
unit = models.IntegerField(choices=Unit.choices, default=Unit.g)
class Step(models.Model):
body = models.JSONField()
Ingredients = models.ManyToManyField(Ingredient, related_name='steps')
class Recipe(models.Model):
title = models.CharField(max_length=128)
teaser = models.CharField(max_length=256)
Steps = models.ManyToManyField(Step, blank=True, related_name='recipes')
Say there's a conversion for flour where 1ml == .53g, and the steps are separated because of how they are displayed so I thought it would be best to put it all into separate models.
Now if a user tells me he has some amount(Ingredients.amount) of ingredients(Conversion) I want to find all recipes that have equal or less of these.
for example user chooses 3 ingresients: |conversion.pk|amount| |-------------|------| |1 |200 | |2 |300 | |3 |1000 |
If a recipe has steps.conv.pk 1 and 2 with right amount but doesn't have the 3rd one, I want to find it too.
Upvotes: 0
Views: 276
Reputation: 1646
If I've got this understood correctly, and I might not, I think you are adding ingredients to each step. The ingredients have a quantity and unit and a Conversion which stores the ingredient name with a base amount and unit. Where I'm getting a little off on the understanding is knowing the difference between Ingredient.amount, Conversion.qty. Is the Conversion.g and Conversion.ml the conversion factor from Conversion.default to that unit?
In any case, I think you want to move steps out of Recipe as a many to many field, and put a foreign key in Step to Recipe. From a stylistic recommendation, I'd also suggest naming all your fields with lowercase (so Step.Ingredients becomes Step.ingredients) as it will make life simpler later on always knowing if you are looking at the class or the property.
I'm also not convinced you want ingredients in Step as a many to many as your quantities are associated with Ingredients. This means if you have a recipe pointing to 1 cup of Flour, and you later have another recipe using that same record, but decide to change it to 3/4 cups of flour, your original recipe will pick up that change. I believe you want Ingredient to have a foreign key to step (but I didn't make that change below).
Here is what I'd suggest for a starting point (but think Ingredient should not be many-to-many of step).
from django.db import models
class Conversion(models.Model):
class Unit(models.IntegerChoices):
g = 10, 'gramy',
ml = 20, 'mililitry',
qty = 30, 'sztuki'
name = models.CharField(max_length=128)
g = models.FloatField()
ml = models.FloatField(null=True, blank=True)
qty = models.FloatField(null=True, blank=True)
default = models.IntegerField(choices=Unit.choices, default=Unit.g)
def __str__(self):
return self.name
class Ingredient(models.Model):
class Unit(models.IntegerChoices):
g = 10, 'gramy',
dkg = 11, 'dekagramy',
kg = 12, 'kilogramy',
ml = 20, 'mililitry',
l = 21, 'litry',
qty = 30, 'sztuki'
conversion = models.ForeignKey(Conversion, on_delete=models.CASCADE, related_name='ingredients')
amount = models.FloatField()
unit = models.IntegerField(choices=Unit.choices, default=Unit.g)
def __str__(self):
return f"{self.amount} {self.conversion} [{self.Unit(self.unit).label}] ({self.unit})"
class Recipe(models.Model):
title = models.CharField(max_length=128)
teaser = models.CharField(max_length=256)
def __str__(self):
return self.title
class Step(models.Model):
body = models.TextField()
recipe = models.ForeignKey(Recipe, blank=True, on_delete=models.CASCADE)
ingredients = models.ManyToManyField(Ingredient, related_name='steps')
def __str__(self):
return self.body[:50]
Some admin.py just for the fun of it.
from django.contrib import admin
from recipe_15285278.models import Recipe, Ingredient, Step, Conversion
@admin.register(Step)
class StepAdmin(admin.ModelAdmin):
pass
class StepInline(admin.TabularInline):
model = Step
@admin.register(Recipe)
class RecipeAdmin(admin.ModelAdmin):
pass
inlines = [StepInline, ]
@admin.register(Ingredient)
class IngredientAdmin(admin.ModelAdmin):
pass
@admin.register(Conversion)
class ConversionAdmin(admin.ModelAdmin):
pass
I'm not convinced this is the right architecture for what you are trying to build but if it is, one can start here to build out the queries. I've changed the step to TextField
as I'm using sqllite which doesn't support the JSONField
.
Upvotes: 1