diff -r c273e74671a6 django/db/models/fields/related.py
--- a/django/db/models/fields/related.py	Thu Jul 03 18:42:47 2008 +0000
+++ b/django/db/models/fields/related.py	Mon Jul 07 17:33:52 2008 -0400
@@ -1,3 +1,4 @@
+import types
 from django.db import connection, transaction
 from django.db.models import signals, get_model
 from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class, FieldDoesNotExist
@@ -168,7 +169,10 @@
     # SingleRelatedObjectDescriptor instance.
     def __init__(self, related):
         self.related = related
-        self.cache_name = '_%s_cache' % related.get_accessor_name()
+        cache_name = '_%s_cache' % related.field.related_query_name()
+        # Contribute to the parent model for later lookup.
+        related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name
+        self.cache_name = cache_name
 
     def __get__(self, instance, instance_type=None):
         if instance is None:
@@ -274,6 +278,11 @@
     # attribute is a ForeignRelatedObjectsDescriptor instance.
     def __init__(self, related):
         self.related = related   # RelatedObject instance
+        if related.field.unique:
+            cache_name = '_%s_cache' % related.field.related_query_name()
+            # Contribute to the parent model for later lookup.
+            related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name
+            self.cache_name = cache_name
 
     def __get__(self, instance, instance_type=None):
         if instance is None:
@@ -281,6 +290,8 @@
 
         rel_field = self.related.field
         rel_model = self.related.model
+        if rel_field.unique:
+            cache_name = self.cache_name
 
         # Dynamically create a class that subclasses the related
         # model's default manager.
@@ -320,6 +331,17 @@
                         setattr(obj, rel_field.name, None)
                         obj.save()
                 clear.alters_data = True
+            
+            if rel_field.unique:
+                def all(self):
+                    try:
+                        result = getattr(instance, cache_name)
+                        if isinstance(result, (types.TupleType, types.ListType)):
+                            return result
+                        else:
+                            return [result]
+                    except AttributeError, ae:
+                        return superclass.get_query_set(self)
 
         manager = RelatedManager()
         attname = rel_field.rel.get_related_field().name
@@ -339,6 +361,9 @@
         if self.related.field.null:
             manager.clear()
         manager.add(*value)
+        # Cache the value specially if from a unique set.
+        if self.related.field.unique:
+            self.cache_name = value
 
 def create_many_related_manager(superclass):
     """Creates a manager that subclasses 'superclass' (which is a Manager)
diff -r c273e74671a6 django/db/models/options.py
--- a/django/db/models/options.py	Thu Jul 03 18:42:47 2008 +0000
+++ b/django/db/models/options.py	Mon Jul 07 17:33:52 2008 -0400
@@ -45,6 +45,7 @@
         self.abstract = False
         self.parents = SortedDict()
         self.duplicate_targets = {}
+        self.reverse_field_cache = {}
 
     def contribute_to_class(self, cls, name):
         from django.db import connection
diff -r c273e74671a6 django/db/models/query.py
--- a/django/db/models/query.py	Thu Jul 03 18:42:47 2008 +0000
+++ b/django/db/models/query.py	Mon Jul 07 17:33:52 2008 -0400
@@ -772,6 +772,29 @@
         if cached_row:
             rel_obj, index_end = cached_row
             setattr(obj, f.get_cache_name(), rel_obj)
+
+            # Do the reverse cache if the field is a unique relation.
+            if f.unique:
+                cache_var_name = rel_obj._meta.reverse_field_cache[f.related_query_name()]
+                setattr(rel_obj, cache_var_name, obj)
+
+    if restricted:
+        related_fields = [(x.field, x.model) for x in klass._meta.get_all_related_objects() if x.field.unique]
+        for f, model in related_fields:
+            if f.related_query_name() not in requested:
+                continue
+
+            next = requested.get(f.related_query_name(), {})
+            cached_row = get_cached_row(model, row, index_end, max_depth,
+                    cur_depth+1, next)
+            if cached_row:
+                rel_obj, index_end = cached_row
+                setattr(rel_obj, f.get_cache_name(), obj)
+
+                # Now do the reverse cache.
+                cache_var_name = obj._meta.reverse_field_cache[f.related_query_name()]
+                setattr(obj, cache_var_name, rel_obj)
+
     return obj, index_end
 
 def delete_objects(seen_objs):
diff -r c273e74671a6 django/db/models/sql/query.py
--- a/django/db/models/sql/query.py	Thu Jul 03 18:42:47 2008 +0000
+++ b/django/db/models/sql/query.py	Mon Jul 07 17:33:52 2008 -0400
@@ -977,14 +977,43 @@
                 next = requested.get(f.name, {})
             else:
                 next = False
-            if f.null is not None:
+            if nullable is not None:
+                new_nullable = nullable
+            else:
                 new_nullable = f.null
-            else:
-                new_nullable = None
             for dupe_opts, dupe_col in dupe_set:
                 self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
             self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
                     used, next, restricted, new_nullable, dupe_set)
+            
+        # Do reverse columns, but only the ones in the requested list.
+        if restricted:
+            related_fields = [(x.field, x.model) for x in opts.get_all_related_objects() if x.field.unique]
+            for f, model in related_fields:
+                if f.rel.parent_link or f.related_query_name() not in requested:
+                    continue
+                table = model._meta.db_table
+                if nullable or f.null:
+                    promote = True
+                else:
+                    promote = False
+                alias = self.join((root_alias, table, f.rel.get_related_field().column,
+                        f.column), exclusions=used,
+                        promote=promote, reuse=used)
+                used.add(alias)
+
+                self.related_select_cols.extend([(alias, f2.column)
+                        for f2 in model._meta.fields])
+                self.related_select_fields.extend(model._meta.fields)
+
+                next = requested.get(f.related_query_name(), {})
+                if nullable is not None:
+                    new_nullable = nullable
+                else:
+                    new_nullable = f.null
+                self.fill_related_selections(model._meta, table, cur_depth + 1,
+                        used, next, restricted, new_nullable)
+
 
     def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
             can_reuse=None):
diff -r c273e74671a6 tests/modeltests/select_related_reverse/models.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/select_related_reverse/models.py	Mon Jul 07 17:33:52 2008 -0400
@@ -0,0 +1,123 @@
+"""
+``select_related()`` follows all forward relationships, but should also follow
+reverse relationships where it is appropriate (currently for OneToOneFields 
+only).
+"""
+
+from django.db import models
+
+# OneToOneField tests only.
+
+class User(models.Model):
+    alias = models.CharField(max_length=20)
+    
+    def __unicode__(self):
+        return 'User, alias = %s' % (self.alias,)
+
+
+class UserInfo(models.Model):
+    user = models.OneToOneField(User, primary_key=True)
+    name = models.CharField(max_length=32)
+
+    def __unicode__(self):
+        return 'UserInfo, name = %s' % (self.name,)
+    
+
+class UserStatResults(models.Model):
+    results = models.CharField(max_length=50)
+
+    def __unicode__(self):
+        return 'UserStatResults, results = %s' % (self.results,)
+    
+
+class UserStat(models.Model):
+    user = models.OneToOneField(User, primary_key=True)
+    posts = models.IntegerField()
+    results = models.ForeignKey(UserStatResults)
+
+    def __unicode__(self):
+        return 'UserStat, posts = %s' % (self.posts,)
+
+
+class StatDetails(models.Model):
+    base_stats = models.OneToOneField(UserStat, primary_key=True)
+    comments = models.IntegerField()
+
+    def __unicode__(self):
+        return 'StatDetails, comments = %s' % (self.comments,)
+
+
+__test__ = {'API_TESTS':"""
+
+# Set up.
+# The test runner sets settings.DEBUG to False, but we want to gather queries
+# so we'll set it to True here and reset it at the end of the test suite.
+>>> from django.conf import settings
+>>> settings.DEBUG = True
+>>> from django import db
+
+>>> usr = UserStatResults.objects.create(results='first results')
+>>> u = User.objects.create(alias='tom')
+>>> ui = UserInfo.objects.create(user=u, name='Tom Jones')
+>>> stat = UserStat.objects.create(user=u, posts=150, results=usr)
+>>> sd = StatDetails.objects.create(base_stats=stat, comments=259)
+>>> u = User.objects.create(alias='john')
+>>> ui = UserInfo.objects.create(user=u, name='John Smith')
+>>> stat = UserStat.objects.create(user=u, posts=33, results=usr)
+>>> sd = StatDetails = StatDetails.objects.create(base_stats=stat, comments=104)
+
+# Specifying models in the select_related(...) works as expected.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom')
+>>> u.userstat
+<UserStat: UserStat, posts = 150>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+2
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+2
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userstat', 'userstat__statdetails', 'userstat__results').get(alias='tom')
+>>> u.userstat
+<UserStat: UserStat, posts = 150>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.results
+<UserStatResults: UserStatResults, results = first results>
+>>> len(db.connection.queries)
+1
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+2
+
+>>> db.reset_queries()
+>>> us = UserStat.objects.select_related('user__userinfo').get(user__alias='john')
+>>> us
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+>>> us.user
+<User: User, alias = john>
+>>> len(db.connection.queries)
+1
+>>> us.user.userinfo
+<UserInfo: UserInfo, name = John Smith>
+>>> len(db.connection.queries)
+1
+>>> us.user.userstat
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+
+"""}
+    
\ No newline at end of file
diff -r c273e74671a6 tests/modeltests/select_related_reverse2/models.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/select_related_reverse2/models.py	Mon Jul 07 17:33:52 2008 -0400
@@ -0,0 +1,123 @@
+"""
+``select_related()`` follows all forward relationships, but should also follow
+reverse relationships where it is appropriate (currently for OneToOneFields 
+only).
+"""
+
+from django.db import models
+
+# OneToOneField tests only.
+
+class User(models.Model):
+    alias = models.CharField(max_length=20)
+    
+    def __unicode__(self):
+        return 'User, alias = %s' % (self.alias,)
+
+
+class UserInfo(models.Model):
+    user = models.OneToOneField(User, primary_key=True)
+    name = models.CharField(max_length=32)
+
+    def __unicode__(self):
+        return 'UserInfo, name = %s' % (self.name,)
+    
+
+class UserStatResults(models.Model):
+    results = models.CharField(max_length=50)
+
+    def __unicode__(self):
+        return 'UserStatResults, results = %s' % (self.results,)
+    
+
+class UserStat(models.Model):
+    user = models.ForeignKey(User, primary_key=True, unique=True, related_name='userstat')
+    posts = models.IntegerField()
+    results = models.ForeignKey(UserStatResults)
+
+    def __unicode__(self):
+        return 'UserStat, posts = %s' % (self.posts,)
+
+
+class StatDetails(models.Model):
+    base_stats = models.OneToOneField(UserStat, primary_key=True)
+    comments = models.IntegerField()
+
+    def __unicode__(self):
+        return 'StatDetails, comments = %s' % (self.comments,)
+
+
+__test__ = {'API_TESTS':"""
+
+# Set up.
+# The test runner sets settings.DEBUG to False, but we want to gather queries
+# so we'll set it to True here and reset it at the end of the test suite.
+>>> from django.conf import settings
+>>> settings.DEBUG = True
+>>> from django import db
+
+>>> usr = UserStatResults.objects.create(results='first results')
+>>> u = User.objects.create(alias='tom')
+>>> ui = UserInfo.objects.create(user=u, name='Tom Jones')
+>>> stat = UserStat.objects.create(user=u, posts=150, results=usr)
+>>> sd = StatDetails.objects.create(base_stats=stat, comments=259)
+>>> u = User.objects.create(alias='john')
+>>> ui = UserInfo.objects.create(user=u, name='John Smith')
+>>> stat = UserStat.objects.create(user=u, posts=33, results=usr)
+>>> sd = StatDetails = StatDetails.objects.create(base_stats=stat, comments=104)
+
+# Specifying models in the select_related(...) works as expected.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom')
+>>> u.userstat.all()
+[<UserStat: UserStat, posts = 150>]
+>>> len(db.connection.queries)
+1
+>>> u.userstat.all()[0].statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+2
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+2
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userstat', 'userstat__statdetails', 'userstat__results').get(alias='tom')
+>>> u.userstat.all()
+[<UserStat: UserStat, posts = 150>]
+>>> len(db.connection.queries)
+1
+>>> u.userstat.all()[0].statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.all()[0].results
+<UserStatResults: UserStatResults, results = first results>
+>>> len(db.connection.queries)
+1
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+2
+
+>>> db.reset_queries()
+>>> us = UserStat.objects.select_related('user__userinfo').get(user__alias='john')
+>>> us
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+>>> us.user
+<User: User, alias = john>
+>>> len(db.connection.queries)
+1
+>>> us.user.userinfo
+<UserInfo: UserInfo, name = John Smith>
+>>> len(db.connection.queries)
+1
+>>> us.user.userstat.all()
+[<UserStat: UserStat, posts = 33>]
+>>> len(db.connection.queries)
+1
+
+"""}
+    
\ No newline at end of file
diff -r c273e74671a6 tests/modeltests/select_related_reverse3/models.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modeltests/select_related_reverse3/models.py	Mon Jul 07 17:33:52 2008 -0400
@@ -0,0 +1,222 @@
+"""
+``select_related()`` follows all forward relationships, but should also follow
+reverse relationships where it is appropriate (currently for OneToOneFields 
+only).
+"""
+
+from django.db import models
+
+# OneToOneField tests only.
+
+class User(models.Model):
+    alias = models.CharField(max_length=20)
+    
+    def __unicode__(self):
+        return 'User, alias = %s' % (self.alias,)
+
+
+class UserInfo(models.Model):
+    user = models.OneToOneField(User, primary_key=True)
+    name = models.CharField(max_length=32)
+
+    def __unicode__(self):
+        return 'UserInfo, name = %s' % (self.name,)
+    
+
+class UserStatResults(models.Model):
+    results = models.CharField(max_length=50)
+
+    def __unicode__(self):
+        return 'UserStatResults, results = %s' % (self.results,)
+    
+
+class UserStat(models.Model):
+    user = models.OneToOneField(User, primary_key=True)
+    posts = models.IntegerField()
+    results = models.ForeignKey(UserStatResults)
+    watcher = models.OneToOneField(User, related_name='stat_watcher')
+
+    def __unicode__(self):
+        return 'UserStat, posts = %s' % (self.posts,)
+
+
+class StatDetails(models.Model):
+    base_stats = models.OneToOneField(UserStat, primary_key=True)
+    comments = models.IntegerField()
+
+    def __unicode__(self):
+        return 'StatDetails, comments = %s' % (self.comments,)
+
+
+__test__ = {'API_TESTS':"""
+
+# Set up.
+# The test runner sets settings.DEBUG to False, but we want to gather queries
+# so we'll set it to True here and reset it at the end of the test suite.
+>>> from django.conf import settings
+>>> settings.DEBUG = True
+>>> from django import db
+
+>>> usr = UserStatResults.objects.create(results='first results')
+>>> u1 = User.objects.create(alias='tom')
+>>> ui = UserInfo.objects.create(user=u1, name='Tom Jones')
+>>> u2 = User.objects.create(alias='john')
+>>> ui = UserInfo.objects.create(user=u2, name='John Smith')
+>>> u3 = User.objects.create(alias='bob')
+>>> ui = UserInfo.objects.create(user=u3, name='Bob Rogers')
+>>> stat = UserStat.objects.create(user=u1, posts=150, results=usr, watcher=u3)
+>>> sd = StatDetails.objects.create(base_stats=stat, comments=259)
+>>> stat = UserStat.objects.create(user=u2, posts=33, results=usr, watcher=u2)
+>>> sd = StatDetails = StatDetails.objects.create(base_stats=stat, comments=104)
+
+# Specifying models in the select_related(...) works as expected.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom')
+>>> u.userstat
+<UserStat: UserStat, posts = 150>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+2
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+2
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userstat', 'userstat__statdetails', 'userstat__results').get(alias='tom')
+>>> u.userstat
+<UserStat: UserStat, posts = 150>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.results
+<UserStatResults: UserStatResults, results = first results>
+>>> len(db.connection.queries)
+1
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+2
+
+>>> db.reset_queries()
+>>> us = UserStat.objects.select_related('user__userinfo').get(user__alias='john')
+>>> us
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+>>> us.user
+<User: User, alias = john>
+>>> len(db.connection.queries)
+1
+>>> us.user.userinfo
+<UserInfo: UserInfo, name = John Smith>
+>>> len(db.connection.queries)
+1
+>>> us.user.userstat
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+
+# !!!!!!!!!!!!!!
+# Everything past this point is testing multiple fields pointing to the same model.
+# !!!!!!!!!!!!!!
+>>> us.user.userstat.watcher
+<User: User, alias = john>
+>>> len(db.connection.queries)
+2
+
+>>> db.reset_queries()
+>>> us = UserStat.objects.select_related('user__userinfo', 'watcher').get(user__alias='john')
+>>> us
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+>>> us.user
+<User: User, alias = john>
+>>> len(db.connection.queries)
+1
+>>> us.user.userinfo
+<UserInfo: UserInfo, name = John Smith>
+>>> len(db.connection.queries)
+1
+>>> us.user.userstat
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+
+>>> us.user.userstat.watcher
+<User: User, alias = john>
+>>> len(db.connection.queries)
+1
+
+# Show that the # of queries increases as expected when used reverse.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom')
+>>> u.userstat
+<UserStat: UserStat, posts = 150>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.statdetails
+<StatDetails: StatDetails, comments = 259>
+>>> len(db.connection.queries)
+2
+>>> u.userstat.watcher
+<User: User, alias = bob>
+>>> len(db.connection.queries)
+3
+>>> u.userinfo
+<UserInfo: UserInfo, name = Tom Jones>
+>>> len(db.connection.queries)
+3
+
+# Show that we can follow the watcher too.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userinfo', 'userstat').get(alias='john')
+>>> u.userstat
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.watcher
+<User: User, alias = john>
+>>> len(db.connection.queries)
+2
+>>> u.stat_watcher
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+3
+
+# Show that we can follow the watcher via the select_related(...) bits.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userstat', 'stat_watcher').get(alias='john')
+>>> u.userstat
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.watcher
+<User: User, alias = john>
+>>> len(db.connection.queries)
+2
+>>> u.stat_watcher
+<UserStat: UserStat, posts = 33>
+>>> len(db.connection.queries)
+2
+
+# Show that we can follow the watcher via the select_related(...) bits that are nested.
+>>> db.reset_queries()
+>>> u = User.objects.select_related('userstat', 'userstat__watcher').get(alias='tom')
+>>> u.userstat
+<UserStat: UserStat, posts = 150>
+>>> len(db.connection.queries)
+1
+>>> u.userstat.watcher
+<User: User, alias = bob>
+>>> len(db.connection.queries)
+1
+
+"""}
+    
\ No newline at end of file
