pythondjangoattributesmany-to-manyrelationship

how can i make many to many relationship between my two models successfully in my project in django?


After I made a project by Flask and SQLite, I want to make it by Django. I succeeded in adding foods to certain date.

And calculates the total calories but I fail to show the days with total calories in the home page. I show only the days that I added to my model but can not show its detail from total protein and fats and calories so I want a solution in create_date function in my view.

Here are my project details

First my model

from django.db import models
from datetime import datetime

# Create your models here.
class Food(models.Model):
    name=models.CharField(max_length=512,unique=True)
    protein=models.IntegerField(null=False)
    carbohydrates=models.IntegerField(null=False)
    fats=models.IntegerField(null=False)
    calories=models.IntegerField(null=True)
   
    def total_calories_food(self):
        total=self.protein * 4 + self.carbohydrates * 4 + self.fats * 9
        self.calories=total
        self.save()

    def __str__(self):
        return f'{self.name}'


class Dates(models.Model):
    food=models.ManyToManyField(Food,through='DatesFoods')
    date=models.DateField()


class DatesFoods(models.Model):
    date_id=models.ForeignKey(Dates,on_delete=models.CASCADE)
    food_id=models.ForeignKey(Food,on_delete=models.CASCADE)

Second my forms:

from django import forms
from .models import Food, Dates


class Food_Form(forms.ModelForm):
    class Meta():
        model = Food
        fields = ('name', 'protein', 'carbohydrates', 'fats')


class Dates_Form(forms.ModelForm):
    class Meta():
        model = Dates
        fields = ('date',)

And finally my views

def create_date(request):
    form=Dates_Form(request.POST)
    dates = Dates.objects.all()
    if form.is_valid():
        form.save()
    
    return render(request,'foodlist/home.html',context={'form':form,'dates':dates,
                                                              })
def add_food(request):
    form=Food_Form(request.POST)
    if form.is_valid():
        form.save()
    
    foods=Food.objects.all()
    for d in foods:
        d.total_calories_food()

    return render(request,'foodlist/add_food.html',context={'form':form,'foods':foods})


def view_foods_day(request,date):
    d=get_object_or_404(Dates,date=date)
    print(d)
    foods=Food.objects.all()
    foods_day=DatesFoods.objects.filter(date_id=d.id)
    protein=[]
    fats=[]
    carbohydrates=[]
    calories=[]

    for i in foods_day:
        protein.append(i.food_id.protein)
        fats.append(i.food_id.fats)
        carbohydrates.append(i.food_id.carbohydrates)
        calories.append(i.food_id.calories)

    if request.method=='POST':
        food_w=request.POST.get('food-select')
        f=Food.objects.get(pk=food_w)
        DatesFoods.objects.create(date_id=d,food_id=f)

    return render(request,'foodlist/day.html', context={'foods':foods,'date':d.date,
                                                        'protein':sum(protein),
                                                        'fats':sum(fats),
                                                        'carbohydrates':sum(carbohydrates),
                                                        'calories':sum(calories),
                                                        'foods_day':foods_day})

I try to show all days with its details from protein, fats, and carbohydrates in grams with total calories.

I succeeded in showing the details for specific days but not for all days.


Solution

  • Here's a solution that involves updating the view to pass the total nutritional details.

    def create_date(request):
        form = Dates_Form(request.POST or None)
    
        if form.is_valid():
            form.save()
    
        dates_with_totals = []
        dates = Dates.objects.all()
    
        for date in dates:
            foods_day = DatesFoods.objects.filter(date_id=date.id)
            total_protein = foods_day.aggregate(Sum('food_id__protein'))['food_id__protein__sum'] or 0
            total_fats = foods_day.aggregate(Sum('food_id__fats'))['food_id__fats__sum'] or 0
            total_carbohydrates = foods_day.aggregate(Sum('food_id__carbohydrates'))['food_id__carbohydrates__sum'] or 0
            total_calories = foods_day.aggregate(Sum('food_id__calories'))['food_id__calories__sum'] or 0
    
            dates_with_totals.append({
                'date': date,
                'total_protein': total_protein,
                'total_fats': total_fats,
                'total_carbohydrates': total_carbohydrates,
                'total_calories': total_calories
            })
    
        return render(request, 'foodlist/home.html', context={
            'form': form,
            'dates_with_totals': dates_with_totals,
        })
    

    I hope this will help you a little.