xyhlon
xyhlon

Reputation: 95

Access a field of a through Model of an ManyToManyField before saving

I am trying to sum the cost of the ingredients of a recipe to have the total cost of a recipe. However when I try to access the cost of "ingredients" or "Ingredient.objects.filter(recipe=self)" these objects aren't created even after a manual save to super. I did however notice if I save it through the Django Admin twice the "Ingredient" objects are populated and the cost field is available. So how can I sum up the total cost of recipe cause my way seems not to work properly? Thanks in advance.

class Material(models.Model):
UNITS = [
    ('kg', 'kg'),
    ('g', 'gramm'),
    ('l', 'liter'),
    ('stk', 'Stuck'),
    ('dkg', 'deka'),
]
name = models.CharField(max_length=200)
unit = models.CharField(max_length=20, choices=UNITS, default="kg", verbose_name="Einheit")
lieferant = models.ForeignKey(Lieferant,default=1, verbose_name="Lieferant",on_delete=models.CASCADE, null=True)
costpunit = models.FloatField(verbose_name="Stuckpreis")
menge = models.FloatField(default=1,verbose_name="Menge")
costpkg = models.FloatField(editable=False, verbose_name="Kilopreis") 

class Meta:
    verbose_name_plural = "Materialien"

def __str__(self):
    return self.name

def save(self):
    if self.unit and (self.unit is not "stk"):
        kilogramms = convert_SI(self.menge, self.unit, "kg")


    self.costpkg = self.costpunit/kilogramms
    super(Material, self).save()


class Recipe(models.Model):
name = models.CharField(max_length=200)
cost = models.FloatField(default=1, editable=False, verbose_name="Kosten")
ingredients = models.ManyToManyField(Material, through='Ingredient') 

def __str__(self):
    return self.name 

def save(self):
    x = 0
    super(Recipe, self).save()
    for ing in Ingredient.objects.filter(recipe=self):
        x += ing.cost
    self.cost = x  
    super(Recipe, self).save()


class Ingredient(models.Model):
UNITS = [
    ('kg', 'kg'),
    ('g', 'gramm'),
    ('l', 'liter'),
    ('stk', 'Stuck'),
    ('dkg', 'deka'),
]
material = models.ForeignKey(Material, default=1, verbose_name="Material", on_delete=models.CASCADE, null=True)
recipe = models.ForeignKey(Recipe, default=1, verbose_name="Recipe", on_delete=models.CASCADE, null=True)
menge = models.FloatField(verbose_name="Menge")
unit = models.CharField(max_length=20, choices=UNITS, default="kilo", verbose_name="Einheit")
cost = models.FloatField(default=1, editable=False, verbose_name="Kosten")

def __str__(self):
    return self.recipe.name + ": " + self.material.name 

def save(self):
    if self.unit and (self.unit is not "stk"):
        kilogramms = convert_SI(self.menge, self.unit, "kg")


    self.cost = kilogramms*self.material.costpkg
    super(Ingredient, self).save()

Upvotes: 0

Views: 34

Answers (1)

xyhlon
xyhlon

Reputation: 95

After some further debugging I found the actual problem it was the way django saves the recipe in admin it first saves the recipe and then the ingredients so the solution was to edit the related_save method of admin.ModelAdmin for recipe and it looks something like this:

class RecipeAdmin(admin.ModelAdmin):
    inlines = (IngredientInlineAdmin,)
    fields = ('name', 'cost')
    readonly_fields = ["cost"]


    def save_related(self, request, form, formsets, change):
       super(RecipeAdmin, self).save_related(request, form, formsets, change)
       # get the recipe from the form
       recip = form.instance
       recip.cost = 0
       # iterate through the saved ingredients
       for ingredient in Ingredient.objects.filter(recipe=recip):
           # populate the cost field of the recipe
           recip.cost += ingredient.cost
       print(recip.cost)
       # and save the recipe again after the ingredients were saved
       recip.save()

Upvotes: 1

Related Questions