I’m trying to create view for my Food Tracking App where I can first pick a given date and then add items to the FoodLog object, however while viewing macros work after picking date, I can’t really add any new other objects becuase of the following error:
django.db.utils.IntegrityError: NOT NULL constraint failed: accounts_foodlogfooditem.food_log_id
[23/Sep/2023 13:56:08] “POST /accounts/food-log/ HTTP/1.1” 500 153768
I want to make it so that first I’ll be able to pick a date and then either show what’s in the FoodLog or be able to add new objects to it.
My View:
def food_log(request):
food_log = None
total_macros = {}
if request.method == 'POST':
date_form = DateForm(request.POST)
food_item_form = FoodLogFoodItemForm(request.POST)
if date_form.is_valid():
selected_date = date_form.cleaned_data['date']
food_log, created = FoodLog.objects.get_or_create(user=request.user, date=selected_date)
total_macros = food_log.calculate_total_macros_log()
if food_item_form.is_valid():
food_log_food_item = food_item_form.save(commit=False)
food_log_food_item.food_log = food_log
food_log_food_item.save()
else:
today = timezone.now().date()
food_log = FoodLog.objects.filter(user=request.user, date=today).first()
date_form = DateForm(initial={'date': today})
if food_log:
total_macros = food_log.calculate_total_macros_log()
food_item_form = FoodLogFoodItemForm()
return render(request, 'food_log.html',
{'food_log': food_log, 'date_form': date_form, 'food_item_form': food_item_form,
'total_macros': total_macros})
Models I have:
class FoodLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
foods = models.ManyToManyField(FoodItem, blank=True, through="FoodLogFoodItem")
meals = models.ManyToManyField(Meal, blank=True, through="FoodLogMeal")
date = models.DateField()
def calculate_total_macros_log(self):
log_total_kcal = 0
log_total_carbohydrates = 0
log_total_fats = 0
log_total_proteins = 0
# Calories and macronutrients from FoodLogFoodItem - divided by 100 as FoodItems have values per 100g
for food_log_food_item in self.foodlogfooditem_set.all():
food_item = food_log_food_item.food_item
quantity = food_log_food_item.quantity
log_total_kcal += food_item.kcal * quantity/100
log_total_carbohydrates += food_item.carbohydrates * quantity/100
log_total_fats += food_item.fats * quantity/100
log_total_proteins += food_item.proteins * quantity/100
# Calories and macronutrients from FoodLogMeal - divided by 100 as Meals have values per 100g
for food_log_meal in self.foodlogmeal_set.all():
meal = food_log_meal.meal
meal_macros = meal.calculate_total_macros_meal()
quantity = food_log_meal.quantity
log_total_kcal += meal_macros['total_kcal_per_100g'] * quantity/100
log_total_carbohydrates += meal_macros['total_carbohydrates_per_100g'] * quantity/100
log_total_fats += meal_macros['total_fats_per_100g'] * quantity/100
log_total_proteins += meal_macros['total_proteins_per_100g'] * quantity/100
return {
'log_total_kcal': log_total_kcal,
'log_total_carbohydrates': log_total_carbohydrates,
'log_total_fats': log_total_fats,
'log_total_proteins': log_total_proteins,
}
def __str__(self):
return f"{self.user.username}'s Food Log - {self.date}"
class FoodLogFoodItem(models.Model):
food_log = models.ForeignKey(FoodLog, on_delete=models.CASCADE)
food_item = models.ForeignKey(FoodItem, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
class FoodLogMeal(models.Model):
food_log = models.ForeignKey(FoodLog, on_delete=models.CASCADE)
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
Forms:
class DateForm(forms.Form):
date = forms.DateField(widget=forms.DateInput(attrs={'type': 'date'}))
def __init__(self, *args, **kwargs):
initial_date = kwargs.pop('initial_date', None)
super().__init__(*args, **kwargs)
if initial_date:
self.fields['date'].initial = initial_date
class FoodLogFoodItemForm(forms.ModelForm):
class Meta:
model = FoodLogFoodItem
fields = ['food_item', 'quantity']
class FoodLogMealForm(forms.ModelForm):
class Meta:
model = FoodLogMeal
fields = ['meal', 'quantity']