Calorie Tracker App With Python Django Framework

Tanmoy Sarkar
Sep 28 · 9 min read
Image for post
Image for post
Photo by Brooke Lark on Unsplash

Maintaining a healthy lifestyle has been one of the main concerns of this century. According to WHO in 2016, more than 1.9 billion adults, 18 years and older, were overweight. Of these over 650 million were obese. With the covid-19 outbreak this year it has been proved that covid-19 is more fatal to people with obesity. So regular exercise and healthy diet is an important thing people are looking into. With this in mind, I thought how about I make a calorie tracker with python Django. I have been learning Django by myself for a while now so I thought it would be a good project to skill up my Django than following project tutorials. Enough about other things, lets get to the coding part. This might be a little hard for absolute django beginner, so basic knowledge of django will be helpful in understanding.

Requirements:

  • Python (I am using version 3.8.2)
  • Django(I am using version 3.1)
  • Django-filter

Project Setup:

I have done this project on windows operating system. If you are on Mac or Linux the process is almost the same with some command line changes, which you can easily find from the internet. Go to the location you prefer on your computer from the command line and start.

mkdir calorieapp
cd calorieapp

Now, lets set up django project. I will try to keep it short since you can easily find tutorials for setting up django project.

pipenv install django
pipenv shell
(calorieapp-kub3HwmJ) django-admin startproject calorie_app .(calorieapp-kub3HwmJ)python manage.py startapp calories

Now let's install django-filter package which we will need a bit later.

(calorieapp-kub3HwmJ) pip install django-filter

Now add the apps in settings.py file

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

'calories.apps.CaloriesConfig',
'django_filters'
]

Change time zone according to your location which you will find at bottom part of settings.py.

TIME_ZONE = 'Asia/Dacca'

Now to add templates folder for storing all our templates for the project also we need a static folder for storing static files like CSS/JS.

(calorieapp-kub3HwmJ) C:\Users\User\Desktop\calorieapp>mkdir templates
(calorieapp-kub3HwmJ) C:\Users\User\Desktop\calorieapp>mkdir static

A tweak is needed in settings.py file for letting django know to use our files that we made above. Change “DIRS” and add static file location at the bottom.

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS':[os.path.join(BASE_DIR,'templates')], #new
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] #new

Okay, our boring setup is out of the way lets get to the fun coding part.

Image for post
Image for post
Photo by Faisal M on Unsplash

Starting with calorie_app/urls.py file we will connect our “calories” app’s urls.

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

Models:

Time to go to calories/models.py file and set up the model for the project.

Here we have made three models. I am going to describe the functionality of each Model class.

Food:

This model is made for storing all the food items that the user wants in their profile. Each user is connected so that users can have food inventory of their own choice.

Profile:

This is the main model that stores all the required data of the user for tracking calories. We got fields like calorie_count for counting calories for each day, food_selected for the food that the user selects for consuming, calorie_goal for setting a goal for calorie consumption each day. Date field is given to keep track of calorie consumption each day. Last field is all_food_selected_today for storing all the foods consumed in a day. This field is connected to PostFood class by a through argument. I am going the explain the reason later in views.py section. Lastly, I performed some calculations before saving each Model instance by overriding save method in the model. Here each time a food is selected PostFood is created and connected to all_food_selected_today by a through field.

PostFood:

This model is used for a through connection with the all_food_selected_today field in Profile class. It will be used for keeping tracking of all food consumed by the user. More on this in our views.py section.

To make all the models visible and accessible in the admin dashboard we need to change calories/admin.py

from django.contrib import admin
from .models import Food,Profile,PostFood
# Register your models here.
class ProfileAdmin(admin.ModelAdmin):
readonly_fields = ('date',)
admin.site.register(Food)
admin.site.register(Profile)

Now we need to migrate our database. In command line

python manage.py makemigrations calories
python manage.py migrate

Django Signals:

Django signal is one of the cool feature django provides use for using in our project. I didn’t know about it before, after searching a problem I came across it in django docs. The thing is we are defining a profile model instance for each day so we also want to create a Profile instance automatically when a user is registered in the database. Here comes the power of django signals. First, let us make a file in calories folder for signals and name it signals.py. Now in calories/signals.py file the following code is written

from django.db.models.signals import post_save
from django.contrib.auth.models import User
from .models import Profile
def create_profile(sender,instance,created,**kwargs):
if created:
Profile.objects.create(person_of=instance)
print("profile created")
post_save.connect(create_profile,sender=User)

We import post_save, User and the Profile model above our code. In simple words, django signals do something/send signals when something is changed/created/deleted. In our case that something is our User model. We used post_save because we want to do something after the User is saved in the database. Here create_profile takes parameter of sender ,instance and created. Sender is the model which is sending the signal, instance is the condition of the model and created is to check if the model was updated or created first. Lastly, we connect your signal by giving arguments to post_save.connect method. There are other signals which you can find about it in official django doc. To make the signals work we need two-line added in calories/apps.py file

from django.apps import AppConfigclass CaloriesConfig(AppConfig):
name = 'calories'
def ready(self): #new
import calories.signals #new

Forms:

Forms are an important part of django. For this project we need 4 forms. All were made with the help of django forms. First make a file in calories folder naming it forms.py. In calories/forms.py file we write

I am going to brief these forms one by one.

  • CreateUserForm: This is used for registering user.
  • SelectFoodForm: This one is used for the user to select the food they consumed and wants to keep track of. One thing to watch is at the end __init__ is overridden. The reason? Well, the queryset of the foods for selecting must be the foods that the user added. So we filtered Food objects by Food.objects.filter(person_of=user).
  • AddFoodForm: It is a simple form for adding food items by the user. The fields taken are name, quantity and calorie.
  • ProfileForm: Another simple form for taking input from the user about their calorie goal for the day.

Filters:

In the beginning, if you remember we installed django-filter. Now its time to use it. For giving more accessibility to the user for searching foods added by them I decided to add filters. First, make a file name filters.py in calories folder. Then add the following code.

import django_filters
from django_filters import CharFilter
from .models import *
class FoodFilter(django_filters.FilterSet):
food_name = CharFilter(field_name = 'name',lookup_expr = 'icontains',label='search food items')
class Meta:
model = Food
fields = ['food_name']

Django filter gives us an easy option for filtering models. Here I filtered all the food items by name. If you want to learn more about django-filters you can learn from here.

Views:

Now for one of the important part of the projects. Our views that will be shown in template. Here’s our views.py

In the same way as forms, I am going to give a short brief for all the views. Another thing is I used django decorators for making all the pages available only to logged users.

  • HomePageView: This is for the view on the main page. We mentioned in models section that we will make one profile each day. So for getting the latest profile we need the last profile object. Then comes the logic of creating one profile each day. Today’s date was checked with the date of the last profile. If greater we create a new profile. Now comes an interesting part. When doing this project I got stuck at this problem. The problem was showing all the food selected by the user in one day. The thing is in django ManyToMany relationship if you select duplicate items then it shows duplicate items as only one item when querying. So the user consumed say like egg two times a day and insert it separately then it will show only one insertion. After much difficulty and searching the internet, stack-overflow(❤) for some time I come to know about django through argument in ManyToMany relationship. It saved the day. It is easily explained in the official django doc. You can check it out here. I made another model PostFood and connect it to all_food_selected_today field by through argument. Then I could easily query all the food items selected for a day. Lastly, there was some basic calculation about calories and passing it on to context.
  • RegisterPage: A simple view for registering new users.
  • LoginPage: Another simple view for the login page.
  • LogOutPage: View for logging out.
  • select_food: This view is used for food selection. Here during food selection, we want to show the user all the foods they registered. So we query out all the foods registered by the user. Then the form for selecting food was passed on.
  • add_food: This view is used for registering food by the user. Like select_food we also passed on all the food that are already registered and also the filter that we made earlier for filtering foods. Lastly, the AddFoodForm was passed.
  • update_food: This is for updating registered foods. Sometime the user may feel the need of updating food status like calorie, quantity.
  • delete_food: Along with update user may also feel the need of deleting certain food.
  • ProfilePage: Lastly the main profile page. It will show all the foods registered, take input the calorie goal for the day, records of calorie consumption for the last 7 days. For querying records for the last 7 days I used timedelta function. Using timedelta date for 1 week ago can be found. Then we filtered Profile objects by date_gte and date_lt.

So our views part is over. The last thing we need is templates for the views to show. This part I am not going to explain since you can design and use front-end as your likings. I used some simple HTML, CSS and bootstrap for designing. I am going to link the github repository at the end if you want to check it out.

Urls:

Now all we need is connect your views to appropriate url paths. We need a urls.py file for that, make the file in calories folder. Then in calories/urls.py file

from django.urls import pathfrom .views import HomePageView,LoginPage,LogOutPage,select_food,add_food,RegisterPage,ProfilePage,update_food,delete_foodurlpatterns = [
path('', HomePageView,name='home'),
path('login/',LoginPage,name='login'),
path('logout/',LogOutPage,name='logout'),
path('select_food/',select_food,name='select_food'),
path('add_food/',add_food,name='add_food'),
path('update_food/<str:pk>/',update_food,name='update_food'),
path('delete_food/<str:pk>/',delete_food,name='delete_food'),
path('register/',RegisterPage,name='register'),
path('profile/',ProfilePage,name='profile'),
]

To run our local server and test we just need to run

python manage.py runserver

Here are some images from the website.

Image for post
Image for post
Add Food Page
Image for post
Image for post
Select Food page
Image for post
Image for post
Home Page
Image for post
Image for post
Profile Page

Our project is complete. Hope I have helped someone to learn something new. If you have any suggestions do mention. Github repository of this project is given here.

Sign up for Best Stories

By Dev Genius

The best stories sent monthly to your email. Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Tanmoy Sarkar

Written by

Tech,coding,sports lover

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Tanmoy Sarkar

Written by

Tech,coding,sports lover

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store