Introduction: Exporting Recipes for the Real World

Welcome back! So far, you have learned how to make your smart cooking assistant more interactive by adding audio support and new API endpoints for ingredients and reviews. In this lesson, we will take your project one step further by teaching you how to export all your recipes into a CSV file.

Exporting data to CSV (Comma-Separated Values) is a common feature in many real-world applications. CSV files are easy to open in spreadsheet programs like Microsoft Excel or Google Sheets, making it simple to share, analyze, or back up your recipes. By the end of this lesson, you will know how to create a script that pulls all recipes from your database and writes them into a well-structured CSV file.

Recall: How Recipes and Ingredients Are Stored

Before we start building the export script, let's quickly remind ourselves how recipes and ingredients are stored in your project.

  • Each Recipe has a name, a set of instructions (steps), and a connection to multiple ingredients.
  • Each Ingredient has a name and can belong to multiple recipes.
  • The relationship between recipes and ingredients is many-to-many. This means a single recipe can have many ingredients, and a single ingredient can be used in many different recipes.

This structure is managed using models. Here’s a quick look at the relationship:

When we export the data, we will need to "flatten" this relationship so that all the ingredients for a single Recipe appear in one cell of our spreadsheet.

Setting Up the Export Script

To create a standalone script that interacts with your project's database, you need to initialize the environment correctly. This ensures the script knows where to find your settings and models.

You should place this script in a scripts directory at the root of your project. Here is how you set up the environment at the beginning of your file:

import os
import sys

# 1. Determine the paths to the project
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_ROOT = os.path.abspath(os.path.join(CURRENT_DIR, ".."))

# 2. Add the project root to the system path so imports work
if PROJECT_ROOT not in sys.path:
    sys.path.insert(0, PROJECT_ROOT)

# 3. Tell the system which settings file to use
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cooking_helper.settings")

# 4. Initialize the application environment
import django
django.setup()

# 5. Now you can safely import your models
from recipes.models import Recipe
  • sys.path.insert: This tells the script where to look for your project modules.
  • os.environ.setdefault: This points the script to your project's configuration settings, specifically DJANGO_SETTINGS_MODULE and cooking_helper.settings.
  • django.setup(): This command initializes the database connections and loads your models so you can use them in the script.
Querying and Formatting Recipe Data

Now that the script is set up, we need to fetch the recipes. When dealing with many-to-many relationships, it is efficient to use prefetch_related. This tool fetches the related ingredients at the same time as the recipes, preventing the script from making hundreds of tiny database queries.

# Fetch all recipes and their ingredients efficiently
recipes = Recipe.objects.all().prefetch_related("ingredients")

Once we have the data, we need to format it for the CSV format. We want each Recipe to be a single row. This means we must:

  1. Format Ingredients: Turn the list of Ingredient objects into a single string of names, separated by commas.
  2. Clean Steps: Remove any line breaks (\n) from the recipe.steps so they do not break the CSV row structure.

Here is how we process the data:

for recipe in recipes:
    # Get unique ingredient names, sort them, and join them with commas
    ingredient_list = ", ".join(sorted({i.name for i in recipe.ingredients.all() if i.name}))
    
    # Flatten steps into a single line by replacing newlines with spaces
    steps_flat = (recipe.steps or "").replace("\n", " ")
    
    # Now the data is ready for the CSV writer
Writing Recipes to a CSV File

The final step is to use the built-in csv module to create the file. We use a writer object to handle the formatting details, like adding quotes around text that contains commas.

Here is the complete structure of the export_to_csv function:

import csv

def export_to_csv(filename: str = "exported_recipes.csv") -> None:
    # Query the database
    recipes = Recipe.objects.all().prefetch_related("ingredients")

    # Open the file for writing
    with open(filename, "w", newline="", encoding="utf-8") as csvfile:
        writer = csv.writer(csvfile)
        
        # Write the header row
        writer.writerow(["ID", "Name", "Ingredients", "Steps"])

        count = 0
        for recipe in recipes:
            # Format the data
            ingredient_list = ", ".join(sorted({i.name for i in recipe.ingredients.all() if i.name}))
            steps_flat = (recipe.steps or "").replace("\n", " ")
            
            # Write the recipe row
            writer.writerow([recipe.id, recipe.name, ingredient_list, steps_flat])
            count += 1

    print(f"Exported {count} recipes to '{filename}'")
  • open(filename, "w", ...): Opens the file in write mode.
  • writer.writerow([...]): Takes a list of values — like ID, Name, Ingredients, and Steps — and writes them as a single line in the CSV.
  • The with statement ensures the file is saved and closed properly even if an error occurs.

When you run this script, you will see a success message:

Exported 5 recipes to 'exported_recipes.csv'

Your resulting CSV file will look like this when opened in a spreadsheet:

IDNameIngredientsSteps
1Veggie Soupcarrot, onion, water1. Chop veggies. 2. Boil. 3. Serve.
2Fruit Saladapple, banana1. Slice fruit. 2. Mix. 3. Enjoy.
Summary and Practice Preview

In this lesson, you learned how to create a standalone script that exports data from your database. You learned how to initialize the project environment manually, use prefetch_related for efficient data retrieval, and format complex relationships into a simple CSV structure. This is a powerful way to make your application's data portable and accessible to other tools.

You are now ready to implement this export feature in your own project!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal