Fixed #27978 -- Allowed loaddata to read data from stdin.
Thanks Squareweave for the django-loaddata-stdin project from which this is adapted.
This commit is contained in:
parent
c930c241f8
commit
af1fa5e7da
1
AUTHORS
1
AUTHORS
|
@ -617,6 +617,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Paulo Poiati <paulogpoiati@gmail.com>
|
||||
Paulo Scardine <paulo@scardine.com.br>
|
||||
Paul Smith <blinkylights23@gmail.com>
|
||||
Pavel Kulikov <kulikovpavel@gmail.com>
|
||||
pavithran s <pavithran.s@gmail.com>
|
||||
Pavlo Kapyshin <i@93z.org>
|
||||
permonik@mesias.brnonet.cz
|
||||
|
|
|
@ -2,6 +2,7 @@ import functools
|
|||
import glob
|
||||
import gzip
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
import zipfile
|
||||
from itertools import product
|
||||
|
@ -25,6 +26,8 @@ try:
|
|||
except ImportError:
|
||||
has_bz2 = False
|
||||
|
||||
READ_STDIN = '-'
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Installs the named fixture(s) in the database.'
|
||||
|
@ -52,6 +55,10 @@ class Command(BaseCommand):
|
|||
'-e', '--exclude', dest='exclude', action='append', default=[],
|
||||
help='An app_label or app_label.ModelName to exclude. Can be used multiple times.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--format', action='store', dest='format', default=None,
|
||||
help='Format of serialized data when reading from stdin.',
|
||||
)
|
||||
|
||||
def handle(self, *fixture_labels, **options):
|
||||
self.ignore = options['ignore']
|
||||
|
@ -59,6 +66,7 @@ class Command(BaseCommand):
|
|||
self.app_label = options['app_label']
|
||||
self.verbosity = options['verbosity']
|
||||
self.excluded_models, self.excluded_apps = parse_apps_and_model_labels(options['exclude'])
|
||||
self.format = options['format']
|
||||
|
||||
with transaction.atomic(using=self.using):
|
||||
self.loaddata(fixture_labels)
|
||||
|
@ -85,6 +93,7 @@ class Command(BaseCommand):
|
|||
None: (open, 'rb'),
|
||||
'gz': (gzip.GzipFile, 'rb'),
|
||||
'zip': (SingleZipReader, 'r'),
|
||||
'stdin': (lambda *args: sys.stdin, None),
|
||||
}
|
||||
if has_bz2:
|
||||
self.compression_formats['bz2'] = (bz2.BZ2File, 'r')
|
||||
|
@ -201,6 +210,9 @@ class Command(BaseCommand):
|
|||
@functools.lru_cache(maxsize=None)
|
||||
def find_fixtures(self, fixture_label):
|
||||
"""Find fixture files for a given label."""
|
||||
if fixture_label == READ_STDIN:
|
||||
return [(READ_STDIN, None, READ_STDIN)]
|
||||
|
||||
fixture_name, ser_fmt, cmp_fmt = self.parse_name(fixture_label)
|
||||
databases = [self.using, None]
|
||||
cmp_fmts = list(self.compression_formats.keys()) if cmp_fmt is None else [cmp_fmt]
|
||||
|
@ -288,6 +300,11 @@ class Command(BaseCommand):
|
|||
"""
|
||||
Split fixture name in name, serialization format, compression format.
|
||||
"""
|
||||
if fixture_name == READ_STDIN:
|
||||
if not self.format:
|
||||
raise CommandError('--format must be specified when reading from stdin.')
|
||||
return READ_STDIN, self.format, 'stdin'
|
||||
|
||||
parts = fixture_name.rsplit('.', 2)
|
||||
|
||||
if len(parts) > 1 and parts[-1] in self.compression_formats:
|
||||
|
|
|
@ -416,6 +416,14 @@ originally generated.
|
|||
|
||||
Specifies a single app to look for fixtures in rather than looking in all apps.
|
||||
|
||||
.. django-admin-option:: --format FORMAT
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Specifies the :ref:`serialization format <serialization-formats>` (e.g.,
|
||||
``json`` or ``xml``) for fixtures :ref:`read from stdin
|
||||
<loading-fixtures-stdin>`.
|
||||
|
||||
.. django-admin-option:: --exclude EXCLUDE, -e EXCLUDE
|
||||
|
||||
.. versionadded:: 1.11
|
||||
|
@ -552,6 +560,27 @@ defined, name the fixture ``mydata.master.json`` or
|
|||
``mydata.master.json.gz`` and the fixture will only be loaded when you
|
||||
specify you want to load data into the ``master`` database.
|
||||
|
||||
.. _loading-fixtures-stdin:
|
||||
|
||||
Loading fixtures from ``stdin``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
You can use a dash as the fixture name to load input from ``sys.stdin``. For
|
||||
example::
|
||||
|
||||
django-admin loaddata --format=json -
|
||||
|
||||
When reading from ``stdin``, the :option:`--format <loaddata --format>` option
|
||||
is required to specify the :ref:`serialization format <serialization-formats>`
|
||||
of the input (e.g., ``json`` or ``xml``).
|
||||
|
||||
Loading from ``stdin`` is useful with standard input and output redirections.
|
||||
For example::
|
||||
|
||||
django-admin dumpdata --format=json --database=test app_label.ModelName | django-admin loaddata --format=json --database=prod -
|
||||
|
||||
``makemessages``
|
||||
----------------
|
||||
|
||||
|
|
|
@ -185,6 +185,8 @@ Management Commands
|
|||
* The new :option:`makemessages --add-location` option controls the comment
|
||||
format in PO files.
|
||||
|
||||
* :djadmin:`loaddata` can now :ref:`read from stdin <loading-fixtures-stdin>`.
|
||||
|
||||
Migrations
|
||||
~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -680,6 +680,35 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase):
|
|||
with self.assertRaisesMessage(management.CommandError, msg):
|
||||
management.call_command('loaddata', 'fixture1', exclude=['fixtures.FooModel'], verbosity=0)
|
||||
|
||||
def test_stdin_without_format(self):
|
||||
"""Reading from stdin raises an error if format isn't specified."""
|
||||
msg = '--format must be specified when reading from stdin.'
|
||||
with self.assertRaisesMessage(management.CommandError, msg):
|
||||
management.call_command('loaddata', '-', verbosity=0)
|
||||
|
||||
def test_loading_stdin(self):
|
||||
"""Loading fixtures from stdin with json and xml."""
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
fixture_json = os.path.join(tests_dir, 'fixtures', 'fixture1.json')
|
||||
fixture_xml = os.path.join(tests_dir, 'fixtures', 'fixture3.xml')
|
||||
|
||||
with mock.patch('django.core.management.commands.loaddata.sys.stdin', open(fixture_json, 'r')):
|
||||
management.call_command('loaddata', '--format=json', '-', verbosity=0)
|
||||
self.assertEqual(Article.objects.count(), 2)
|
||||
self.assertQuerysetEqual(Article.objects.all(), [
|
||||
'<Article: Time to reform copyright>',
|
||||
'<Article: Poker has no place on ESPN>',
|
||||
])
|
||||
|
||||
with mock.patch('django.core.management.commands.loaddata.sys.stdin', open(fixture_xml, 'r')):
|
||||
management.call_command('loaddata', '--format=xml', '-', verbosity=0)
|
||||
self.assertEqual(Article.objects.count(), 3)
|
||||
self.assertQuerysetEqual(Article.objects.all(), [
|
||||
'<Article: XML identified as leading cause of cancer>',
|
||||
'<Article: Time to reform copyright>',
|
||||
'<Article: Poker on TV is great!>',
|
||||
])
|
||||
|
||||
|
||||
class NonexistentFixtureTests(TestCase):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue