Fixed #17530 - Fixture loading with deferred constraints broken for Postgres for fixtures from multiple dirs/files

Thanks to claudep for the review.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17372 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Luke Plant 2012-01-14 17:26:32 +00:00
parent 64229eb388
commit 4e29b70b9d
4 changed files with 152 additions and 109 deletions

View File

@ -104,87 +104,89 @@ class Command(BaseCommand):
app_module_paths.append(app.__file__) app_module_paths.append(app.__file__)
app_fixtures = [os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths] app_fixtures = [os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths]
for fixture_label in fixture_labels:
parts = fixture_label.split('.')
if len(parts) > 1 and parts[-1] in compression_types: try:
compression_formats = [parts[-1]] with connection.constraint_checks_disabled():
parts = parts[:-1] for fixture_label in fixture_labels:
else: parts = fixture_label.split('.')
compression_formats = compression_types.keys()
if len(parts) == 1: if len(parts) > 1 and parts[-1] in compression_types:
fixture_name = parts[0] compression_formats = [parts[-1]]
formats = serializers.get_public_serializer_formats() parts = parts[:-1]
else:
fixture_name, format = '.'.join(parts[:-1]), parts[-1]
if format in serializers.get_public_serializer_formats():
formats = [format]
else:
formats = []
if formats:
if verbosity >= 2:
self.stdout.write("Loading '%s' fixtures...\n" % fixture_name)
else:
self.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" %
(fixture_name, format)))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
return
if os.path.isabs(fixture_name):
fixture_dirs = [fixture_name]
else:
fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
for fixture_dir in fixture_dirs:
if verbosity >= 2:
self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir))
label_found = False
for combo in product([using, None], formats, compression_formats):
database, format, compression_format = combo
file_name = '.'.join(
p for p in [
fixture_name, database, format, compression_format
]
if p
)
if verbosity >= 3:
self.stdout.write("Trying %s for %s fixture '%s'...\n" % \
(humanize(fixture_dir), file_name, fixture_name))
full_path = os.path.join(fixture_dir, file_name)
open_method = compression_types[compression_format]
try:
fixture = open_method(full_path, 'r')
except IOError:
if verbosity >= 2:
self.stdout.write("No %s fixture '%s' in %s.\n" % \
(format, fixture_name, humanize(fixture_dir)))
else: else:
if label_found: compression_formats = compression_types.keys()
fixture.close()
self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" % if len(parts) == 1:
(fixture_name, humanize(fixture_dir)))) fixture_name = parts[0]
if commit: formats = serializers.get_public_serializer_formats()
transaction.rollback(using=using) else:
transaction.leave_transaction_management(using=using) fixture_name, format = '.'.join(parts[:-1]), parts[-1]
return if format in serializers.get_public_serializer_formats():
else: formats = [format]
fixture_count += 1 else:
objects_in_fixture = 0 formats = []
loaded_objects_in_fixture = 0
if verbosity >= 2: if formats:
self.stdout.write("Installing %s fixture '%s' from %s.\n" % \ if verbosity >= 2:
(format, fixture_name, humanize(fixture_dir))) self.stdout.write("Loading '%s' fixtures...\n" % fixture_name)
try: else:
objects = serializers.deserialize(format, fixture, using=using) self.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" %
(fixture_name, format)))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
return
if os.path.isabs(fixture_name):
fixture_dirs = [fixture_name]
else:
fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
for fixture_dir in fixture_dirs:
if verbosity >= 2:
self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir))
label_found = False
for combo in product([using, None], formats, compression_formats):
database, format, compression_format = combo
file_name = '.'.join(
p for p in [
fixture_name, database, format, compression_format
]
if p
)
if verbosity >= 3:
self.stdout.write("Trying %s for %s fixture '%s'...\n" % \
(humanize(fixture_dir), file_name, fixture_name))
full_path = os.path.join(fixture_dir, file_name)
open_method = compression_types[compression_format]
try:
fixture = open_method(full_path, 'r')
except IOError:
if verbosity >= 2:
self.stdout.write("No %s fixture '%s' in %s.\n" % \
(format, fixture_name, humanize(fixture_dir)))
else:
try:
if label_found:
self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" %
(fixture_name, humanize(fixture_dir))))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
return
fixture_count += 1
objects_in_fixture = 0
loaded_objects_in_fixture = 0
if verbosity >= 2:
self.stdout.write("Installing %s fixture '%s' from %s.\n" % \
(format, fixture_name, humanize(fixture_dir)))
objects = serializers.deserialize(format, fixture, using=using)
with connection.constraint_checks_disabled():
for obj in objects: for obj in objects:
objects_in_fixture += 1 objects_in_fixture += 1
if router.allow_syncdb(using, obj.object.__class__): if router.allow_syncdb(using, obj.object.__class__):
@ -201,41 +203,43 @@ class Command(BaseCommand):
} }
raise e.__class__, e.__class__(msg), sys.exc_info()[2] raise e.__class__, e.__class__(msg), sys.exc_info()[2]
# Since we disabled constraint checks, we must manually check for loaded_object_count += loaded_objects_in_fixture
# any invalid keys that might have been added fixture_object_count += objects_in_fixture
table_names = [model._meta.db_table for model in models] label_found = True
connection.check_constraints(table_names=table_names) finally:
fixture.close()
loaded_object_count += loaded_objects_in_fixture # If the fixture we loaded contains 0 objects, assume that an
fixture_object_count += objects_in_fixture # error was encountered during fixture loading.
label_found = True if objects_in_fixture == 0:
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
fixture.close()
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
if show_traceback:
traceback.print_exc()
else:
self.stderr.write( self.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" % self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" %
(full_path, ''.join(traceback.format_exception(sys.exc_type, (fixture_name)))
sys.exc_value, sys.exc_traceback))))) if commit:
return transaction.rollback(using=using)
fixture.close() transaction.leave_transaction_management(using=using)
return
# Since we disabled constraint checks, we must manually check for
# any invalid keys that might have been added
table_names = [model._meta.db_table for model in models]
connection.check_constraints(table_names=table_names)
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
if show_traceback:
traceback.print_exc()
else:
self.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" %
(full_path, ''.join(traceback.format_exception(sys.exc_type,
sys.exc_value, sys.exc_traceback)))))
return
# If the fixture we loaded contains 0 objects, assume that an
# error was encountered during fixture loading.
if objects_in_fixture == 0:
self.stderr.write(
self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" %
(fixture_name)))
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
return
# If we found even one object in a fixture, we need to reset the # If we found even one object in a fixture, we need to reset the
# database sequences. # database sequences.

View File

@ -0,0 +1,10 @@
[
{
"pk": 1,
"model": "fixtures_regress.book",
"fields": {
"name": "Cryptonomicon",
"author": 4
}
}
]

View File

@ -0,0 +1,9 @@
[
{
"pk": "4",
"model": "fixtures_regress.person",
"fields": {
"name": "Neal Stephenson"
}
}
]

View File

@ -16,6 +16,7 @@ from django.db import transaction
from django.db.models import signals from django.db.models import signals
from django.test import (TestCase, TransactionTestCase, skipIfDBFeature, from django.test import (TestCase, TransactionTestCase, skipIfDBFeature,
skipUnlessDBFeature) skipUnlessDBFeature)
from django.test.utils import override_settings
from .models import (Animal, Stuff, Absolute, Parent, Child, Article, Widget, from .models import (Animal, Stuff, Absolute, Parent, Child, Article, Widget,
Store, Person, Book, NKChild, RefToNKChild, Circle1, Circle2, Circle3, Store, Person, Book, NKChild, RefToNKChild, Circle1, Circle2, Circle3,
@ -390,6 +391,25 @@ class TestFixtures(TestCase):
stderr.getvalue().startswith('Problem installing fixture') stderr.getvalue().startswith('Problem installing fixture')
) )
_cur_dir = os.path.dirname(os.path.abspath(__file__))
@override_settings(FIXTURE_DIRS=[os.path.join(_cur_dir, 'fixtures_1'),
os.path.join(_cur_dir, 'fixtures_2')])
def test_loaddata_forward_refs_split_fixtures(self):
"""
Regression for #17530 - should be able to cope with forward references
when the fixtures are not in the same files or directories.
"""
management.call_command(
'loaddata',
'forward_ref_1.json',
'forward_ref_2.json',
verbosity=0,
commit=False
)
self.assertEqual(Book.objects.all()[0].id, 1)
self.assertEqual(Person.objects.all()[0].id, 4)
def test_loaddata_no_fixture_specified(self): def test_loaddata_no_fixture_specified(self):
""" """
Regression for #7043 - Error is quickly reported when no fixtures is provided in the command line. Regression for #7043 - Error is quickly reported when no fixtures is provided in the command line.