Django management command to list, filter and exclude models from a fixture
Dumping Django data to a fixture and loading it back up again are accomplished with the built-in management commands dumpdata and loaddata. However, loading a fixture into an existing database is a little trickier than loading it into an empty database. Because the fixture json contains the original primary keys of the records, you can get integrity errors.
Recently, I had a use case where I wanted to recover from this situation by excluding some models from the fixture. What I came up with is a new management command called copydata that takes an existing fixture file and can list, filter and exclude a subset of the models.
from django.core.management.base import BaseCommand from optparse import make_option import json class Command(BaseCommand): operation = None filters = [] excludes = [] args = '<file [--list|--filter|--exclude]>' help = 'Copy/filter JSON fixture data to strip out certain models. ' \ 'Useful if certain parts of a fixture are failing.' option_list = BaseCommand.option_list + ( make_option('--list', action='store_true', dest='list', default=False, help='List the models in a fixture'), make_option('--filter', action='store', dest='filter', default=False, help='Output the fixture with only these models, ' \ 'comma-separated list.'), make_option('--exclude', action='store', dest='exclude', default=False, help='Output the fixture without these models, ' \ 'comma-separated list.'), ) def handle(self, file_path=None, list=False, filter=None, exclude=None, **options): if list: self.operation = 'list' if filter: self.filters = filter.split(',') self.operation = 'copy' if exclude: self.excludes = exclude.split(',') self.operation = 'copy' self.call('init') for record in json.load(open(file_path)): self.call('iter', record) self.call('final') def call(self, func_postfix, *args, **kwargs): func_name = self.operation + '_' + func_postfix if hasattr(self, func_name): func = getattr(self, func_name) func(*args, **kwargs) def list_init(self): self.model_set = set() def list_iter(self, record): self.model_set.add(record.get('model')) def list_final(self): for model in list(self.model_set): print model def copy_init(self): self.json_out = [] def copy_iter(self, record): if self.filters and record.get('model') not in self.filters: return if self.excludes and record.get('model') in self.excludes: return self.json_out.append(record) def copy_final(self): print json.dumps(self.json_out, indent=4)
After saving this file as copydata.py inside the management/commands directory of your Django application, you can do the following.
>./manage.py dumpdata myapp > /tmp/fixture.json >./manage.py copydata /tmp/fixture.json --list myapp.friend myapp.invite myapp.profile >./manage.py copydata /tmp/fixture.json --filter myapp.friend > /tmp/just_friends.json >./manage.py copydata /tmp/fixture.json --exclude myapp.friend > /tmp/just_invites_and_profiles.json
If you still have access to the database where the fixture was dumped from, it's more straight forward to just dump the data again and use the built-in appname.Model arguments on the command-line, though you will need to list ALL of them in the exclude case.