Ticket #7270: 121_reverse_r7601.patch
File 121_reverse_r7601.patch, 9.6 KB (added by , 16 years ago) |
---|
-
django/db/models/sql/query.py
909 909 next = requested.get(f.name, {}) 910 910 else: 911 911 next = False 912 if f.null is not None: 912 if nullable is not None: 913 new_nullable = nullable 914 else: 913 915 new_nullable = f.null 914 else:915 new_nullable = None916 916 self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, 917 917 used, next, restricted, new_nullable) 918 919 # Do reverse columns, but only the ones in the requested list. 920 if restricted: 921 related_fields = [(x.field, x.model) for x in opts.get_all_related_objects() if x.field.unique] 922 for f, model in related_fields: 923 if f.rel.parent_link or f.related_query_name() not in requested: 924 continue 925 table = model._meta.db_table 926 if nullable or f.null: 927 promote = True 928 else: 929 promote = False 930 alias = root_alias 931 alias = self.join((alias, table, f.rel.get_related_field().column, 932 f.column), exclusions=used, 933 promote=promote, reuse=used) 934 used.add(alias) 935 936 self.related_select_cols.extend([(alias, f2.column) 937 for f2 in model._meta.fields]) 938 self.related_select_fields.extend(model._meta.fields) 939 940 next = requested.get(f.related_query_name(), {}) 941 if nullable is not None: 942 new_nullable = nullable 943 else: 944 new_nullable = f.null 945 self.fill_related_selections(model._meta, table, cur_depth + 1, 946 used, next, restricted, new_nullable) 918 947 919 948 def add_filter(self, filter_expr, connector=AND, negate=False, trim=False, 920 949 can_reuse=None): -
django/db/models/query.py
673 673 if cached_row: 674 674 rel_obj, index_end = cached_row 675 675 setattr(obj, f.get_cache_name(), rel_obj) 676 677 # Do the reverse cache if the field is a unique relation. 678 if f.unique and f.related_query_name() in dir(f.rel.to): 679 cache_var_name = rel_obj.__class__.__dict__[f.related_query_name()].cache_name 680 setattr(rel_obj, cache_var_name, obj) 681 682 if restricted: 683 related_fields = [(x.field, x.model) for x in klass._meta.get_all_related_objects() if x.field.unique] 684 for f, model in related_fields: 685 if f.related_query_name() not in requested: 686 continue 687 688 next = requested.get(f.related_query_name(), {}) 689 cached_row = get_cached_row(model, row, index_end, max_depth, 690 cur_depth+1, next) 691 if cached_row: 692 rel_obj, index_end = cached_row 693 setattr(rel_obj, f.get_cache_name(), obj) 694 695 # Now do the reverse cache. 696 cache_var_name = obj.__class__.__dict__[f.related_query_name()].cache_name 697 setattr(obj, cache_var_name, rel_obj) 698 676 699 return obj, index_end 677 700 678 701 def delete_objects(seen_objs): -
tests/modeltests/select_related_reverse/models.py
1 """ 2 ``select_related()`` follows all forward relationships, but should also follow 3 reverse relationships where it is appropriate (currently for OneToOneFields 4 only). 5 """ 6 7 from django.db import models 8 9 # OneToOneField tests only. 10 11 class User(models.Model): 12 alias = models.CharField(max_length=20) 13 14 def __unicode__(self): 15 return 'User, alias = %s' % (self.alias,) 16 17 18 class UserInfo(models.Model): 19 user = models.OneToOneField(User, primary_key=True) 20 name = models.CharField(max_length=32) 21 22 def __unicode__(self): 23 return 'UserInfo, name = %s' % (self.name,) 24 25 26 class UserStatResults(models.Model): 27 results = models.CharField(max_length=50) 28 29 def __unicode__(self): 30 return 'UserStatResults, results = %s' % (self.results,) 31 32 33 class UserStat(models.Model): 34 user = models.OneToOneField(User, primary_key=True) 35 posts = models.IntegerField() 36 results = models.ForeignKey(UserStatResults) 37 38 def __unicode__(self): 39 return 'UserStat, posts = %s' % (self.posts,) 40 41 42 class StatDetails(models.Model): 43 base_stats = models.OneToOneField(UserStat, primary_key=True) 44 comments = models.IntegerField() 45 46 def __unicode__(self): 47 return 'StatDetails, comments = %s' % (self.comments,) 48 49 50 __test__ = {'API_TESTS':""" 51 52 # Set up. 53 # The test runner sets settings.DEBUG to False, but we want to gather queries 54 # so we'll set it to True here and reset it at the end of the test suite. 55 >>> from django.conf import settings 56 >>> settings.DEBUG = True 57 >>> from django import db 58 59 >>> usr = UserStatResults.objects.create(results='first results') 60 >>> u = User.objects.create(alias='tom') 61 >>> ui = UserInfo.objects.create(user=u, name='Tom Jones') 62 >>> stat = UserStat.objects.create(user=u, posts=150, results=usr) 63 >>> sd = StatDetails.objects.create(base_stats=stat, comments=259) 64 >>> u = User.objects.create(alias='john') 65 >>> ui = UserInfo.objects.create(user=u, name='John Smith') 66 >>> stat = UserStat.objects.create(user=u, posts=33, results=usr) 67 >>> sd = StatDetails = StatDetails.objects.create(base_stats=stat, comments=104) 68 69 # Specifying models in the select_related(...) works as expected. 70 >>> db.reset_queries() 71 >>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom') 72 >>> u.userstat 73 <UserStat: UserStat, posts = 150> 74 >>> len(db.connection.queries) 75 1 76 >>> u.userstat.statdetails 77 <StatDetails: StatDetails, comments = 259> 78 >>> len(db.connection.queries) 79 2 80 >>> u.userinfo 81 <UserInfo: UserInfo, name = Tom Jones> 82 >>> len(db.connection.queries) 83 2 84 >>> db.reset_queries() 85 >>> u = User.objects.select_related('userstat', 'userstat__statdetails', 'userstat__results').get(alias='tom') 86 >>> u.userstat 87 <UserStat: UserStat, posts = 150> 88 >>> len(db.connection.queries) 89 1 90 >>> u.userstat.statdetails 91 <StatDetails: StatDetails, comments = 259> 92 >>> len(db.connection.queries) 93 1 94 >>> u.userstat.results 95 <UserStatResults: UserStatResults, results = first results> 96 >>> len(db.connection.queries) 97 1 98 >>> u.userinfo 99 <UserInfo: UserInfo, name = Tom Jones> 100 >>> len(db.connection.queries) 101 2 102 103 >>> db.reset_queries() 104 >>> us = UserStat.objects.select_related('user__userinfo').get(user__alias='john') 105 >>> us 106 <UserStat: UserStat, posts = 33> 107 >>> len(db.connection.queries) 108 1 109 >>> us.user 110 <User: User, alias = john> 111 >>> len(db.connection.queries) 112 1 113 >>> us.user.userinfo 114 <UserInfo: UserInfo, name = John Smith> 115 >>> len(db.connection.queries) 116 1 117 >>> us.user.userstat 118 <UserStat: UserStat, posts = 33> 119 >>> len(db.connection.queries) 120 1 121 122 """} 123 124 No newline at end of file -
docs/db-api.txt
889 889 list of fields and the ``depth`` parameter in the same ``select_related()`` 890 890 call, since they are conflicting options. 891 891 892 **New in Django development version:** If you are using OneToOneFields, you 893 can refer to them from either side of the relationship via their related 894 field names in ``select_related()``, and they will be followed and cached 895 appropriately. Also, any ``ForeignKey`` relations that are appropriate to be 896 followed from either side of the OneToOneField relation can be, in the same 897 way that is described above. For example, if we have the following:: 898 899 class User(models.Model): 900 alias = models.CharField(...) 901 902 class UserInfo(models.Model): 903 user = models.OneToOneField(User) 904 name = models.CharField(...) 905 906 class UserStatResults(models.Model): 907 results = models.CharField(...) 908 909 class UserStat(models.Model): 910 user = models.OneToOneField(User) 911 posts = models.IntegerField(...) 912 results = models.ForeignKey(UserStatResults) 913 914 This is valid:: 915 916 User.objects.select_related('userstat__results', 'userinfo') 917 918 This would cache not only the UserStat and UserInfo instances that are 919 appropriate to be cached, but also any UserStatResults instances, 920 since the ForeignKey goes in the proper direction from UserStat. 921 922 This is also valid:: 923 924 ui = UserInfo.objects.select_related('user__userstat').get(id=1) 925 print ui.user.userstat 926 print ui.user.userinfo.user.userstat 927 928 This would cache all the UserInfo, User and UserStat objects that are selected 929 by the criteria, and would generate no extra queries no matter how the related 930 OneToOneFields are chained together. 931 892 932 ``extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)`` 893 933 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 894 934