Python Project – Create a Calorie Calculator in Django

What is a Calorie Calculator?

"Health is wealth" Obviously, you have heard it a thousand times but as we grow older we often realize it’s true and this app provides an interesting way to get started with the very boring term “dieting” as we already know "FITNESS START WITH WHAT WE EAT".

Let’s develop an interesting project – Calorie Calculator to record and estimate number of calories we need to consume daily. This app can also provide guidelines for gaining or losing weight.

Calorie Calculator in Python – Django Project

This exciting python project will be developed using a python Django. Django framework provides inbuilt libraries for web development. We can develop any kind of web app using Django.

Project Prerequisites

To implement this app we will use :

  1. Basic concepts of Python
  2. HTML
  3. CSS
  4. Bootstrap
  5. Django framework

To install required libraries, use pip installer from the command line: (Django filters are used and explained later)

1. pip install django
2. pip install django-filters

Final product

Below is the final GUI of Calorie Calculator Project

Steps to Build a Python Project – Calorie calculator

Now we will make a new project CalorieCalc and an app inside it named 'Fityfeed'

1. django-admin startproject CalorieClac
2. cd  CalorieClac
3. django-admin startapp Fityfeed

1. Writing Models: models.py

To access databases, we will need the model.

In Fityfeed/models.py, create these models.

Code:

	from django.db import models
	from django.contrib.auth.models import User
	# Create your models here.
	class Customer(models.Model):
	    user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
	    name=models.CharField(max_length=200,null=True)
	    email=models.CharField(max_length=200,null=True)
	    date_created=models.DateTimeField(auto_now_add=True,null=True)
    
	    def __str__(self):
	        return str(self.name)

	class Category(models.Model):
	    options=(
	        ('breakfast','breakfast'),
	        ('lunch','lunch'),
	        ('dinner','dinner'),
	        ('snacks','snacks'),
	    )
	    name=models.CharField(max_length=50,choices=options)
	    def __str__(self):
	        return self.name

	class Fooditem(models.Model):
	    name = models.CharField(max_length=200)
	    category = models.ManyToManyField(Category)
	    carbohydrate = models.DecimalField(max_digits=5,decimal_places=2,default=0)
	    fats = models.DecimalField(max_digits=5,decimal_places=2,default=0)
	    protein = models.DecimalField(max_digits=5,decimal_places=2,default=0)
	    calorie=models.DecimalField(max_digits=5,decimal_places=2,default=0,blank=True)
	    quantity = models.IntegerField(default=1,null=True,blank=True)
	    
	    def __str__(self):
	        return str(self.name)
	        
	#for user page-------------------------------------------------------------
	class UserFooditem(models.Model):
	    customer = models.ManyToManyField(Customer ,blank=True)
	    fooditem=models.ManyToManyField(Fooditem)

Our models will store:

  1. Customers
  2. All available food items
  3. Category of food items we offer
  4. Consumed food items

We are using simple model fields for that purpose. The __str__() method will return the string representation of the object. These are simple Django concepts.

2. Authentication: decorators.py

Code:

	from django.http import HttpResponse
	from django.shortcuts import redirect

	def unauthorized_user(view_func): 
	    def wrapper_func(request,*args,**kwargs):
	        if request.user.is_authenticated:
	            return redirect('home')
	        else:
	            return view_func(request,*args,**kwargs)
	    return wrapper_func

	def allowed_users(allowed_roles=[]):
	    def decorator(view_func): 
	        def wrapper_func(request,*args,**kwargs):
	            group=None
	            if request.user.groups.exists():
	                group=request.user.groups.all()[0].name
	            if group in allowed_roles:
	                return view_func(request,*args,**kwargs)
	            else:
	                return HttpResponse("<p>You are not allowed to access this page</p>")
	        return wrapper_func
	    return decorator

	def admin_only(view_func):
	    def wrapper_func(request,*args,**kwargs):
	        group=None
	        if request.user.groups.exists():
	            group=request.user.groups.all()[0].name
	        if group=='user':
	            return redirect('userPage')
	        if group=='admin':
	            return view_func(request,*args,**kwargs)
	    return wrapper_func

This will take care of all access privileges so that the customer and the admin cannot access each other’s data. Also, it will make sure that the logged-in user can’t go to the login/register page, unauthorized users can’t access any page except login and registration page.

3. Search feature: filers.py

Code:

	import django_filters
	from .models import *
	
	class fooditemFilter(django_filters.FilterSet):
	    class Meta:
	        model = Fooditem
	        fields = ['name']

This includes a small code which enables search filter for the user.

4. Creating and updating models: forms.py

Code: 

	from django.forms import ModelForm
	from .models import *
	from django.contrib.auth.forms import UserCreationForm
	
	class fooditemForm(ModelForm):
	    class Meta:
	        model=Fooditem
	        fields="__all__"
	class addUserFooditem(ModelForm):
	    class Meta:
	        model=UserFooditem
	        fields="__all__"
	        
	class createUserForm(UserCreationForm):
	    class Meta:
	        model=User
	        fields=['username','email','password1','password2']

Above mentioned code is required to create, update models which we have created earlier in models.py (for CRUD functionality)

5. To create/update models from django admin site: admin.py

Code:

	from django.contrib import admin
	from .models import *
	
	# Now, Register the models here.

	class foodAdmin(admin.ModelAdmin):
	    class Meta:
	        model=Fooditem
	    list_display=['name']
	    list_filter=['name']

	admin.site.register(Customer)
	admin.site.register(UserFooditem)
	admin.site.register(Category)
	admin.site.register(Fooditem,foodAdmin)

It will register all our models to admin site, Now we can easily create/update on following UI: http://127.0.0.1:8000/admin/

NOTE: Create a superuser and apply all migrations:

1. py manage.py makemigrations
2. py manage.py migrate
3. py manage.py createsuperuser

It will ask for your username, email, password.

And then boom! You are more than halfway towards reaching your goal.

6. Let’s work with urls.py

First, we need to make a new file urls.py in our app and include it in ‘CalorieCalc/urls.py’ so that it finally contains

	from django.contrib import admin
	from django.urls import path, include
	
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('',include('Fityfeed.urls')),
	]

Now let’s work with Fityfeed/urls.py

Code:

	from django.urls import path,include
	from . import views
	from django.contrib.auth import views as auth_views

	urlpatterns = [
	    path('',views.home,name='home'),
	    path('user/',views.userPage,name='userPage'), #userPage
	    path('product/',views.fooditem,name='fooditem'),
	    path('createfooditem/',views.createfooditem,name='createfooditem'),
	    path('register/',views.registerPage,name='register'),
	    path('login/',views.loginPage,name='login'),
	    path('logout/',views.logoutUser,name='logout'),
	    path('addFooditem/',views.addFooditem,name='addFooditem'),

	    path('reset_password/',
	         auth_views.PasswordResetView.as_view(),
	         name="reset_password"),

	    path('reset_password_sent/',
	         auth_views.PasswordResetDoneView.as_view(),
	         name="password_reset_done"),

	    path('reset///',
	         auth_views.PasswordResetConfirmView.as_view(),
	         name="password_reset_confirm"),
	         
	    path('reset_password_complete/',
	         auth_views.PasswordResetCompleteView.as_view(),
	         name="password_reset_complete"),
	]

It contains a lot more than we’ve discussed right. No worries, we’ll get it as soon as we step further.

7. Let’s finally set our ‘views.py’

Code: 

	from django.shortcuts import render,redirect
	from django.http import HttpResponse
	from django.contrib.auth.models import User
	from .models import *
	from .forms import *
	from django.contrib import messages
	from django.contrib.auth import authenticate,login,logout
	from django.contrib.auth.decorators import login_required
	from .decorators import *
	from django.contrib.auth.models import Group
	from .filters import fooditemFilter
	# Create your views here.

	@login_required(login_url='login')
	@admin_only
	def home(request):
	    breakfast=Category.objects.filter(name='breakfast')[0].fooditem_set.all()[:5]
	    lunch=Category.objects.filter(name='lunch')[0].fooditem_set.all()[:5]
	    dinner=Category.objects.filter(name='dinner')[0].fooditem_set.all()[:5]
	    snacks=Category.objects.filter(name='snacks')[0].fooditem_set.all()[:5]
	    customers=Customer.objects.all()
	    context={'breakfast':breakfast,
	              'lunch':lunch,
	              'dinner':dinner,
	              'snacks':snacks,
	              'customers':customers,
	            }
	    return render(request,'main.html',context)

	@login_required(login_url='login')
	@allowed_users(allowed_roles=['admin'])
	def fooditem(request):
	    breakfast=Category.objects.filter(name='breakfast')[0].fooditem_set.all()
	    bcnt=breakfast.count()
	    lunch=Category.objects.filter(name='lunch')[0].fooditem_set.all()
	    lcnt=lunch.count()
	    dinner=Category.objects.filter(name='dinner')[0].fooditem_set.all()
	    dcnt=dinner.count()
	    snacks=Category.objects.filter(name='snacks')[0].fooditem_set.all()
	    scnt=snacks.count()
	    context={'breakfast':breakfast,
	              'bcnt':bcnt,
	              'lcnt':lcnt,
	              'scnt':scnt,
	              'dcnt':dcnt,
	              'lunch':lunch,
	              'dinner':dinner,
	              'snacks':snacks,
	            }
	    return render(request,'fooditem.html',context)

	@login_required(login_url='login')
	@allowed_users(allowed_roles=['admin'])
	def createfooditem(request):
	    form = fooditemForm()
	    if request.method == 'POST':
	        form = fooditemForm(request.POST)
	        if form.is_valid():
	            form.save()
	            return redirect('/')
	    context={'form':form}
	    return render(request,'createfooditem.html',context)

	@unauthorized_user
	def registerPage(request):
	    form=createUserForm()
	    if request.method=='POST':
	        form=createUserForm(request.POST)
	        if form.is_valid():
	            user=form.save()
	            username=form.cleaned_data.get('username')
	            group=Group.objects.get(name='user')
	            user.groups.add(group)
	            email=form.cleaned_data.get('email')
	            Customer.objects.create(user=user, name=username,email=email)
	            messages.success(request,'Account created for '+username)
	            return redirect('login')
	    context={'form':form}
	    return render(request,'register.html',context)

	@unauthorized_user
	def loginPage(request):
	    if request.method=='POST':
	        username=request.POST.get('username')
	        password=request.POST.get('password')
	        user=authenticate(request,username=username,password=password)
	        if user is not None:
	            login(request,user)
	            return redirect('home')
	        else:
	            messages.info(request,'username or password is invalid')
	    return render(request,'login.html')

	@login_required(login_url='login')
	def logoutUser(request):
	    logout(request)
	    return redirect('login')

	def userPage(request):
	    user=request.user
	    cust=user.customer
	    fooditems=Fooditem.objects.filter()
	    myfilter = fooditemFilter(request.GET,queryset=fooditems)
	    fooditems=myfilter.qs
	    total=UserFooditem.objects.all()
	    myfooditems=total.filter(customer=cust)
	    cnt=myfooditems.count()
	    querysetFood=[]
	    for food in myfooditems:
	        querysetFood.append(food.fooditem.all())
	    finalFoodItems=[]
	    for items in querysetFood:
	        for food_items in items:
	            finalFoodItems.append(food_items)
	    totalCalories=0
	    for foods in finalFoodItems:
	        totalCalories+=foods.calorie
	    CalorieLeft=2000-totalCalories
	    context={'CalorieLeft':CalorieLeft,'totalCalories':totalCalories,'cnt':cnt,'foodlist':finalFoodItems,'fooditem':fooditems,'myfilter':myfilter}
	    return render(request,'user.html',context)
	    
	def addFooditem(request):
	    user=request.user
	    cust=user.customer
	    if request.method=="POST":
	        form =addUserFooditem(request.POST)
	        if form.is_valid():
	            form.save()
	            return redirect('/')
	    form=addUserFooditem()
	    context={'form':form}
	    return render(request,'addUserFooditem.html',context)

Registration Page:

Login Page

User Page:

Admin Page:

8. Let’s do some styling

We need to make a new static folder in the outer project folder. For this, we need to customize our setting.py and append the following

Code:

	STATIC_URL = '/static/'
	STATICFILES_DIRS = [
	    os.path.join(BASE_DIR,'static'),
	]

We need to use this line ‘{% load static %}’ in our templates and now make a new file main.css in our static folder:

Code:

	.flex-box-row {
	    display:flex;
	    padding : 10px;
	    flex-wrap: wrap;
	    justify-content: space-around;
	}
	#heading{
	    text-shadow: antiquewhite;
	    font-family: 'Courier New', Courier, monospace;
	    font-weight: 550;
	    font-size: xx-large;
	    text-align: left;
	}

9. Now the last step, develop templates:

This you can easily get from source code as there are many files so pasting it here would make it bigger.

10. Forget password functionality:

Include this in your setting.py

Code:

	1. EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
	2. EMAIL_HOST='smtp.gmail.com'
	3. EMAIL_PORT=587
	4. EMAIL_USE_TLS=True
	5. EMAIL_HOST_USER='***'
	6. EMAIL_HOST_PASSWORD='***'

Do not forget to include your email and password here.

11. Output Screens

Summary

We have successfully developed the calorie calculator python project in Django and now we can use it every day to keep track of what we eat and how much we should eat.