Quellcode durchsuchen

Fixed #9279 -- Added ignorenonexistent option to loaddata

Thanks to Roman Gladkov for the initial patch and Simon Charette for review.
Preston Holmes vor 12 Jahren
Ursprung
Commit
e7723683dc

+ 7 - 1
django/core/management/commands/loaddata.py

@@ -23,6 +23,7 @@ try:
 except ImportError:
     has_bz2 = False
 
+
 class Command(BaseCommand):
     help = 'Installs the named fixture(s) in the database.'
     args = "fixture [fixture ...]"
@@ -31,9 +32,14 @@ class Command(BaseCommand):
         make_option('--database', action='store', dest='database',
             default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load '
                 'fixtures into. Defaults to the "default" database.'),
+        make_option('--ignorenonexistent', '-i', action='store_true', dest='ignore',
+            default=False, help='Ignores entries in the serialised data for fields'
+                                ' that have been removed from the database'),
     )
 
     def handle(self, *fixture_labels, **options):
+
+        ignore = options.get('ignore')
         using = options.get('database')
 
         connection = connections[using]
@@ -175,7 +181,7 @@ class Command(BaseCommand):
                                         self.stdout.write("Installing %s fixture '%s' from %s." % \
                                             (format, fixture_name, humanize(fixture_dir)))
 
-                                    objects = serializers.deserialize(format, fixture, using=using)
+                                    objects = serializers.deserialize(format, fixture, using=using, ignorenonexistent=ignore)
 
                                     for obj in objects:
                                         objects_in_fixture += 1

+ 11 - 1
django/core/serializers/python.py

@@ -11,6 +11,7 @@ from django.db import models, DEFAULT_DB_ALIAS
 from django.utils.encoding import smart_text, is_protected_type
 from django.utils import six
 
+
 class Serializer(base.Serializer):
     """
     Serializes a QuerySet to basic Python objects.
@@ -72,6 +73,7 @@ class Serializer(base.Serializer):
     def getvalue(self):
         return self.objects
 
+
 def Deserializer(object_list, **options):
     """
     Deserialize simple Python objects back into Django ORM instances.
@@ -80,15 +82,23 @@ def Deserializer(object_list, **options):
     stream or a string) to the constructor
     """
     db = options.pop('using', DEFAULT_DB_ALIAS)
+    ignore = options.pop('ignorenonexistent', False)
+
     models.get_apps()
     for d in object_list:
         # Look up the model and starting build a dict of data for it.
         Model = _get_model(d["model"])
-        data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
+        data = {Model._meta.pk.attname: Model._meta.pk.to_python(d["pk"])}
         m2m_data = {}
+        model_fields = Model._meta.get_all_field_names()
 
         # Handle each field
         for (field_name, field_value) in six.iteritems(d["fields"]):
+
+            if ignore and field_name not in model_fields:
+                # skip fields no longer on model
+                continue
+
             if isinstance(field_value, str):
                 field_value = smart_text(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
 

+ 5 - 0
docs/ref/django-admin.txt

@@ -289,6 +289,11 @@ Searches for and loads the contents of the named fixture into the database.
 The :djadminopt:`--database` option can be used to specify the database
 onto which the data will be loaded.
 
+.. versionadded:: 1.5
+
+The :djadminopt:`--ignorenonexistent` option can be used to ignore fields that
+may have been removed from models since the fixture was originally generated.
+
 What's a "fixture"?
 ~~~~~~~~~~~~~~~~~~~
 

+ 3 - 0
docs/releases/1.5.txt

@@ -195,6 +195,9 @@ Django 1.5 also includes several smaller improvements worth noting:
   whenever a user fails to login successfully. See
   :data:`~django.contrib.auth.signals.user_login_failed`
 
+* The loaddata management command now supports an `ignorenonexistent` option to
+  ignore data for fields that no longer exist.
+
 Backwards incompatible changes in 1.5
 =====================================
 

+ 8 - 0
docs/topics/serialization.txt

@@ -130,6 +130,14 @@ trust your data source you could just save the object and move on.
 
 The Django object itself can be inspected as ``deserialized_object.object``.
 
+.. versionadded:: 1.5
+
+If fields in the serialized data do not exist on a model,
+a ``DeserializationError`` will be raised unless the ``ignorenonexistent``
+argument is passed in as True::
+
+    serializers.deserialize("xml", data, ignorenonexistent=True)
+
 .. _serialization-formats:
 
 Serialization formats

+ 13 - 0
tests/regressiontests/fixtures_regress/fixtures/sequence_extra.json

@@ -0,0 +1,13 @@
+[
+    {
+        "pk": "1",
+        "model": "fixtures_regress.animal",
+        "fields": {
+            "name": "Lion",
+            "extra_name": "Super Lion",
+            "latin_name": "Panthera leo",
+            "count": 3,
+            "weight": 1.2
+        }
+    }
+]

+ 29 - 0
tests/regressiontests/fixtures_regress/tests.py

@@ -5,6 +5,7 @@ from __future__ import absolute_import, unicode_literals
 import os
 import re
 
+from django.core.serializers.base import DeserializationError
 from django.core import management
 from django.core.management.base import CommandError
 from django.core.management.commands.dumpdata import sort_dependencies
@@ -22,6 +23,7 @@ from .models import (Animal, Stuff, Absolute, Parent, Child, Article, Widget,
 
 
 class TestFixtures(TestCase):
+
     def animal_pre_save_check(self, signal, sender, instance, **kwargs):
         self.pre_save_checks.append(
             (
@@ -54,6 +56,33 @@ class TestFixtures(TestCase):
         animal.save()
         self.assertGreater(animal.id, 1)
 
+    def test_loaddata_not_found_fields_not_ignore(self):
+        """
+        Test for ticket #9279 -- Error is raised for entries in
+        the serialised data for fields that have been removed
+        from the database when not ignored.
+        """
+        with self.assertRaises(DeserializationError):
+            management.call_command(
+                'loaddata',
+                'sequence_extra',
+                verbosity=0
+            )
+
+    def test_loaddata_not_found_fields_ignore(self):
+        """
+        Test for ticket #9279 -- Ignores entries in
+        the serialised data for fields that have been removed
+        from the database.
+        """
+        management.call_command(
+            'loaddata',
+            'sequence_extra',
+            ignore=True,
+            verbosity=0
+        )
+        self.assertEqual(Animal.specimens.all()[0].name, 'Lion')
+
     @skipIfDBFeature('interprets_empty_strings_as_nulls')
     def test_pretty_print_xml(self):
         """