Ticket #13252: 13252-natural-key-serializing-r17262.diff

File 13252-natural-key-serializing-r17262.diff, 39.7 KB (added by Claude Paroz, 8 years ago)

Updated to current trunk

  • django/core/management/commands/dumpdata.py

    diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py
    index 1622929..db0a5bc 100644
    a b class Command(BaseCommand): 
    1919            help='An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).'),
    2020        make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False,
    2121            help='Use natural keys if they are available.'),
     22        make_option('--natural-foreign', action='store_true', dest='use_natural_foreign_keys', default=False,
     23            help='Use natural foreign keys if they are available.'),
     24        make_option('--natural-primary', action='store_true', dest='use_natural_primary_keys', default=False,
     25            help='Use natural primary keys if they are available.'),
    2226        make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False,
    2327            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."),
    2428    )
    class Command(BaseCommand): 
    3640        excludes = options.get('exclude')
    3741        show_traceback = options.get('traceback')
    3842        use_natural_keys = options.get('use_natural_keys')
     43        if use_natural_keys:
     44            # raise pending deprecation warning.
     45            pass
     46        use_natural_foreign_keys = options.get('use_natural_foreign_keys') or use_natural_keys
     47        use_natural_primary_keys = options.get('use_natural_primary_keys')
    3948        use_base_manager = options.get('use_base_manager')
    4049
    4150        excluded_apps = set()
    class Command(BaseCommand): 
    110119
    111120        try:
    112121            return serializers.serialize(format, objects, indent=indent,
    113                         use_natural_keys=use_natural_keys)
     122                        use_natural_foreign_keys=use_natural_foreign_keys,
     123                        use_natural_primary_keys=use_natural_primary_keys)
    114124        except Exception, e:
    115125            if show_traceback:
    116126                raise
  • django/core/serializers/base.py

    diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
    index c7e6226..3b769e3 100644
    a b class Serializer(object): 
    3737        self.stream = options.pop("stream", StringIO())
    3838        self.selected_fields = options.pop("fields", None)
    3939        self.use_natural_keys = options.pop("use_natural_keys", False)
     40        if self.use_natural_keys:
     41            # raise pending deprecation warning.
     42            pass
     43        self.use_natural_foreign_keys = options.pop('use_natural_foreign_keys', False)
     44        self.use_natural_primary_keys = options.pop('use_natural_primary_keys', False)
    4045
    4146        self.start_serialization()
    4247        for obj in queryset:
    class DeserializedObject(object): 
    167172        # prevent a second (possibly accidental) call to save() from saving
    168173        # the m2m data twice.
    169174        self.m2m_data = None
     175
     176def build_instance(Model, data, db):
     177    """
     178    Build a model instance.
     179
     180    If the model instance doesn't have a primary key and the model supports
     181    natural keys, try to retrieve it from the database.
     182    """
     183    obj = Model(**data)
     184    if obj.pk is None and hasattr(Model, 'natural_key') and\
     185            hasattr(Model._default_manager, 'get_by_natural_key'):
     186        pk = obj.natural_key()
     187        try:
     188            obj.pk = Model._default_manager.db_manager(db)\
     189                                           .get_by_natural_key(*pk).pk
     190        except Model.DoesNotExist:
     191            pass
     192    return obj
  • django/core/serializers/python.py

    diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
    index a68ea21..7b839f0 100644
    a b class Serializer(base.Serializer): 
    2727        self._current = {}
    2828
    2929    def end_object(self, obj):
    30         self.objects.append({
    31             "model"  : smart_unicode(obj._meta),
    32             "pk"     : smart_unicode(obj._get_pk_val(), strings_only=True),
    33             "fields" : self._current
    34         })
     30        if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
     31            data = {
     32                "model": smart_unicode(obj._meta),
     33                "pk": smart_unicode(obj._get_pk_val(), strings_only=True),
     34                "fields": self._current,
     35            }
     36        else:
     37            data = {
     38                "model": smart_unicode(obj._meta),
     39                "fields": self._current
     40            }
     41        self.objects.append(data)
    3542        self._current = None
    3643
    3744    def handle_field(self, obj, field):
    class Serializer(base.Serializer): 
    4754    def handle_fk_field(self, obj, field):
    4855        related = getattr(obj, field.name)
    4956        if related is not None:
    50             if self.use_natural_keys and hasattr(related, 'natural_key'):
     57            if self.use_natural_foreign_keys and hasattr(related, 'natural_key'):
    5158                related = related.natural_key()
    5259            else:
    5360                if field.rel.field_name == related._meta.pk.name:
    class Serializer(base.Serializer): 
    6067
    6168    def handle_m2m_field(self, obj, field):
    6269        if field.rel.through._meta.auto_created:
    63             if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
     70            if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
    6471                m2m_value = lambda value: value.natural_key()
    6572            else:
    6673                m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True)
    def Deserializer(object_list, **options): 
    8289    for d in object_list:
    8390        # Look up the model and starting build a dict of data for it.
    8491        Model = _get_model(d["model"])
    85         data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
     92        data = {}
     93        if 'pk' in d:
     94            data[Model._meta.pk.attname] = Model._meta.pk.to_python(d['pk'])
    8695        m2m_data = {}
    8796
    8897        # Handle each field
    def Deserializer(object_list, **options): 
    127136            else:
    128137                data[field.name] = field.to_python(field_value)
    129138
    130         yield base.DeserializedObject(Model(**data), m2m_data)
     139        obj = base.build_instance(Model, data, db)
     140
     141        yield base.DeserializedObject(obj, m2m_data)
    131142
    132143def _get_model(model_identifier):
    133144    """
  • django/core/serializers/xml_serializer.py

    diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
    index bcf5631..22205d9 100644
    a b class Serializer(base.Serializer): 
    4242            raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
    4343
    4444        self.indent(1)
    45         obj_pk = obj._get_pk_val()
    46         if obj_pk is None:
    47             attrs = {"model": smart_unicode(obj._meta),}
    48         else:
    49             attrs = {
    50                 "pk": smart_unicode(obj._get_pk_val()),
    51                 "model": smart_unicode(obj._meta),
    52             }
    53 
    54         self.xml.startElement("object", attrs)
     45        object_data = {"model": smart_unicode(obj._meta)}
     46        if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
     47            obj_pk = obj._get_pk_val()
     48            if obj_pk is not None:
     49                object_data['pk'] = smart_unicode(obj_pk)
     50        self.xml.startElement("object", object_data)
    5551
    5652    def end_object(self, obj):
    5753        """
    class Serializer(base.Serializer): 
    8783        self._start_relational_field(field)
    8884        related = getattr(obj, field.name)
    8985        if related is not None:
    90             if self.use_natural_keys and hasattr(related, 'natural_key'):
     86            if self.use_natural_foreign_keys and hasattr(related, 'natural_key'):
    9187                # If related object has a natural key, use it
    9288                related = related.natural_key()
    9389                # Iterable natural keys are rolled out as subelements
    class Serializer(base.Serializer): 
    115111        """
    116112        if field.rel.through._meta.auto_created:
    117113            self._start_relational_field(field)
    118             if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
     114            if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
    119115                # If the objects in the m2m have a natural key, use it
    120116                def handle_m2m(value):
    121117                    natural = value.natural_key()
    class Deserializer(base.Deserializer): 
    173169        Model = self._get_model_from_node(node, "model")
    174170
    175171        # Start building a data dictionary from the object.
    176         # If the node is missing the pk set it to None
    177         if node.hasAttribute("pk"):
    178             pk = node.getAttribute("pk")
    179         else:
    180             pk = None
    181 
    182         data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}
     172        data = {}
     173        if node.hasAttribute('pk'):
     174            data[Model._meta.pk.attname] = Model._meta.pk.to_python(
     175                                                    node.getAttribute('pk'))
    183176
    184177        # Also start building a dict of m2m data (this is saved as
    185178        # {m2m_accessor_attribute : [list_of_related_objects]})
    class Deserializer(base.Deserializer): 
    210203                    value = field.to_python(getInnerText(field_node).strip())
    211204                data[field.name] = value
    212205
     206        obj = base.build_instance(Model, data, self.db)
     207
    213208        # Return a DeserializedObject so that the m2m data has a place to live.
    214         return base.DeserializedObject(Model(**data), m2m_data)
     209        return base.DeserializedObject(obj, m2m_data)
    215210
    216211    def _handle_fk_field_node(self, node, field):
    217212        """
  • docs/topics/serialization.txt

    diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt
    index e6a94ca..25d998c 100644
    a b into the primary key of an actual ``Person`` object. 
    313313    fields will be effectively unique, you can still use those fields
    314314    as a natural key.
    315315
     316.. versionadded:: 1.4
     317
     318Deserialization of objects with no primary key will always check whether the
     319model's manager has a ``get_by_natural_key()`` method and if so, use it to
     320populate the deserialized object's primary key.
     321
    316322Serialization of natural keys
    317323~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    318324
    Firstly, you need to add another method -- this time to the model itself:: 
    335341
    336342That method should always return a natural key tuple -- in this
    337343example, ``(first name, last name)``. Then, when you call
    338 ``serializers.serialize()``, you provide a ``use_natural_keys=True``
    339 argument::
    340 
    341     >>> serializers.serialize('json', [book1, book2], indent=2, use_natural_keys=True)
    342 
    343 When ``use_natural_keys=True`` is specified, Django will use the
    344 ``natural_key()`` method to serialize any reference to objects of the
    345 type that defines the method.
     344``serializers.serialize()``, you provide ``use_natural_foreign_keys=True``
     345or ``use_natural_primary_keys=True`` arguments::
     346
     347    >>> serializers.serialize('json', [book1, book2], indent=2, use_natural_foreign_keys=True, use_natural_primary_keys=True)
     348
     349When ``use_natural_foreign_keys=True`` is specified, Django will use the
     350``natural_key()`` method to serialize any foreign key reference to objects
     351of the type that defines the method.
     352
     353When ``use_natural_primary_keys=True``is specified, Django will not provide the
     354primary key in the serialized data of this object since it can be calculated
     355during deserialization::
     356
     357        ...
     358        {
     359            "model": "store.person",
     360            "fields": {
     361                "first_name": "Douglas",
     362                "last_name": "Adams",
     363                "birth_date": "1952-03-11",
     364            }
     365        }
     366        ...
     367
     368This can be useful when you need to load serialized data into an existing
     369database and you cannot guarantee that the serialized primary key value is not
     370already in use, and do not need to ensure that deserialized objects retain the
     371same primary keys.
    346372
    347373If you are using :djadmin:`dumpdata` to generate serialized data, you
    348 use the `--natural` command line flag to generate natural keys.
     374use the `--natural-foreign` and `--natural-primary` command line flags to
     375generate natural keys.
    349376
    350377.. note::
    351378
    use the `--natural` command line flag to generate natural keys. 
    359386    natural keys during serialization, but *not* be able to load those
    360387    key values, just don't define the ``get_by_natural_key()`` method.
    361388
     389.. versionchanged:: 1.4
     390
     391Previously there was only a ``use_natural_keys`` argument for
     392``serializers.serialize()`` and the `-n` or `--natural` command line flags.
     393These have been deprecated in favor of the ``use_natural_foreign_keys`` and
     394``use_natural_primary_keys`` arguments, and the corresponding
     395`--natural-foreign` and `--natural-primary` command line flags.
     396
     397The original argument and command line flags remain for backwards
     398compatibility, and map to the new ``use_natural_foreign_keys`` argument and
     399`--natural-foreign` command line flag.
     400
    362401Dependencies during serialization
    363402~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    364403
  • tests/modeltests/fixtures/tests.py

    diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py
    index d22010d..7d4d62f 100644
    a b class TestCaseFixtureLoadingTests(TestCase): 
    2424
    2525class FixtureLoadingTests(TestCase):
    2626
    27     def _dumpdata_assert(self, args, output, format='json', natural_keys=False,
     27    def _dumpdata_assert(self, args, output, format='json',
     28                         natural_foreign_keys=False, natural_primary_keys=False,
    2829                         use_base_manager=False, exclude_list=[]):
    2930        new_io = StringIO.StringIO()
    3031        management.call_command('dumpdata', *args, **{'format':format,
    3132                                                      'stdout':new_io,
    3233                                                      'stderr':new_io,
    33                                                       'use_natural_keys':natural_keys,
     34                                                      'use_natural_foreign_keys':natural_foreign_keys,
     35                                                      'use_natural_primary_keys':natural_primary_keys,
    3436                                                      'use_base_manager':use_base_manager,
    3537                                                      'exclude': exclude_list})
    3638        command_output = new_io.getvalue().strip()
    class FixtureLoadingTests(TestCase): 
    146148        self._dumpdata_assert(['fixtures.book'], '[{"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}]')
    147149
    148150        # But you can get natural keys if you ask for them and they are available
    149         self._dumpdata_assert(['fixtures.book'], '[{"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
     151        self._dumpdata_assert(['fixtures.book'], '[{"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_foreign_keys=True)
     152
     153        # You can also omit the primary keys for models that we can get later with natural keys.
     154        self._dumpdata_assert(['fixtures.person'], '[{"fields": {"name": "Artist formerly known as \\"Prince\\""}, "model": "fixtures.person"}, {"fields": {"name": "Django Reinhardt"}, "model": "fixtures.person"}, {"fields": {"name": "Stephane Grappelli"}, "model": "fixtures.person"}]', natural_primary_keys=True)
    150155
    151156        # Dump the current contents of the database as a JSON fixture
    152         self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16T16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16T15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16T11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
     157        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16T16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16T15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16T11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_foreign_keys=True)
    153158
    154159        # Dump the current contents of the database as an XML fixture
    155160        self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
    156 <django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16T16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16T15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16T14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16T11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="10" model="fixtures.book"><field type="CharField" name="name">Achieving self-awareness of Python programs</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True)
     161<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16T16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16T15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16T14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16T11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="10" model="fixtures.book"><field type="CharField" name="name">Achieving self-awareness of Python programs</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_foreign_keys=True)
    157162
    158163    def test_dumpdata_with_excludes(self):
    159164        # Load fixture1 which has a site, two articles, and a category
    class FixtureLoadingTests(TestCase): 
    292297        ])
    293298
    294299        # Dump the current contents of the database as a JSON fixture
    295         self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}]', natural_keys=True)
     300        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}]', natural_foreign_keys=True)
    296301
    297302        # Dump the current contents of the database as an XML fixture
    298303        self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
    299 <django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16T13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16T12:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="10" model="fixtures.book"><field type="CharField" name="name">Achieving self-awareness of Python programs</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"></field></object></django-objects>""", format='xml', natural_keys=True)
     304<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16T13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16T12:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="10" model="fixtures.book"><field type="CharField" name="name">Achieving self-awareness of Python programs</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"></field></object></django-objects>""", format='xml', natural_foreign_keys=True)
    300305
    301306class FixtureTransactionTests(TransactionTestCase):
    302307    def _dumpdata_assert(self, args, output, format='json'):
  • tests/regressiontests/fixtures_regress/tests.py

    diff --git a/tests/regressiontests/fixtures_regress/tests.py b/tests/regressiontests/fixtures_regress/tests.py
    index 5b8dc66..8101cd9 100644
    a b class NaturalKeyFixtureTests(TestCase): 
    499499            'fixtures_regress.store',
    500500            verbosity=0,
    501501            format='json',
    502             use_natural_keys=True,
     502            use_natural_foreign_keys=True,
     503            use_natural_primary_keys=True,
    503504            stdout=stdout,
    504505        )
    505506        self.assertEqual(
    506507            stdout.getvalue(),
    507             """[{"pk": 2, "model": "fixtures_regress.store", "fields": {"name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"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"]}}]"""
     508            """[{"fields": {"name": "Amazon"}, "model": "fixtures_regress.store"}, {"fields": {"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"]}}]"""
    508509        )
    509510
    510511    def test_dependency_sorting(self):
  • tests/regressiontests/serializers_regress/models.py

    diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py
    index b3ae1fe..780c0cd 100644
    a b class LengthModel(models.Model): 
    264264
    265265    def __len__(self):
    266266        return self.data
     267
     268#Tests for natural keys.
     269class BookManager(models.Manager):
     270    def get_by_natural_key(self, isbn13):
     271        return self.get(isbn13=isbn13)
     272
     273class Book(models.Model):
     274    isbn13 = models.CharField(max_length=14)
     275    title = models.CharField(max_length=100)
     276
     277    objects = BookManager()
     278
     279    def natural_key(self):
     280        return (self.isbn13,)
  • tests/regressiontests/serializers_regress/tests.py

    diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py
    index 9c9022d..6653d92 100644
    a b from .models import (BooleanData, CharData, DateData, DateTimeData, EmailData, 
    3535    PositiveSmallIntegerPKData, SlugPKData, SmallPKData, USStatePKData,
    3636    AutoNowDateTimeData, ModifyingSaveData, InheritAbstractModel,
    3737    ExplicitInheritBaseModel, InheritBaseModel, BigIntegerData, LengthModel,
    38     Tag, ComplexModel)
     38    Tag, ComplexModel, Book)
    3939
    4040# A set of functions that can be used to recreate
    4141# test data objects of various kinds.
    def streamTest(format, self): 
    450450    self.assertEqual(string_data, stream.getvalue())
    451451    stream.close()
    452452
     453def naturalKeyTest(format, self):
     454    book1 = {'isbn13': '978-1590597255', 'title': 'The Definitive Guide to '
     455             'Django: Web Development Done Right'}
     456    book2 = {'isbn13':'978-1590599969', 'title': 'Practical Django Projects'}
     457
     458    # Create the books.
     459    adrian = Book.objects.create(**book1)
     460    james = Book.objects.create(**book2)
     461
     462    # Serialize the books.
     463    string_data = serializers.serialize(format, Book.objects.all(), indent=2,
     464                                        use_natural_foreign_keys=True,
     465                                        use_natural_primary_keys=True)
     466
     467    # Delete one book (to prove that the natural key generation will only
     468    # restore the primary keys of books found in the database via the
     469    # get_natural_key manager method).
     470    james.delete()
     471
     472    # Deserialize and test.
     473    books = list(serializers.deserialize(format, string_data))
     474    self.assertEqual(len(books), 2)
     475    self.assertEqual(books[0].object.title, book1['title'])
     476    self.assertEqual(books[0].object.pk, adrian.pk)
     477    self.assertEqual(books[1].object.title, book2['title'])
     478    self.assertEqual(books[1].object.pk, None)
     479
    453480for format in serializers.get_serializer_formats():
    454481    setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format))
    455482    setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))
     483    setattr(SerializerTests, 'test_' + format + '_serializer_natural_keys', curry(naturalKeyTest, format))
    456484    if format != 'python':
    457485        setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
Back to Top