15 August 2018

how to load initial data and test data in Django 2+

There are two ways to automatically load data in Django:

  • for data you need while running tests, place xml/json/yaml files in yourapp/fixtures.
  • for data you need while setting up the database from scratch, or at specific points in time, you must create a Migration

This is a bit annoying, because chances are these locations will get out of sync sooner or later, and it duplicates effort if you do reproducible builds, docker, and stuff like that.

The solution is to create a migration that actually loads fixtures. So:

  1. Create your fixtures: manage.py dumpdata --output yourapp/fixtures/yourmodel.json yourapp.YourModel
  2. Create an empty Migration: manage.py makemigrations --empty yourapp
  3. Edit the resulting migration (the last file created under yourapp/migrations, making it look like this:
    from django.db import migrations
    
    def load_fixtures(apps, schema_editor):
        # This is what will be executed by the migration
        from django.core.management import call_command
        # this is the equivalent of running manage.py loaddata yourmodel.json
        for fixture_name in ['yourmodel']: # add any additional model here
            call_command("loaddata", fixture_name)
        # add other calls if you have multiple models
    
    def rollback(apps, schema_editor):
        # This will be executed if you rollback the migration, so you want to clean up
        for model_name in ["YourModel"]:  # add any additional model here
            model = apps.get_model("yourapp", model_name)
            model.objects.all().delete()
    
    class Migration(migrations.Migration):
        dependencies = [
          # ... don't touch anything here ...
        ]
    
        operations = [
            migrations.RunPython(load_fixtures, rollback),
        ]
    # -*- coding: utf-8 -*-
    
  4. Profit

Note that this does not remove the option to have data that is available only in certain situation: just don't list the fixtures you don't want in the migration, and vice-versa.