How to Transfer Data from One Database to Another in Django

Hey there! If you’ve ever found yourself needing to transfer data from one database to another in Django, you’re in the right place. Whether you’re migrating to a new database, syncing data between environments, or just moving data around, Django makes it pretty straightforward. In this blog, I’ll walk you through the process step-by-step, and I’ll even throw in some practical functionality to make your life easier.

Why Transfer Data Between Databases?

Before we get into the code, let’s talk about why you might need to transfer data between databases. Here are a few common scenarios:

  1. Database Migration: You’re switching from SQLite to PostgreSQL or MySQL.
  2. Environment Syncing: You need to copy production data to your development environment.
  3. Data Backup: You want to create a backup of your data in a separate database.
  4. Data Aggregation: You’re consolidating data from multiple databases into one.

Whatever your reason, Django’s ORM and database routing capabilities make this process smooth and efficient.

Setting Up the Project

Let’s start by setting up a Django project with two databases. For this example, I’ll use SQLite for the source database and PostgreSQL for the destination database. You can adapt this to any database backend Django supports.

Install Django and Required Packages

First, make sure you have Django and the PostgreSQL adapter installed:

pip install django psycopg2-binary

Configure Django Settings

In your settings.py, define two database connections:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'source_db.sqlite3',  # Source database
    },
    'destination_db': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'destination_db',
        'USER': 'your_db_user',
        'PASSWORD': 'your_db_password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

Here, default is our source database (SQLite), and destination_db is our target database (PostgreSQL).

Create a Django Model

Let’s create a simple model to work with. For this example, we’ll use a Product model:

# models.py
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    description = models.TextField()

    def __str__(self):
        return self.name

Run migrations to create the table in both databases:

python manage.py migrate --database=default
python manage.py migrate --database=destination_db

Transferring Data Between Databases

Now that our setup is ready, let’s write a script to transfer data from the source database to the destination database.

Create a Management Command

Django’s management commands are perfect for tasks like this. Create a custom command to handle the data transfer:

python manage.py startapp data_transfer

In the data_transfer app, create a management/commands directory and add a file named transfer_data.py:

# data_transfer/management/commands/transfer_data.py
from django.core.management.base import BaseCommand
from myapp.models import Product

class Command(BaseCommand):
    help = 'Transfer data from the default database to the destination database'

    def handle(self, *args, **kwargs):
        # Fetch all products from the source database
        products = Product.objects.using('default').all()

        # Transfer each product to the destination database
        for product in products:
            Product.objects.using('destination_db').create(
                name=product.name,
                price=product.price,
                description=product.description
            )

        self.stdout.write(self.style.SUCCESS('Data transfer completed successfully!'))

Run the Command

Execute the command to transfer the data:

python manage.py transfer_data

If everything is set up correctly, you should see the message: Data transfer completed successfully!

Adding Practical Functionality

Let’s make this script more robust by adding some practical features:

Progress Tracking

Add a progress bar to track the transfer process:

from tqdm import tqdm

class Command(BaseCommand):
    help = 'Transfer data from the default database to the destination database'

    def handle(self, *args, **kwargs):
        products = Product.objects.using('default').all()
        total_products = products.count()

        self.stdout.write(f'Transferring {total_products} products...')

        for product in tqdm(products, total=total_products):
            Product.objects.using('destination_db').create(
                name=product.name,
                price=product.price,
                description=product.description
            )

        self.stdout.write(self.style.SUCCESS('Data transfer completed successfully!'))

Error Handling

Handle potential errors, such as duplicate entries or database connection issues:

class Command(BaseCommand):
    help = 'Transfer data from the default database to the destination database'

    def handle(self, *args, **kwargs):
        products = Product.objects.using('default').all()
        total_products = products.count()

        self.stdout.write(f'Transferring {total_products} products...')

        for product in tqdm(products, total=total_products):
            try:
                Product.objects.using('destination_db').create(
                    name=product.name,
                    price=product.price,
                    description=product.description
                )
            except Exception as e:
                self.stdout.write(self.style.ERROR(f'Error transferring product {product.id}: {e}'))

        self.stdout.write(self.style.SUCCESS('Data transfer completed successfully!'))

Dry Run Option

Add a --dry-run option to simulate the transfer without making changes:

class Command(BaseCommand):
    help = 'Transfer data from the default database to the destination database'

    def add_arguments(self, parser):
        parser.add_argument(
            '--dry-run',
            action='store_true',
            help='Simulate the transfer without saving data',
        )

    def handle(self, *args, **kwargs):
        dry_run = kwargs['dry_run']
        products = Product.objects.using('default').all()
        total_products = products.count()

        self.stdout.write(f'Transferring {total_products} products...')

        for product in tqdm(products, total=total_products):
            if not dry_run:
                try:
                    Product.objects.using('destination_db').create(
                        name=product.name,
                        price=product.price,
                        description=product.description
                    )
                except Exception as e:
                    self.stdout.write(self.style.ERROR(f'Error transferring product {product.id}: {e}'))
            else:
                self.stdout.write(f'Would transfer product: {product.name}')

        self.stdout.write(self.style.SUCCESS('Data transfer completed successfully!'))

Run the command with the --dry-run flag to test it:

python manage.py transfer_data --dry-run

Final Thoughts

Transferring data between databases in Django doesn’t have to be complicated. With Django’s ORM and management commands, you can automate the process and even add advanced features like progress tracking, error handling, and dry runs. Whether you’re migrating to a new database or syncing data between environments, this approach is flexible and scalable.

Related blog posts