Skip to content

Commit

Permalink
Fixed #9279 -- Added ignorenonexistent option to loaddata
Browse files Browse the repository at this point in the history
Thanks to Roman Gladkov for the initial patch and Simon Charette for review.
  • Loading branch information
ptone committed Oct 1, 2012
1 parent 7cc4068 commit e772368
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 2 deletions.
8 changes: 7 additions & 1 deletion django/core/management/commands/loaddata.py
Expand Up @@ -23,6 +23,7 @@
except ImportError:
has_bz2 = False


class Command(BaseCommand):
help = 'Installs the named fixture(s) in the database.'
args = "fixture [fixture ...]"
Expand All @@ -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'

This comment has been minimized.

Copy link
@claudep

claudep Oct 1, 2012

Member

s/serialised/serialized/

' that have been removed from the database'),
)

def handle(self, *fixture_labels, **options):

ignore = options.get('ignore')
using = options.get('database')

connection = connections[using]
Expand Down Expand Up @@ -175,7 +181,7 @@ def read(self):
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
Expand Down
12 changes: 11 additions & 1 deletion django/core/serializers/python.py
Expand Up @@ -11,6 +11,7 @@
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.
Expand Down Expand Up @@ -72,6 +73,7 @@ def handle_m2m_field(self, obj, field):
def getvalue(self):
return self.objects


def Deserializer(object_list, **options):
"""
Deserialize simple Python objects back into Django ORM instances.
Expand All @@ -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)

Expand Down
5 changes: 5 additions & 0 deletions docs/ref/django-admin.txt
Expand Up @@ -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"?
~~~~~~~~~~~~~~~~~~~

Expand Down
3 changes: 3 additions & 0 deletions docs/releases/1.5.txt
Expand Up @@ -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
=====================================

Expand Down
8 changes: 8 additions & 0 deletions docs/topics/serialization.txt
Expand Up @@ -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
Expand Down
@@ -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 changes: 29 additions & 0 deletions tests/regressiontests/fixtures_regress/tests.py
Expand Up @@ -5,6 +5,7 @@
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
Expand All @@ -22,6 +23,7 @@


class TestFixtures(TestCase):

def animal_pre_save_check(self, signal, sender, instance, **kwargs):
self.pre_save_checks.append(
(
Expand Down Expand Up @@ -54,6 +56,33 @@ def test_duplicate_pk(self):
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):
"""
Expand Down

0 comments on commit e772368

Please sign in to comment.