Kaydet (Commit) e527c0b6 authored tarafından Tai Lee's avatar Tai Lee Kaydeden (comit) Tim Graham

Fixed #13252 -- Added ability to serialize with natural primary keys.

Added ``--natural-foreign`` and ``--natural-primary`` options and
deprecated the ``--natural`` option to the ``dumpdata`` management
command.

Added ``use_natural_foreign_keys`` and ``use_natural_primary_keys``
arguments and deprecated the ``use_natural_keys`` argument to
``django.core.serializers.Serializer.serialize()``.

Thanks SmileyChris for the suggestion.
üst 945e033a
import warnings
from collections import OrderedDict from collections import OrderedDict
from optparse import make_option from optparse import make_option
...@@ -20,6 +22,10 @@ class Command(BaseCommand): ...@@ -20,6 +22,10 @@ class Command(BaseCommand):
help='An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).'), help='An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).'),
make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False, make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False,
help='Use natural keys if they are available.'), help='Use natural keys if they are available.'),
make_option('--natural-foreign', action='store_true', dest='use_natural_foreign_keys', default=False,
help='Use natural foreign keys if they are available.'),
make_option('--natural-primary', action='store_true', dest='use_natural_primary_keys', default=False,
help='Use natural primary keys if they are available.'),
make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False, make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False,
help="Use Django's base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager."), help="Use Django's base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager."),
make_option('--pks', dest='primary_keys', help="Only dump objects with " make_option('--pks', dest='primary_keys', help="Only dump objects with "
...@@ -40,6 +46,11 @@ class Command(BaseCommand): ...@@ -40,6 +46,11 @@ class Command(BaseCommand):
excludes = options.get('exclude') excludes = options.get('exclude')
show_traceback = options.get('traceback') show_traceback = options.get('traceback')
use_natural_keys = options.get('use_natural_keys') use_natural_keys = options.get('use_natural_keys')
if use_natural_keys:
warnings.warn("``--natural`` is deprecated; use ``--natural-foreign`` instead.",
PendingDeprecationWarning)
use_natural_foreign_keys = options.get('use_natural_foreign_keys') or use_natural_keys
use_natural_primary_keys = options.get('use_natural_primary_keys')
use_base_manager = options.get('use_base_manager') use_base_manager = options.get('use_base_manager')
pks = options.get('primary_keys') pks = options.get('primary_keys')
...@@ -133,7 +144,9 @@ class Command(BaseCommand): ...@@ -133,7 +144,9 @@ class Command(BaseCommand):
try: try:
self.stdout.ending = None self.stdout.ending = None
serializers.serialize(format, get_objects(), indent=indent, serializers.serialize(format, get_objects(), indent=indent,
use_natural_keys=use_natural_keys, stream=self.stdout) use_natural_foreign_keys=use_natural_foreign_keys,
use_natural_primary_keys=use_natural_primary_keys,
stream=self.stdout)
except Exception as e: except Exception as e:
if show_traceback: if show_traceback:
raise raise
......
""" """
Module for abstract serializer/unserializer base classes. Module for abstract serializer/unserializer base classes.
""" """
import warnings
from django.db import models from django.db import models
from django.utils import six from django.utils import six
...@@ -35,6 +36,11 @@ class Serializer(object): ...@@ -35,6 +36,11 @@ class Serializer(object):
self.stream = options.pop("stream", six.StringIO()) self.stream = options.pop("stream", six.StringIO())
self.selected_fields = options.pop("fields", None) self.selected_fields = options.pop("fields", None)
self.use_natural_keys = options.pop("use_natural_keys", False) self.use_natural_keys = options.pop("use_natural_keys", False)
if self.use_natural_keys:
warnings.warn("``use_natural_keys`` is deprecated; use ``use_natural_foreign_keys`` instead.",
PendingDeprecationWarning)
self.use_natural_foreign_keys = options.pop('use_natural_foreign_keys', False) or self.use_natural_keys
self.use_natural_primary_keys = options.pop('use_natural_primary_keys', False)
self.start_serialization() self.start_serialization()
self.first = True self.first = True
...@@ -169,3 +175,20 @@ class DeserializedObject(object): ...@@ -169,3 +175,20 @@ class DeserializedObject(object):
# prevent a second (possibly accidental) call to save() from saving # prevent a second (possibly accidental) call to save() from saving
# the m2m data twice. # the m2m data twice.
self.m2m_data = None self.m2m_data = None
def build_instance(Model, data, db):
"""
Build a model instance.
If the model instance doesn't have a primary key and the model supports
natural keys, try to retrieve it from the database.
"""
obj = Model(**data)
if (obj.pk is None and hasattr(Model, 'natural_key') and
hasattr(Model._default_manager, 'get_by_natural_key')):
natural_key = obj.natural_key()
try:
obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
except Model.DoesNotExist:
pass
return obj
...@@ -34,11 +34,14 @@ class Serializer(base.Serializer): ...@@ -34,11 +34,14 @@ class Serializer(base.Serializer):
self._current = None self._current = None
def get_dump_object(self, obj): def get_dump_object(self, obj):
return { data = {
"pk": smart_text(obj._get_pk_val(), strings_only=True),
"model": smart_text(obj._meta), "model": smart_text(obj._meta),
"fields": self._current "fields": self._current,
} }
if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
data["pk"] = smart_text(obj._get_pk_val(), strings_only=True)
return data
def handle_field(self, obj, field): def handle_field(self, obj, field):
value = field._get_val_from_obj(obj) value = field._get_val_from_obj(obj)
...@@ -51,7 +54,7 @@ class Serializer(base.Serializer): ...@@ -51,7 +54,7 @@ class Serializer(base.Serializer):
self._current[field.name] = field.value_to_string(obj) self._current[field.name] = field.value_to_string(obj)
def handle_fk_field(self, obj, field): def handle_fk_field(self, obj, field):
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
related = getattr(obj, field.name) related = getattr(obj, field.name)
if related: if related:
value = related.natural_key() value = related.natural_key()
...@@ -63,7 +66,7 @@ class Serializer(base.Serializer): ...@@ -63,7 +66,7 @@ class Serializer(base.Serializer):
def handle_m2m_field(self, obj, field): def handle_m2m_field(self, obj, field):
if field.rel.through._meta.auto_created: if field.rel.through._meta.auto_created:
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
m2m_value = lambda value: value.natural_key() m2m_value = lambda value: value.natural_key()
else: else:
m2m_value = lambda value: smart_text(value._get_pk_val(), strings_only=True) m2m_value = lambda value: smart_text(value._get_pk_val(), strings_only=True)
...@@ -88,7 +91,9 @@ def Deserializer(object_list, **options): ...@@ -88,7 +91,9 @@ def Deserializer(object_list, **options):
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.get("pk", None))} data = {}
if 'pk' in d:
data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get("pk", None))
m2m_data = {} m2m_data = {}
model_fields = Model._meta.get_all_field_names() model_fields = Model._meta.get_all_field_names()
...@@ -139,7 +144,8 @@ def Deserializer(object_list, **options): ...@@ -139,7 +144,8 @@ def Deserializer(object_list, **options):
else: else:
data[field.name] = field.to_python(field_value) data[field.name] = field.to_python(field_value)
yield base.DeserializedObject(Model(**data), m2m_data) obj = base.build_instance(Model, data, db)
yield base.DeserializedObject(obj, m2m_data)
def _get_model(model_identifier): def _get_model(model_identifier):
""" """
......
...@@ -46,14 +46,11 @@ class Serializer(base.Serializer): ...@@ -46,14 +46,11 @@ class Serializer(base.Serializer):
raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
self.indent(1) self.indent(1)
obj_pk = obj._get_pk_val() attrs = {"model": smart_text(obj._meta)}
if obj_pk is None: if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
attrs = {"model": smart_text(obj._meta),} obj_pk = obj._get_pk_val()
else: if obj_pk is not None:
attrs = { attrs['pk'] = smart_text(obj_pk)
"pk": smart_text(obj._get_pk_val()),
"model": smart_text(obj._meta),
}
self.xml.startElement("object", attrs) self.xml.startElement("object", attrs)
...@@ -91,7 +88,7 @@ class Serializer(base.Serializer): ...@@ -91,7 +88,7 @@ class Serializer(base.Serializer):
self._start_relational_field(field) self._start_relational_field(field)
related_att = getattr(obj, field.get_attname()) related_att = getattr(obj, field.get_attname())
if related_att is not None: if related_att is not None:
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
related = getattr(obj, field.name) related = getattr(obj, field.name)
# If related object has a natural key, use it # If related object has a natural key, use it
related = related.natural_key() related = related.natural_key()
...@@ -114,7 +111,7 @@ class Serializer(base.Serializer): ...@@ -114,7 +111,7 @@ class Serializer(base.Serializer):
""" """
if field.rel.through._meta.auto_created: if field.rel.through._meta.auto_created:
self._start_relational_field(field) self._start_relational_field(field)
if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
# If the objects in the m2m have a natural key, use it # If the objects in the m2m have a natural key, use it
def handle_m2m(value): def handle_m2m(value):
natural = value.natural_key() natural = value.natural_key()
...@@ -177,13 +174,10 @@ class Deserializer(base.Deserializer): ...@@ -177,13 +174,10 @@ class Deserializer(base.Deserializer):
Model = self._get_model_from_node(node, "model") Model = self._get_model_from_node(node, "model")
# Start building a data dictionary from the object. # Start building a data dictionary from the object.
# If the node is missing the pk set it to None data = {}
if node.hasAttribute("pk"): if node.hasAttribute('pk'):
pk = node.getAttribute("pk") data[Model._meta.pk.attname] = Model._meta.pk.to_python(
else: node.getAttribute('pk'))
pk = None
data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}
# Also start building a dict of m2m data (this is saved as # Also start building a dict of m2m data (this is saved as
# {m2m_accessor_attribute : [list_of_related_objects]}) # {m2m_accessor_attribute : [list_of_related_objects]})
...@@ -217,8 +211,10 @@ class Deserializer(base.Deserializer): ...@@ -217,8 +211,10 @@ class Deserializer(base.Deserializer):
value = field.to_python(getInnerText(field_node).strip()) value = field.to_python(getInnerText(field_node).strip())
data[field.name] = value data[field.name] = value
obj = base.build_instance(Model, data, self.db)
# Return a DeserializedObject so that the m2m data has a place to live. # Return a DeserializedObject so that the m2m data has a place to live.
return base.DeserializedObject(Model(**data), m2m_data) return base.DeserializedObject(obj, m2m_data)
def _handle_fk_field_node(self, node, field): def _handle_fk_field_node(self, node, field):
""" """
......
...@@ -461,6 +461,12 @@ these changes. ...@@ -461,6 +461,12 @@ these changes.
``BaseMemcachedCache._get_memcache_timeout()`` method to ``BaseMemcachedCache._get_memcache_timeout()`` method to
``get_backend_timeout()``. ``get_backend_timeout()``.
* The ``--natural`` and ``-n`` options for :djadmin:`dumpdata` will be removed.
Use :djadminopt:`--natural-foreign` instead.
* The ``use_natural_keys`` argument for ``serializers.serialize()`` will be
removed. Use ``use_natural_foreign_keys`` instead.
2.0 2.0
--- ---
......
...@@ -220,13 +220,34 @@ also mix application names and model names. ...@@ -220,13 +220,34 @@ also mix application names and model names.
The :djadminopt:`--database` option can be used to specify the database The :djadminopt:`--database` option can be used to specify the database
from which data will be dumped. from which data will be dumped.
.. django-admin-option:: --natural-foreign
.. versionadded:: 1.7
When this option is specified, Django will use the ``natural_key()`` model
method to serialize any foreign key and many-to-many relationship to objects of
the type that defines the method. If you are dumping ``contrib.auth``
``Permission`` objects or ``contrib.contenttypes`` ``ContentType`` objects, you
should probably be using this flag. See the :ref:`natural keys
<topics-serialization-natural-keys>` documentation for more details on this
and the next option.
.. django-admin-option:: --natural-primary
.. versionadded:: 1.7
When this option is specified, Django will not provide the primary key in the
serialized data of this object since it can be calculated during
deserialization.
.. django-admin-option:: --natural .. django-admin-option:: --natural
.. deprecated:: 1.7
Equivalent to the :djadminopt:`--natural-foreign` option; use that instead.
Use :ref:`natural keys <topics-serialization-natural-keys>` to represent Use :ref:`natural keys <topics-serialization-natural-keys>` to represent
any foreign key and many-to-many relationship with a model that provides any foreign key and many-to-many relationship with a model that provides
a natural key definition. If you are dumping ``contrib.auth`` ``Permission`` a natural key definition.
objects or ``contrib.contenttypes`` ``ContentType`` objects, you should
probably be using this flag.
.. versionadded:: 1.6 .. versionadded:: 1.6
......
...@@ -294,6 +294,11 @@ Management Commands ...@@ -294,6 +294,11 @@ Management Commands
* The :djadminopt:`--no-color` option for ``django-admin.py`` allows you to * The :djadminopt:`--no-color` option for ``django-admin.py`` allows you to
disable the colorization of management command output. disable the colorization of management command output.
* The new :djadminopt:`--natural-foreign` and :djadminopt:`--natural-primary`
options for :djadmin:`dumpdata`, and the new ``use_natural_foreign_keys`` and
``use_natural_primary_keys`` arguments for ``serializers.serialize()``, allow
the use of natural primary keys when serializing.
Models Models
^^^^^^ ^^^^^^
...@@ -588,3 +593,12 @@ The :class:`django.db.models.IPAddressField` and ...@@ -588,3 +593,12 @@ The :class:`django.db.models.IPAddressField` and
The ``BaseMemcachedCache._get_memcache_timeout()`` method has been renamed to The ``BaseMemcachedCache._get_memcache_timeout()`` method has been renamed to
``get_backend_timeout()``. Despite being a private API, it will go through the ``get_backend_timeout()``. Despite being a private API, it will go through the
normal deprecation. normal deprecation.
Natural key serialization options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``--natural`` and ``-n`` options for :djadmin:`dumpdata` have been
deprecated. Use :djadminopt:`--natural-foreign` instead.
Similarly, the ``use_natural_keys`` argument for ``serializers.serialize()``
has been deprecated. Use ``use_natural_foreign_keys`` instead.
...@@ -404,6 +404,12 @@ into the primary key of an actual ``Person`` object. ...@@ -404,6 +404,12 @@ into the primary key of an actual ``Person`` object.
fields will be effectively unique, you can still use those fields fields will be effectively unique, you can still use those fields
as a natural key. as a natural key.
.. versionadded:: 1.7
Deserialization of objects with no primary key will always check whether the
model's manager has a ``get_by_natural_key()`` method and if so, use it to
populate the deserialized object's primary key.
Serialization of natural keys Serialization of natural keys
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -426,17 +432,39 @@ Firstly, you need to add another method -- this time to the model itself:: ...@@ -426,17 +432,39 @@ Firstly, you need to add another method -- this time to the model itself::
That method should always return a natural key tuple -- in this That method should always return a natural key tuple -- in this
example, ``(first name, last name)``. Then, when you call example, ``(first name, last name)``. Then, when you call
``serializers.serialize()``, you provide a ``use_natural_keys=True`` ``serializers.serialize()``, you provide ``use_natural_foreign_keys=True``
argument:: or ``use_natural_primary_keys=True`` arguments::
>>> serializers.serialize('json', [book1, book2], indent=2,
... use_natural_foreign_keys=True, use_natural_primary_keys=True)
When ``use_natural_foreign_keys=True`` is specified, Django will use the
``natural_key()`` method to serialize any foreign key reference to objects
of the type that defines the method.
>>> serializers.serialize('json', [book1, book2], indent=2, use_natural_keys=True) When ``use_natural_primary_keys=True`` is specified, Django will not provide the
primary key in the serialized data of this object since it can be calculated
during deserialization::
...
{
"model": "store.person",
"fields": {
"first_name": "Douglas",
"last_name": "Adams",
"birth_date": "1952-03-11",
}
}
...
When ``use_natural_keys=True`` is specified, Django will use the This can be useful when you need to load serialized data into an existing
``natural_key()`` method to serialize any reference to objects of the database and you cannot guarantee that the serialized primary key value is not
type that defines the method. already in use, and do not need to ensure that deserialized objects retain the
same primary keys.
If you are using :djadmin:`dumpdata` to generate serialized data, you If you are using :djadmin:`dumpdata` to generate serialized data, use the
use the :djadminopt:`--natural` command line flag to generate natural keys. :djadminopt:`--natural-foreign` and :djadminopt:`--natural-primary` command
line flags to generate natural keys.
.. note:: .. note::
...@@ -450,6 +478,19 @@ use the :djadminopt:`--natural` command line flag to generate natural keys. ...@@ -450,6 +478,19 @@ use the :djadminopt:`--natural` command line flag to generate natural keys.
natural keys during serialization, but *not* be able to load those natural keys during serialization, but *not* be able to load those
key values, just don't define the ``get_by_natural_key()`` method. key values, just don't define the ``get_by_natural_key()`` method.
.. versionchanged:: 1.7
Previously there was only a ``use_natural_keys`` argument for
``serializers.serialize()`` and the `-n` or `--natural` command line flags.
These have been deprecated in favor of the ``use_natural_foreign_keys`` and
``use_natural_primary_keys`` arguments and the corresponding
:djadminopt:`--natural-foreign` and :djadminopt:`--natural-primary` options
for :djadmin:`dumpdata`.
The original argument and command line flags remain for backwards
compatibility and map to the new ``use_natural_foreign_keys`` argument and
`--natural-foreign` command line flag. They'll be removed in Django 1.9.
Dependencies during serialization Dependencies during serialization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -459,7 +500,7 @@ a "forward reference" with natural keys -- the data you're referencing ...@@ -459,7 +500,7 @@ a "forward reference" with natural keys -- the data you're referencing
must exist before you include a natural key reference to that data. must exist before you include a natural key reference to that data.
To accommodate this limitation, calls to :djadmin:`dumpdata` that use To accommodate this limitation, calls to :djadmin:`dumpdata` that use
the :djadminopt:`--natural` option will serialize any model with a the :djadminopt:`--natural-foreign` option will serialize any model with a
``natural_key()`` method before serializing standard primary key objects. ``natural_key()`` method before serializing standard primary key objects.
However, this may not always be enough. If your natural key refers to However, this may not always be enough. If your natural key refers to
......
This diff is collapsed.
...@@ -546,12 +546,13 @@ class NaturalKeyFixtureTests(TestCase): ...@@ -546,12 +546,13 @@ class NaturalKeyFixtureTests(TestCase):
'fixtures_regress.store', 'fixtures_regress.store',
verbosity=0, verbosity=0,
format='json', format='json',
use_natural_keys=True, use_natural_foreign_keys=True,
use_natural_primary_keys=True,
stdout=stdout, stdout=stdout,
) )
self.assertJSONEqual( self.assertJSONEqual(
stdout.getvalue(), stdout.getvalue(),
"""[{"pk": 2, "model": "fixtures_regress.store", "fields": {"main": null, "name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"main": null, "name": "Borders"}}, {"pk": 4, "model": "fixtures_regress.person", "fields": {"name": "Neal Stephenson"}}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}]""" """[{"fields": {"main": null, "name": "Amazon"}, "model": "fixtures_regress.store"}, {"fields": {"main": null, "name": "Borders"}, "model": "fixtures_regress.store"}, {"fields": {"name": "Neal Stephenson"}, "model": "fixtures_regress.person"}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}]"""
) )
def test_dependency_sorting(self): def test_dependency_sorting(self):
......
...@@ -118,6 +118,7 @@ class NaturalKeyAnchor(models.Model): ...@@ -118,6 +118,7 @@ class NaturalKeyAnchor(models.Model):
objects = NaturalKeyAnchorManager() objects = NaturalKeyAnchorManager()
data = models.CharField(max_length=100, unique=True) data = models.CharField(max_length=100, unique=True)
title = models.CharField(max_length=100, null=True)
def natural_key(self): def natural_key(self):
return (self.data,) return (self.data,)
......
...@@ -11,6 +11,7 @@ from __future__ import unicode_literals ...@@ -11,6 +11,7 @@ from __future__ import unicode_literals
import datetime import datetime
import decimal import decimal
from unittest import expectedFailure, skipUnless from unittest import expectedFailure, skipUnless
import warnings
try: try:
import yaml import yaml
...@@ -476,9 +477,12 @@ def naturalKeySerializerTest(format, self): ...@@ -476,9 +477,12 @@ def naturalKeySerializerTest(format, self):
for klass in instance_count: for klass in instance_count:
instance_count[klass] = klass.objects.count() instance_count[klass] = klass.objects.count()
# Serialize the test database # use_natural_keys is deprecated and to be removed in Django 1.9
serialized_data = serializers.serialize(format, objects, indent=2, with warnings.catch_warnings(record=True) as w:
use_natural_keys=True) warnings.simplefilter("always")
# Serialize the test database
serialized_data = serializers.serialize(format, objects, indent=2,
use_natural_keys=True)
for obj in serializers.deserialize(format, serialized_data): for obj in serializers.deserialize(format, serialized_data):
obj.save() obj.save()
...@@ -523,6 +527,35 @@ def streamTest(format, self): ...@@ -523,6 +527,35 @@ def streamTest(format, self):
else: else:
self.assertEqual(string_data, stream.content.decode('utf-8')) self.assertEqual(string_data, stream.content.decode('utf-8'))
def naturalKeyTest(format, self):
book1 = {'data': '978-1590597255', 'title': 'The Definitive Guide to '
'Django: Web Development Done Right'}
book2 = {'data':'978-1590599969', 'title': 'Practical Django Projects'}
# Create the books.
adrian = NaturalKeyAnchor.objects.create(**book1)
james = NaturalKeyAnchor.objects.create(**book2)
# Serialize the books.
string_data = serializers.serialize(format, NaturalKeyAnchor.objects.all(),
indent=2, use_natural_foreign_keys=True,
use_natural_primary_keys=True)
# Delete one book (to prove that the natural key generation will only
# restore the primary keys of books found in the database via the
# get_natural_key manager method).
james.delete()
# Deserialize and test.
books = list(serializers.deserialize(format, string_data))
self.assertEqual(len(books), 2)
self.assertEqual(books[0].object.title, book1['title'])
self.assertEqual(books[0].object.pk, adrian.pk)
self.assertEqual(books[1].object.title, book2['title'])
self.assertEqual(books[1].object.pk, None)
for format in [ for format in [
f for f in serializers.get_serializer_formats() f for f in serializers.get_serializer_formats()
if not isinstance(serializers.get_serializer(f), serializers.BadSerializer) if not isinstance(serializers.get_serializer(f), serializers.BadSerializer)
...@@ -530,6 +563,7 @@ for format in [ ...@@ -530,6 +563,7 @@ for format in [
setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format))
setattr(SerializerTests, 'test_' + format + '_natural_key_serializer', curry(naturalKeySerializerTest, format)) setattr(SerializerTests, 'test_' + format + '_natural_key_serializer', curry(naturalKeySerializerTest, format))
setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))
setattr(SerializerTests, 'test_' + format + '_serializer_natural_keys', curry(naturalKeyTest, format))
if format != 'python': if format != 'python':
setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment