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.
This commit is contained in:
Preston Holmes 2013-05-19 12:39:14 +02:00
parent bdde7feb26
commit 6786920fd8
4 changed files with 74 additions and 4 deletions

View File

@ -21,6 +21,8 @@ class Command(BaseCommand):
help='Use natural keys if they are available.'), help='Use natural keys if they are available.'),
make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False, 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."), 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 " help = ("Output the contents of the database as a fixture of the given "
"format (using each model's default manager unless --all is " "format (using each model's default manager unless --all is "
@ -37,6 +39,12 @@ class Command(BaseCommand):
show_traceback = options.get('traceback') show_traceback = options.get('traceback')
use_natural_keys = options.get('use_natural_keys') use_natural_keys = options.get('use_natural_keys')
use_base_manager = options.get('use_base_manager') 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_apps = set()
excluded_models = set() excluded_models = set()
@ -55,8 +63,12 @@ class Command(BaseCommand):
raise CommandError('Unknown app in excludes: %s' % exclude) raise CommandError('Unknown app in excludes: %s' % exclude)
if len(app_labels) == 0: 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) app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps)
else: else:
if len(app_labels) > 1 and primary_keys:
raise CommandError("You can only use --pks option with one model")
app_list = SortedDict() app_list = SortedDict()
for label in app_labels: for label in app_labels:
try: try:
@ -77,6 +89,8 @@ class Command(BaseCommand):
else: else:
app_list[app] = [model] app_list[app] = [model]
except ValueError: except ValueError:
if primary_keys:
raise CommandError("You can only use --pks option with one model")
# This is just an app - no model qualifier # This is just an app - no model qualifier
app_label = label app_label = label
try: try:
@ -107,8 +121,11 @@ class Command(BaseCommand):
objects = model._base_manager objects = model._base_manager
else: else:
objects = model._default_manager 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 yield obj
try: try:

View File

@ -227,6 +227,15 @@ a natural key definition. If you are dumping ``contrib.auth`` ``Permission``
objects or ``contrib.contenttypes`` ``ContentType`` objects, you should objects or ``contrib.contenttypes`` ``ContentType`` objects, you should
probably be using this flag. 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 flush
----- -----

View File

@ -249,6 +249,10 @@ Minor features
and :func:`~django.contrib.auth.views.password_change`, you can now pass and :func:`~django.contrib.auth.views.password_change`, you can now pass
URL names and they will be resolved. 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 Backwards incompatible changes in 1.6
===================================== =====================================

View File

@ -27,14 +27,15 @@ class TestCaseFixtureLoadingTests(TestCase):
class DumpDataAssertMixin(object): class DumpDataAssertMixin(object):
def _dumpdata_assert(self, args, output, format='json', natural_keys=False, 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() new_io = six.StringIO()
management.call_command('dumpdata', *args, **{'format': format, management.call_command('dumpdata', *args, **{'format': format,
'stdout': new_io, 'stdout': new_io,
'stderr': new_io, 'stderr': new_io,
'use_natural_keys': natural_keys, 'use_natural_keys': natural_keys,
'use_base_manager': use_base_manager, 'use_base_manager': use_base_manager,
'exclude': exclude_list}) 'exclude': exclude_list,
'primary_keys': primary_keys})
command_output = new_io.getvalue().strip() command_output = new_io.getvalue().strip()
if format == "json": if format == "json":
self.assertJSONEqual(command_output, output) self.assertJSONEqual(command_output, output)
@ -223,6 +224,45 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase):
# even those normally filtered by the manager # 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) 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): def test_compress_format_loading(self):
# Load fixture 4 (compressed), using format specification # Load fixture 4 (compressed), using format specification
management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False) management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False)