mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						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 | ||||
| ~~~~~~~~~~ | ||||
|  | ||||
|   | ||||
							
								
								
									
										29
									
								
								tests/fixtures/tests.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								tests/fixtures/tests.py
									
									
									
									
										vendored
									
									
								
							| @@ -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): | ||||
|     """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user