DEV Community

Cover image for How to Build a Daily Nutrition Check-in Feature in Django REST Framework
ugbotu eferhire
ugbotu eferhire

Posted on

How to Build a Daily Nutrition Check-in Feature in Django REST Framework

If you’re building a nutrition app, one of the most useful features you can add is a daily nutrition check-in. It gives users a simple way to log whether they followed their meal plan, drank enough water, and met their calorie target.

In this tutorial, I’ll show you how to build a Daily Nutrition Check-in feature in Django REST Framework. We’ll also connect it to a streak system so the app can reward consistency.

What we’ll build

By the end of this guide, you’ll have:

  • a DailyNutritionCheckIn model
  • a serializer for the API
  • an authenticated API endpoint
  • streak tracking with UserStreak
  • a clean route users can call from the frontend

Prerequisites

Before you begin, make sure you already have:

  • Django installed
  • Django REST Framework installed
  • a Django project with authentication set up
  • a nutrition app where you can add models and views

Step 1: Create the model

We need a model that stores one nutrition check-in per user per day.

Add this to your models.py:

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

class DailyNutritionCheckIn(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='nutrition_checkins')
    log_date = models.DateField(default=timezone.localdate)
    breakfast_completed = models.BooleanField(default=False)
    lunch_completed = models.BooleanField(default=False)
    dinner_completed = models.BooleanField(default=False)
    water_intake_ml = models.PositiveIntegerField(default=0)
    met_calorie_target = models.BooleanField(default=False)
    notes = models.TextField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('user', 'log_date')
        ordering = ['-log_date']

    def __str__(self):
        return f"{self.user.username} - {self.log_date}"
Enter fullscreen mode Exit fullscreen mode

Why this model works

The most important part here is:

unique_together = ('user', 'log_date')
Enter fullscreen mode Exit fullscreen mode

That ensures each user can only have one check-in per day. It prevents duplicate entries and keeps your data clean.

Step 2: Add a streak model

If your app already has streak tracking, you can reuse it. If not, add this model:

class UserStreak(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='streak')
    current_streak = models.PositiveIntegerField(default=1)
    longest_streak = models.PositiveIntegerField(default=1)
    last_logged_date = models.DateField(default=timezone.localdate)

    def __str__(self):
        return f"{self.user.username} - {self.current_streak} Day Streak"
Enter fullscreen mode Exit fullscreen mode

Why this is useful

This lets your app track:

  • the user’s current streak
  • their longest streak
  • the date they last completed a check-in

That means the app can motivate users to stay consistent.

Step 3: Create the serializer

Now we need to expose the model through the API.

Add this to serializers.py:

from rest_framework import serializers
from .models import DailyNutritionCheckIn

class DailyNutritionCheckInSerializer(serializers.ModelSerializer):
    class Meta:
        model = DailyNutritionCheckIn
        fields = '__all__'
        read_only_fields = ('user', 'created_at')
Enter fullscreen mode Exit fullscreen mode

Why mark fields as read-only?

We do not want the client to set:

  • user
  • created_at

Those should be controlled by the backend for security and consistency.

Step 4: Build the API view

Now let’s create the endpoint that saves the check-in and updates the streak.

Add this to views.py:

from datetime import timedelta
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status

from .models import DailyNutritionCheckIn, UserStreak
from .serializers import DailyNutritionCheckInSerializer


class DailyNutritionCheckInView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        serializer = DailyNutritionCheckInSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        checkin = serializer.save(user=request.user)
        log_date = checkin.log_date

        streak, created = UserStreak.objects.get_or_create(
            user=request.user,
            defaults={
                'current_streak': 1,
                'longest_streak': 1,
                'last_logged_date': log_date
            }
        )

        if not created and streak.last_logged_date != log_date:
            if streak.last_logged_date == log_date - timedelta(days=1):
                streak.current_streak += 1
            else:
                streak.current_streak = 1

            if streak.current_streak > streak.longest_streak:
                streak.longest_streak = streak.current_streak

            streak.last_logged_date = log_date
            streak.save()

        return Response({
            "message": "Daily check-in saved successfully.",
            "checkin": DailyNutritionCheckInSerializer(checkin).data,
            "current_streak": streak.current_streak,
            "longest_streak": streak.longest_streak
        }, status=status.HTTP_201_CREATED)
Enter fullscreen mode Exit fullscreen mode

How the streak logic works

The logic is simple:

  • if the user checked in yesterday, increase the streak
  • if they missed a day, reset the streak
  • update the longest streak if the current one beats it

This is a good baseline for gamification in a nutrition app.

Step 5: Add the URL route

Now register the endpoint in urls.py:

from django.urls import path
from .views import DailyNutritionCheckInView

urlpatterns = [
    path('api/check-in/', DailyNutritionCheckInView.as_view(), name='daily-checkin'),
]
Enter fullscreen mode Exit fullscreen mode

Step 6: Test the endpoint

Here’s an example request body you can send to the API:

{
  "breakfast_completed": true,
  "lunch_completed": true,
  "dinner_completed": false,
  "water_intake_ml": 1800,
  "met_calorie_target": true,
  "notes": "Healthy day overall."
}
Enter fullscreen mode Exit fullscreen mode

Expected response

You should get a response like this:

{
  "message": "Daily check-in saved successfully.",
  "checkin": {
    "id": 1,
    "user": 3,
    "log_date": "2025-06-01",
    "breakfast_completed": true,
    "lunch_completed": true,
    "dinner_completed": false,
    "water_intake_ml": 1800,
    "met_calorie_target": true,
    "notes": "Healthy day overall.",
    "created_at": "2025-06-01T10:30:00Z"
  },
  "current_streak": 4,
  "longest_streak": 7
}
Enter fullscreen mode Exit fullscreen mode

Common improvements you can add later

Once the basic feature works, you can improve it by adding:

  • edit/update support for same-day check-ins
  • badge rewards for streak milestones
  • daily reminders
  • weekly progress summaries
  • charts for water intake and meal completion

Conclusion

A daily nutrition check-in is a simple feature, but it adds a lot of value to a meal-planning app. It helps users stay accountable, gives them visible progress, and makes the app feel more engaging.

With Django REST Framework, this feature is straightforward to build using:

  • a model for storing logs
  • a serializer for API validation
  • an authenticated API view
  • streak tracking for motivation

If you’re building a nutrition app, this is a great feature to add early because it creates a strong foundation for gamification and habit tracking.

Top comments (0)