Commit 6786920f authored by Preston Holmes's avatar Preston Holmes
Browse files

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.
parent bdde7feb
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -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:
+9 −0
Original line number Diff line number Diff line
@@ -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
-----

+4 −0
Original line number Diff line number Diff line
@@ -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
=====================================

+42 −2
Original line number Diff line number Diff line
@@ -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)