From 6786920fd8a1dfa43bba8333548c2496847298af Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Sun, 19 May 2013 12:39:14 +0200 Subject: [PATCH] Fixed #16330 -- added --pks option in dumpdata command Thanks to guettli for the initial ticket and patch, with additional work from mehmetakyuz and Kevin Brolly. --- django/core/management/commands/dumpdata.py | 21 +++++++++- docs/ref/django-admin.txt | 9 +++++ docs/releases/1.6.txt | 4 ++ tests/fixtures/tests.py | 44 ++++++++++++++++++++- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index d3650b1eb8a..b1e06e42558 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -21,6 +21,8 @@ class Command(BaseCommand): help='Use natural keys if they are available.'), make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False, help="Use Django's base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager."), + make_option('--pks', dest='primary_keys', action='append', default=[], + help="Only dump objects with given primary keys. Accepts a comma seperated list of keys. This option will only work when you specify one model."), ) help = ("Output the contents of the database as a fixture of the given " "format (using each model's default manager unless --all is " @@ -37,6 +39,12 @@ class Command(BaseCommand): show_traceback = options.get('traceback') use_natural_keys = options.get('use_natural_keys') use_base_manager = options.get('use_base_manager') + pks = options.get('primary_keys') + + if pks: + primary_keys = pks.split(',') + else: + primary_keys = False excluded_apps = set() excluded_models = set() @@ -55,8 +63,12 @@ class Command(BaseCommand): raise CommandError('Unknown app in excludes: %s' % exclude) if len(app_labels) == 0: + if primary_keys: + raise CommandError("You can only use --pks option with one model") app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps) else: + if len(app_labels) > 1 and primary_keys: + raise CommandError("You can only use --pks option with one model") app_list = SortedDict() for label in app_labels: try: @@ -77,6 +89,8 @@ class Command(BaseCommand): else: app_list[app] = [model] except ValueError: + if primary_keys: + raise CommandError("You can only use --pks option with one model") # This is just an app - no model qualifier app_label = label try: @@ -107,8 +121,11 @@ class Command(BaseCommand): objects = model._base_manager else: objects = model._default_manager - for obj in objects.using(using).\ - order_by(model._meta.pk.name).iterator(): + + queryset = objects.using(using).order_by(model._meta.pk.name) + if primary_keys: + queryset = queryset.filter(pk__in=primary_keys) + for obj in queryset.iterator(): yield obj try: diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 2f2880679cf..e193a448d2c 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -227,6 +227,15 @@ a natural key definition. If you are dumping ``contrib.auth`` ``Permission`` objects or ``contrib.contenttypes`` ``ContentType`` objects, you should probably be using this flag. +.. versionadded:: 1.6 + +.. django-admin-option:: --pks + +By default, ``dumpdata`` will output all the records of the model, but +you can use the ``--pks`` option to specify a comma seperated list of +primary keys on which to filter. This is only available when dumping +one model. + flush ----- diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index a6244498f28..9950717420b 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -249,6 +249,10 @@ Minor features and :func:`~django.contrib.auth.views.password_change`, you can now pass URL names and they will be resolved. +* The ``dumpdata`` manage.py command now has a --pks option which will + allow users to specify the primary keys of objects they want to dump. + This option can only be used with one model. + Backwards incompatible changes in 1.6 ===================================== diff --git a/tests/fixtures/tests.py b/tests/fixtures/tests.py index f9549330469..e32522b9295 100644 --- a/tests/fixtures/tests.py +++ b/tests/fixtures/tests.py @@ -27,14 +27,15 @@ class TestCaseFixtureLoadingTests(TestCase): class DumpDataAssertMixin(object): def _dumpdata_assert(self, args, output, format='json', natural_keys=False, - use_base_manager=False, exclude_list=[]): + use_base_manager=False, exclude_list=[], primary_keys=[]): new_io = six.StringIO() management.call_command('dumpdata', *args, **{'format': format, 'stdout': new_io, 'stderr': new_io, 'use_natural_keys': natural_keys, 'use_base_manager': use_base_manager, - 'exclude': exclude_list}) + 'exclude': exclude_list, + 'primary_keys': primary_keys}) command_output = new_io.getvalue().strip() if format == "json": self.assertJSONEqual(command_output, output) @@ -223,6 +224,45 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): # even those normally filtered by the manager self._dumpdata_assert(['fixtures.Spy'], '[{"pk": %d, "model": "fixtures.spy", "fields": {"cover_blown": true}}, {"pk": %d, "model": "fixtures.spy", "fields": {"cover_blown": false}}]' % (spy2.pk, spy1.pk), use_base_manager=True) + def test_dumpdata_with_pks(self): + management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False) + self._dumpdata_assert( + ['fixtures.Article'], + '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}]', + primary_keys='2,3' + ) + + self._dumpdata_assert( + ['fixtures.Article'], + '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}]', + primary_keys='2' + ) + + with six.assertRaisesRegex(self, management.CommandError, + "You can only use --pks option with one model"): + self._dumpdata_assert( + ['fixtures'], + '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}]', + primary_keys='2,3' + ) + + with six.assertRaisesRegex(self, management.CommandError, + "You can only use --pks option with one model"): + self._dumpdata_assert( + '', + '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}]', + primary_keys='2,3' + ) + + with six.assertRaisesRegex(self, management.CommandError, + "You can only use --pks option with one model"): + self._dumpdata_assert( + ['fixtures.Article', 'fixtures.category'], + '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}]', + primary_keys='2,3' + ) + def test_compress_format_loading(self): # Load fixture 4 (compressed), using format specification management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False)