mirror of https://github.com/django/django.git
Fixed #9279 -- Added ignorenonexistent option to loaddata
Thanks to Roman Gladkov for the initial patch and Simon Charette for review.
This commit is contained in:
parent
7cc4068c44
commit
e7723683dc
|
@ -23,6 +23,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_bz2 = False
|
has_bz2 = False
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Installs the named fixture(s) in the database.'
|
help = 'Installs the named fixture(s) in the database.'
|
||||||
args = "fixture [fixture ...]"
|
args = "fixture [fixture ...]"
|
||||||
|
@ -31,9 +32,14 @@ class Command(BaseCommand):
|
||||||
make_option('--database', action='store', dest='database',
|
make_option('--database', action='store', dest='database',
|
||||||
default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load '
|
default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load '
|
||||||
'fixtures into. Defaults to the "default" database.'),
|
'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):
|
def handle(self, *fixture_labels, **options):
|
||||||
|
|
||||||
|
ignore = options.get('ignore')
|
||||||
using = options.get('database')
|
using = options.get('database')
|
||||||
|
|
||||||
connection = connections[using]
|
connection = connections[using]
|
||||||
|
@ -175,7 +181,7 @@ class Command(BaseCommand):
|
||||||
self.stdout.write("Installing %s fixture '%s' from %s." % \
|
self.stdout.write("Installing %s fixture '%s' from %s." % \
|
||||||
(format, fixture_name, humanize(fixture_dir)))
|
(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:
|
for obj in objects:
|
||||||
objects_in_fixture += 1
|
objects_in_fixture += 1
|
||||||
|
|
|
@ -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.encoding import smart_text, is_protected_type
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
|
||||||
class Serializer(base.Serializer):
|
class Serializer(base.Serializer):
|
||||||
"""
|
"""
|
||||||
Serializes a QuerySet to basic Python objects.
|
Serializes a QuerySet to basic Python objects.
|
||||||
|
@ -72,6 +73,7 @@ class Serializer(base.Serializer):
|
||||||
def getvalue(self):
|
def getvalue(self):
|
||||||
return self.objects
|
return self.objects
|
||||||
|
|
||||||
|
|
||||||
def Deserializer(object_list, **options):
|
def Deserializer(object_list, **options):
|
||||||
"""
|
"""
|
||||||
Deserialize simple Python objects back into Django ORM instances.
|
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
|
stream or a string) to the constructor
|
||||||
"""
|
"""
|
||||||
db = options.pop('using', DEFAULT_DB_ALIAS)
|
db = options.pop('using', DEFAULT_DB_ALIAS)
|
||||||
|
ignore = options.pop('ignorenonexistent', False)
|
||||||
|
|
||||||
models.get_apps()
|
models.get_apps()
|
||||||
for d in object_list:
|
for d in object_list:
|
||||||
# Look up the model and starting build a dict of data for it.
|
# Look up the model and starting build a dict of data for it.
|
||||||
Model = _get_model(d["model"])
|
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 = {}
|
m2m_data = {}
|
||||||
|
model_fields = Model._meta.get_all_field_names()
|
||||||
|
|
||||||
# Handle each field
|
# Handle each field
|
||||||
for (field_name, field_value) in six.iteritems(d["fields"]):
|
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):
|
if isinstance(field_value, str):
|
||||||
field_value = smart_text(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
|
field_value = smart_text(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
|
||||||
|
|
||||||
|
|
|
@ -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
|
The :djadminopt:`--database` option can be used to specify the database
|
||||||
onto which the data will be loaded.
|
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"?
|
What's a "fixture"?
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -195,6 +195,9 @@ Django 1.5 also includes several smaller improvements worth noting:
|
||||||
whenever a user fails to login successfully. See
|
whenever a user fails to login successfully. See
|
||||||
:data:`~django.contrib.auth.signals.user_login_failed`
|
: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
|
Backwards incompatible changes in 1.5
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -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``.
|
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:
|
||||||
|
|
||||||
Serialization formats
|
Serialization formats
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -5,6 +5,7 @@ from __future__ import absolute_import, unicode_literals
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from django.core.serializers.base import DeserializationError
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.management.commands.dumpdata import sort_dependencies
|
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):
|
class TestFixtures(TestCase):
|
||||||
|
|
||||||
def animal_pre_save_check(self, signal, sender, instance, **kwargs):
|
def animal_pre_save_check(self, signal, sender, instance, **kwargs):
|
||||||
self.pre_save_checks.append(
|
self.pre_save_checks.append(
|
||||||
(
|
(
|
||||||
|
@ -54,6 +56,33 @@ class TestFixtures(TestCase):
|
||||||
animal.save()
|
animal.save()
|
||||||
self.assertGreater(animal.id, 1)
|
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')
|
@skipIfDBFeature('interprets_empty_strings_as_nulls')
|
||||||
def test_pretty_print_xml(self):
|
def test_pretty_print_xml(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue