diff -r f266dfb70b06 django/contrib/auth/models.py
--- a/django/contrib/auth/models.py	Wed Nov 04 11:38:06 2009 +0000
+++ b/django/contrib/auth/models.py	Thu Dec 03 22:24:52 2009 +0800
@@ -47,6 +47,14 @@
 class SiteProfileNotAvailable(Exception):
     pass
 
+class PermissionManager(models.Manager):
+    def get_by_surrogate(self, key):
+        codename, ct_key = key.split('|',1)
+        return self.get(
+            codename=codename,
+            content_type=ContentType.objects.get_by_surrogate(ct_key)
+        )
+
 class Permission(models.Model):
     """The permissions system provides a way to assign permissions to specific users and groups of users.
 
@@ -63,6 +71,7 @@
     name = models.CharField(_('name'), max_length=50)
     content_type = models.ForeignKey(ContentType)
     codename = models.CharField(_('codename'), max_length=100)
+    objects = PermissionManager()
 
     class Meta:
         verbose_name = _('permission')
@@ -76,6 +85,9 @@
             unicode(self.content_type),
             unicode(self.name))
 
+    def surrogate_key(self):
+        return '%s|%s' % (self.codename, self.content_type.surrogate_key())
+
 class Group(models.Model):
     """Groups are a generic way of categorizing users to apply permissions, or some other label, to those users. A user can belong to any number of groups.
 
diff -r f266dfb70b06 django/contrib/contenttypes/models.py
--- a/django/contrib/contenttypes/models.py	Wed Nov 04 11:38:06 2009 +0000
+++ b/django/contrib/contenttypes/models.py	Thu Dec 03 22:24:52 2009 +0800
@@ -8,6 +8,14 @@
     # This cache is shared by all the get_for_* methods.
     _cache = {}
 
+    def get_by_surrogate(self, key):
+        try:
+            app_label, model = key.split(':')
+            ct = self.__class__._cache[(app_label, model)]
+        except KeyError:
+            ct = self.get(app_label=app_label, model=model)
+        return ct
+
     def get_for_model(self, model):
         """
         Returns the ContentType object for a given model, creating the
@@ -93,3 +101,6 @@
         so code that calls this method should catch it.
         """
         return self.model_class()._default_manager.get(**kwargs)
+
+    def surrogate_key(self):
+        return "%s:%s" % (self.app_label, self.model)
diff -r f266dfb70b06 django/core/serializers/python.py
--- a/django/core/serializers/python.py	Wed Nov 04 11:38:06 2009 +0000
+++ b/django/core/serializers/python.py	Thu Dec 03 22:24:52 2009 +0800
@@ -47,17 +47,24 @@
     def handle_fk_field(self, obj, field):
         related = getattr(obj, field.name)
         if related is not None:
-            if field.rel.field_name == related._meta.pk.name:
-                # Related to remote object via primary key
-                related = related._get_pk_val()
+            if hasattr(related, 'surrogate_key'):
+                related = related.surrogate_key()
             else:
-                # Related to remote object via other field
-                related = getattr(related, field.rel.field_name)
+                if field.rel.field_name == related._meta.pk.name:
+                    # Related to remote object via primary key
+                    related = related._get_pk_val()
+                else:
+                    # Related to remote object via other field
+                    related = getattr(related, field.rel.field_name)
         self._current[field.name] = smart_unicode(related, strings_only=True)
 
     def handle_m2m_field(self, obj, field):
         if field.rel.through._meta.auto_created:
-            self._current[field.name] = [smart_unicode(related._get_pk_val(), strings_only=True)
+            if hasattr(field.rel.to, 'surrogate_key'):
+                m2m_value = lambda value: value.surrogate_key()
+            else:
+                m2m_value = lambda value: value._get_pk_val()
+            self._current[field.name] = [smart_unicode(m2m_value(related), strings_only=True)
                                for related in getattr(obj, field.name).iterator()]
 
     def getvalue(self):
@@ -86,13 +93,19 @@
 
             # Handle M2M relations
             if field.rel and isinstance(field.rel, models.ManyToManyRel):
-                m2m_convert = field.rel.to._meta.pk.to_python
+                if hasattr(field.rel.to._default_manager, 'get_by_surrogate'):
+                    m2m_convert = lambda value: field.rel.to._default_manager.get_by_surrogate(value).pk
+                else:
+                    m2m_convert = field.rel.to._meta.pk.to_python
                 m2m_data[field.name] = [m2m_convert(smart_unicode(pk)) for pk in field_value]
 
             # Handle FK fields
             elif field.rel and isinstance(field.rel, models.ManyToOneRel):
                 if field_value is not None:
-                    data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+                    if hasattr(field.rel.to._default_manager, 'get_by_surrogate'):
+                        data[field.attname] = field.rel.to._default_manager.get_by_surrogate(field_value).pk
+                    else:
+                        data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
                 else:
                     data[field.attname] = None
 
diff -r f266dfb70b06 django/core/serializers/xml_serializer.py
--- a/django/core/serializers/xml_serializer.py	Wed Nov 04 11:38:06 2009 +0000
+++ b/django/core/serializers/xml_serializer.py	Thu Dec 03 22:24:52 2009 +0800
@@ -81,12 +81,15 @@
         self._start_relational_field(field)
         related = getattr(obj, field.name)
         if related is not None:
-            if field.rel.field_name == related._meta.pk.name:
-                # Related to remote object via primary key
-                related = related._get_pk_val()
+            if hasattr(related, 'surrogate_key'):
+                related = related.surrogate_key()
             else:
-                # Related to remote object via other field
-                related = getattr(related, field.rel.field_name)
+                if field.rel.field_name == related._meta.pk.name:
+                    # Related to remote object via primary key
+                    related = related._get_pk_val()
+                else:
+                    # Related to remote object via other field
+                    related = getattr(related, field.rel.field_name)
             self.xml.characters(smart_unicode(related))
         else:
             self.xml.addQuickElement("None")
@@ -100,8 +103,14 @@
         """
         if field.rel.through._meta.auto_created:
             self._start_relational_field(field)
+            if hasattr(field.rel.to, 'surrogate_key'):
+                m2m_value = lambda value: value.surrogate_key()
+                attr_name = 'surrogate'
+            else:
+                m2m_value = lambda value: value._get_pk_val()
+                attr_name = 'pk'
             for relobj in getattr(obj, field.name).iterator():
-                self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())})
+                self.xml.addQuickElement("object", attrs={attr_name : smart_unicode(m2m_value(relobj))})
             self.xml.endElement("field")
 
     def _start_relational_field(self, field):
@@ -187,15 +196,23 @@
         if node.getElementsByTagName('None'):
             return None
         else:
-            return field.rel.to._meta.get_field(field.rel.field_name).to_python(
-                       getInnerText(node).strip())
+            field_value = getInnerText(node).strip()
+            if hasattr(field.rel.to._default_manager, 'get_by_surrogate'):
+                return field.rel.to._default_manager.get_by_surrogate(field_value).pk
+            else:
+                return field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
 
     def _handle_m2m_field_node(self, node, field):
         """
         Handle a <field> node for a ManyToManyField.
         """
-        return [field.rel.to._meta.pk.to_python(
-                    c.getAttribute("pk"))
+        if hasattr(field.rel.to._default_manager, 'get_by_surrogate'):
+            m2m_convert = lambda value: field.rel.to._default_manager.get_by_surrogate(value).pk
+            attr_name = 'surrogate'
+        else:
+            m2m_convert = field.rel.to._meta.pk.to_python
+            attr_name = 'pk'
+        return [m2m_convert(c.getAttribute(attr_name))
                     for c in node.getElementsByTagName("object")]
 
     def _get_model_from_node(self, node, attr):
diff -r f266dfb70b06 tests/modeltests/fixtures/fixtures/fixture6.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/fixtures/fixtures/fixture6.json	Thu Dec 03 22:24:52 2009 +0800
@@ -0,0 +1,20 @@
+[
+    {
+        "pk": "1",
+        "model": "fixtures.tag",
+        "fields": {
+            "name": "copyright",
+            "tagged_type": "fixtures:article",
+            "tagged_id": "3"
+        }
+    },
+    {
+        "pk": "2",
+        "model": "fixtures.tag",
+        "fields": {
+            "name": "law",
+            "tagged_type": "fixtures:article",
+            "tagged_id": "3"
+        }
+    }
+]
diff -r f266dfb70b06 tests/modeltests/fixtures/fixtures/fixture7.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/fixtures/fixtures/fixture7.xml	Thu Dec 03 22:24:52 2009 +0800
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="2" model="fixtures.tag">
+        <field type="CharField" name="name">legal</field>
+        <field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel">
+            fixtures:article
+        </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">
+            fixtures:article
+        </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">
+            fixtures:article
+        </field>
+        <field type="PositiveIntegerField" name="tagged_id">4</field>
+    </object>
+</django-objects>
diff -r f266dfb70b06 tests/modeltests/fixtures/fixtures/fixture8.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/fixtures/fixtures/fixture8.json	Thu Dec 03 22:24:52 2009 +0800
@@ -0,0 +1,32 @@
+[
+    {
+    	"pk": "1",
+    	"model": "fixtures.visa",
+    	"fields": {
+    	    "name": "Django Reinhardt",
+    	    "permissions": [
+    	        "add_user|auth:user",
+    	        "change_user|auth:user",
+    	        "delete_user|auth:user"
+    	    ]
+    	}
+    },
+    {
+    	"pk": "2",
+    	"model": "fixtures.visa",
+    	"fields": {
+    	    "name": "Stephane Grappelli",
+    	    "permissions": [
+    	        "add_user|auth:user"
+    	    ]
+    	}
+    },
+        {
+    	"pk": "3",
+    	"model": "fixtures.visa",
+    	"fields": {
+    	    "name": "Prince",
+    	    "permissions": []
+    	}
+    }
+]
diff -r f266dfb70b06 tests/modeltests/fixtures/fixtures/fixture9.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/fixtures/fixtures/fixture9.xml	Thu Dec 03 22:24:52 2009 +0800
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="2" model="fixtures.visa">
+      <field type="CharField" name="name">Stephane Grappelli</field>
+      <field to="auth.permission" name="permissions" rel="ManyToManyRel">
+          <object surrogate="add_user|auth:user"></object>
+          <object surrogate="delete_user|auth:user"></object>
+      </field>
+    </object>
+    <object pk="3" model="fixtures.visa">
+      <field type="CharField" name="name">Artist formerly known as &quot;Prince&quot;</field>
+      <field to="auth.permission" name="permissions" rel="ManyToManyRel"></field>
+    </object>
+</django-objects>
diff -r f266dfb70b06 tests/modeltests/fixtures/models.py
--- a/tests/modeltests/fixtures/models.py	Wed Nov 04 11:38:06 2009 +0000
+++ b/tests/modeltests/fixtures/models.py	Thu Dec 03 22:24:52 2009 +0800
@@ -8,6 +8,9 @@
 ``FIXTURE_DIRS`` setting.
 """
 
+from django.contrib.auth.models import Permission
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.conf import settings
 
@@ -31,6 +34,35 @@
     class Meta:
         ordering = ('-pub_date', 'headline')
 
+class Blog(models.Model):
+    name = models.CharField(max_length=100)
+    featured = models.ForeignKey(Article, related_name='fixtures_featured_set')
+    articles = models.ManyToManyField(Article, blank=True,
+                                      related_name='fixtures_articles_set')
+
+    def __unicode__(self):
+        return self.name
+
+
+class Tag(models.Model):
+    name = models.CharField(max_length=100)
+    tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set")
+    tagged_id = models.PositiveIntegerField(default=0)
+    tagged = generic.GenericForeignKey(ct_field='tagged_type',
+                                       fk_field='tagged_id')
+
+    def __unicode__(self):
+        return '<%s: %s> tagged "%s"' % (self.tagged.__class__.__name__,
+                                         self.tagged, self.name)
+
+class Visa(models.Model):
+    name = models.CharField(max_length=100)
+    permissions = models.ManyToManyField(Permission, blank=True)
+
+    def __unicode__(self):
+        return '%s %s' % (self.name,
+                          ', '.join(p.name for p in self.permissions.all()))
+
 __test__ = {'API_TESTS': """
 >>> from django.core import management
 >>> from django.db.models import get_app
@@ -90,12 +122,41 @@
 >>> Article.objects.all()
 [<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>]
 
+# Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne.
+>>> management.call_command('loaddata', 'fixture6.json', verbosity=0)
+>>> Tag.objects.all()
+[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "law">]
+
+# Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne.
+>>> management.call_command('loaddata', 'fixture7.xml', verbosity=0)
+>>> Tag.objects.all()
+[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "legal">, <Tag: <Article: Django conquers world!> tagged "django">, <Tag: <Article: Django conquers world!> tagged "world domination">]
+
+# Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany.
+>>> management.call_command('loaddata', 'fixture8.json', verbosity=0)
+>>> Visa.objects.all()
+[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user>, <Visa: Prince >]
+
+# Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany.
+>>> management.call_command('loaddata', 'fixture9.xml', verbosity=0)
+>>> Visa.objects.all()
+[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user, Can delete user>, <Visa: Artist formerly known as "Prince" >]
+
 # Load a fixture that doesn't exist
 >>> management.call_command('loaddata', 'unknown.json', verbosity=0)
 
 # object list is unaffected
 >>> Article.objects.all()
 [<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>]
+
+# Dump the current contents of the database as a JSON fixture
+>>> management.call_command('dumpdata', 'fixtures', format='json')
+[{"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-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11: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": 1, "model": "fixtures.visa", "fields": {"name": "Django Reinhardt", "permissions": ["add_user|auth:user", "change_user|auth:user", "delete_user|auth:user"]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"name": "Stephane Grappelli", "permissions": ["add_user|auth:user", "delete_user|auth:user"]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"name": "Artist formerly known as \\"Prince\\"", "permissions": []}}]
+
+# Dump the current contents of the database as an XML fixture
+>>> management.call_command('dumpdata', 'fixtures', format='xml')
+<?xml version="1.0" encoding="utf-8"?>
+<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-16 16: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-16 15: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-16 14: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-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11: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">fixtures:article</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">fixtures:article</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">fixtures:article</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">fixtures:article</field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="1" model="fixtures.visa"><field type="CharField" name="name">Django Reinhardt</field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object surrogate="add_user|auth:user"></object><object surrogate="change_user|auth:user"></object><object surrogate="delete_user|auth:user"></object></field></object><object pk="2" model="fixtures.visa"><field type="CharField" name="name">Stephane Grappelli</field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object surrogate="add_user|auth:user"></object><object surrogate="delete_user|auth:user"></object></field></object><object pk="3" model="fixtures.visa"><field type="CharField" name="name">Artist formerly known as "Prince"</field><field to="auth.permission" name="permissions" rel="ManyToManyRel"></field></object></django-objects>
 """}
 
 # Database flushing does not work on MySQL with the default storage engine
@@ -159,6 +220,24 @@
 >>> management.call_command('loaddata', 'fixture5', verbosity=0) # doctest: +ELLIPSIS
 Multiple fixtures named 'fixture5' in '...fixtures'. Aborting.
 
+>>> management.call_command('flush', verbosity=0, interactive=False)
+
+# Load back in fixture 1, we need the articles from it
+>>> management.call_command('loaddata', 'fixture1', verbosity=0)
+
+# Try to load fixture 6 using format discovery
+>>> management.call_command('loaddata', 'fixture6', verbosity=0)
+>>> Tag.objects.all()
+[<Tag: <Article: Time to reform copyright> tagged "copyright">, <Tag: <Article: Time to reform copyright> tagged "law">]
+
+# Dump the current contents of the database as a JSON fixture
+>>> management.call_command('dumpdata', 'fixtures', format='json')
+[{"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-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11: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}}]
+
+# Dump the current contents of the database as an XML fixture
+>>> management.call_command('dumpdata', 'fixtures', format='xml')
+<?xml version="1.0" encoding="utf-8"?>
+<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-16 13: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-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11: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">fixtures:article</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">fixtures:article</field><field type="PositiveIntegerField" name="tagged_id">3</field></object></django-objects>
 """
 
 from django.test import TestCase
diff -r f266dfb70b06 tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json	Thu Dec 03 22:24:52 2009 +0800
@@ -0,0 +1,32 @@
+[
+    {
+        "pk": 1,
+        "model": "fixtures_regress.book",
+        "fields": {
+            "name": "Cryptonomicon",
+            "author": "Neal Stephenson",
+            "stores": ["Amazon", "Borders"]
+        }
+    },
+    {
+        "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"
+        }
+    }
+]
\ No newline at end of file
diff -r f266dfb70b06 tests/regressiontests/fixtures_regress/models.py
--- a/tests/regressiontests/fixtures_regress/models.py	Wed Nov 04 11:38:06 2009 +0000
+++ b/tests/regressiontests/fixtures_regress/models.py	Thu Dec 03 22:24:52 2009 +0800
@@ -13,7 +13,7 @@
     specimens = models.Manager()
 
     def __unicode__(self):
-        return self.common_name
+        return self.name
 
 def animal_pre_save_check(signal, sender, instance, **kwargs):
     "A signal that is used to check the type of data loaded from fixtures"
@@ -69,10 +69,56 @@
 class Widget(models.Model):
     name = models.CharField(max_length=255)
 
+    class Meta:
+        ordering = ('name',)
+
+    def __unicode__(self):
+        return self.name
+
 class WidgetProxy(Widget):
     class Meta:
         proxy = True
 
+# Check for forward references in FKs and M2Ms with surrogate keys
+
+class TestManager(models.Manager):
+    def get_by_surrogate(self, key):
+        return self.get(name=key)
+
+class Store(models.Model):
+    objects = TestManager()
+    name = models.CharField(max_length=255)
+
+    class Meta:
+        ordering = ('name',)
+
+    def __unicode__(self):
+        return self.name
+
+    def surrogate_key():
+        return self.name
+
+class Person(models.Model):
+    objects = TestManager()
+    name = models.CharField(max_length=255)
+
+    class Meta:
+        ordering = ('name',)
+
+    def __unicode__(self):
+        return self.name
+
+    def surrogate_key():
+        return self.name
+
+class Book(models.Model):
+    name = models.CharField(max_length=255)
+    author = models.ForeignKey(Person)
+    stores = models.ManyToManyField(Store)
+
+    class Meta:
+        ordering = ('name',)
+
 __test__ = {'API_TESTS':"""
 >>> from django.core import management
 
@@ -192,4 +238,15 @@
 >>> management.call_command('dumpdata', 'fixtures_regress', format='json')
 [{"pk": 1, "model": "fixtures_regress.widget", "fields": {"name": "grommet"}}]
 
+###############################################
+# Check that a fixture can contain forward references
+>>> management.call_command('loaddata', 'forward_ref_lookup', verbosity=0)
+
+>>> b = Book.objects.get(name="Cryptonomicon")
+>>> b.author
+<Person: Neal Stephenson>
+
+>>> b.stores.all()
+[<Store: Amazon>, <Store: Borders>]
+
 """}
