Ticket #7270: 121_reverse_r10396.patch
File 121_reverse_r10396.patch, 21.2 KB (added by , 16 years ago) |
---|
-
django/db/models/sql/query.py
1391 1391 self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, 1392 1392 used, next, restricted, new_nullable, dupe_set, avoid) 1393 1393 1394 # Do reverse columns, but only the ones in the requested list. 1395 if restricted: 1396 related_fields = [(x.field, x.model) for x in opts.get_all_related_objects() if x.field.unique] 1397 for f, model in related_fields: 1398 if f.rel.parent_link or f.related_query_name() not in requested: 1399 continue 1400 table = model._meta.db_table 1401 alias = self.join((root_alias, table, f.rel.get_related_field().column, 1402 f.column), exclusions=used, 1403 promote=True, reuse=used) 1404 used.add(alias) 1405 1406 self.related_select_cols.extend([(alias, f2.column) 1407 for f2 in model._meta.fields]) 1408 self.related_select_fields.extend(model._meta.fields) 1409 1410 next = requested.get(f.related_query_name(), {}) 1411 #if nullable is not None: 1412 # new_nullable = nullable 1413 #else: 1414 # new_nullable = f.null 1415 if f.null is not None: 1416 new_nullable = f.null 1417 else: 1418 new_nullable = None 1419 self.fill_related_selections(model._meta, table, cur_depth + 1, 1420 used, next, restricted, new_nullable) 1421 1422 1423 1394 1424 def add_aggregate(self, aggregate, model, alias, is_summary): 1395 1425 """ 1396 1426 Adds a single aggregate expression to the Query -
django/db/models/options.py
47 47 self.proxy_for_model = None 48 48 self.parents = SortedDict() 49 49 self.duplicate_targets = {} 50 self.reverse_field_cache = {} 50 51 51 52 # To handle various inheritance situations, we need to track where 52 53 # managers came from (concrete or abstract base classes). -
django/db/models/fields/related.py
1 import types 1 2 from django.db import connection, transaction 2 3 from django.db.backends import util 3 4 from django.db.models import signals, get_model … … 178 179 # SingleRelatedObjectDescriptor instance. 179 180 def __init__(self, related): 180 181 self.related = related 181 self.cache_name = '_%s_cache' % related.get_accessor_name() 182 #self.cache_name = '_%s_cache' % related.get_accessor_name() 183 cache_name = '_%s_cache' % related.field.related_query_name() 184 # Contribute to the parent model for later lookup. 185 related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name 186 self.cache_name = cache_name 182 187 183 188 def __get__(self, instance, instance_type=None): 184 189 if instance is None: … … 291 296 # attribute is a ForeignRelatedObjectsDescriptor instance. 292 297 def __init__(self, related): 293 298 self.related = related # RelatedObject instance 299 if related.field.unique: 300 cache_name = '_%s_cache' % related.field.related_query_name() 301 # Contribute to the parent model for later lookup. 302 related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name 303 self.cache_name = cache_name 294 304 305 295 306 def __get__(self, instance, instance_type=None): 296 307 if instance is None: 297 308 return self … … 309 320 if self.related.field.null: 310 321 manager.clear() 311 322 manager.add(*value) 323 # Cache the value specially if from a unique set. 324 if self.related.field.unique: 325 self.cache_name = value 312 326 327 313 328 def delete_manager(self, instance): 314 329 """ 315 330 Returns a queryset based on the related model's base manager (rather … … 325 340 """ 326 341 rel_field = self.related.field 327 342 rel_model = self.related.model 343 if rel_field.unique: 344 cache_name = self.cache_name 328 345 329 346 class RelatedManager(superclass): 330 347 def get_query_set(self): … … 369 386 obj.save() 370 387 clear.alters_data = True 371 388 389 if rel_field.unique: 390 def all(self): 391 try: 392 result = getattr(instance, cache_name) 393 if isinstance(result, (types.TupleType, types.ListType)): 394 return result 395 else: 396 return [result] 397 except AttributeError, ae: 398 return superclass.get_query_set(self) 399 400 372 401 manager = RelatedManager() 373 402 attname = rel_field.rel.get_related_field().name 374 403 manager.core_filters = {'%s__%s' % (rel_field.name, attname): -
django/db/models/query.py
950 950 rel_obj, index_end = cached_row 951 951 if obj is not None: 952 952 setattr(obj, f.get_cache_name(), rel_obj) 953 954 # Do the reverse cache if the field is a unique relation. 955 if f.unique: 956 cache_var_name = rel_obj._meta.reverse_field_cache[f.related_query_name()] 957 setattr(rel_obj, cache_var_name, obj) 958 959 if restricted: 960 related_fields = [(x.field, x.model) for x in klass._meta.get_all_related_objects() if x.field.unique] 961 for f, model in related_fields: 962 if f.related_query_name() not in requested: 963 continue 964 965 next = requested.get(f.related_query_name(), {}) 966 cached_row = get_cached_row(model, row, index_end, max_depth, 967 cur_depth+1, next) 968 if cached_row: 969 rel_obj, index_end = cached_row 970 setattr(rel_obj, f.get_cache_name(), obj) 971 972 # Now do the reverse cache. 973 cache_var_name = obj._meta.reverse_field_cache[f.related_query_name()] 974 setattr(obj, cache_var_name, rel_obj) 953 975 return obj, index_end 954 976 955 977 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 -
tests/modeltests/select_related_reverse2/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.ForeignKey(User, primary_key=True, unique=True, related_name='userstat') 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.all() 73 [<UserStat: UserStat, posts = 150>] 74 >>> len(db.connection.queries) 75 1 76 >>> u.userstat.all()[0].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.all() 87 [<UserStat: UserStat, posts = 150>] 88 >>> len(db.connection.queries) 89 1 90 >>> u.userstat.all()[0].statdetails 91 <StatDetails: StatDetails, comments = 259> 92 >>> len(db.connection.queries) 93 1 94 >>> u.userstat.all()[0].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.all() 118 [<UserStat: UserStat, posts = 33>] 119 >>> len(db.connection.queries) 120 1 121 122 """} 123 -
tests/modeltests/select_related_reverse3/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 watcher = models.OneToOneField(User, related_name='stat_watcher') 38 39 def __unicode__(self): 40 return 'UserStat, posts = %s' % (self.posts,) 41 42 43 class StatDetails(models.Model): 44 base_stats = models.OneToOneField(UserStat, primary_key=True) 45 comments = models.IntegerField() 46 47 def __unicode__(self): 48 return 'StatDetails, comments = %s' % (self.comments,) 49 50 51 __test__ = {'API_TESTS':""" 52 53 # Set up. 54 # The test runner sets settings.DEBUG to False, but we want to gather queries 55 # so we'll set it to True here and reset it at the end of the test suite. 56 >>> from django.conf import settings 57 >>> settings.DEBUG = True 58 >>> from django import db 59 60 >>> usr = UserStatResults.objects.create(results='first results') 61 >>> u1 = User.objects.create(alias='tom') 62 >>> ui = UserInfo.objects.create(user=u1, name='Tom Jones') 63 >>> u2 = User.objects.create(alias='john') 64 >>> ui = UserInfo.objects.create(user=u2, name='John Smith') 65 >>> u3 = User.objects.create(alias='bob') 66 >>> ui = UserInfo.objects.create(user=u3, name='Bob Rogers') 67 >>> stat = UserStat.objects.create(user=u1, posts=150, results=usr, watcher=u3) 68 >>> sd = StatDetails.objects.create(base_stats=stat, comments=259) 69 >>> stat = UserStat.objects.create(user=u2, posts=33, results=usr, watcher=u2) 70 >>> sd = StatDetails = StatDetails.objects.create(base_stats=stat, comments=104) 71 72 # Specifying models in the select_related(...) works as expected. 73 >>> db.reset_queries() 74 >>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom') 75 >>> u.userstat 76 <UserStat: UserStat, posts = 150> 77 >>> len(db.connection.queries) 78 1 79 >>> u.userstat.statdetails 80 <StatDetails: StatDetails, comments = 259> 81 >>> len(db.connection.queries) 82 2 83 >>> u.userinfo 84 <UserInfo: UserInfo, name = Tom Jones> 85 >>> len(db.connection.queries) 86 2 87 >>> db.reset_queries() 88 >>> u = User.objects.select_related('userstat', 'userstat__statdetails', 'userstat__results').get(alias='tom') 89 >>> u.userstat 90 <UserStat: UserStat, posts = 150> 91 >>> len(db.connection.queries) 92 1 93 >>> u.userstat.statdetails 94 <StatDetails: StatDetails, comments = 259> 95 >>> len(db.connection.queries) 96 1 97 >>> u.userstat.results 98 <UserStatResults: UserStatResults, results = first results> 99 >>> len(db.connection.queries) 100 1 101 >>> u.userinfo 102 <UserInfo: UserInfo, name = Tom Jones> 103 >>> len(db.connection.queries) 104 2 105 106 >>> db.reset_queries() 107 >>> us = UserStat.objects.select_related('user__userinfo').get(user__alias='john') 108 >>> us 109 <UserStat: UserStat, posts = 33> 110 >>> len(db.connection.queries) 111 1 112 >>> us.user 113 <User: User, alias = john> 114 >>> len(db.connection.queries) 115 1 116 >>> us.user.userinfo 117 <UserInfo: UserInfo, name = John Smith> 118 >>> len(db.connection.queries) 119 1 120 >>> us.user.userstat 121 <UserStat: UserStat, posts = 33> 122 >>> len(db.connection.queries) 123 1 124 125 # !!!!!!!!!!!!!! 126 # Everything past this point is testing multiple fields pointing to the same model. 127 # !!!!!!!!!!!!!! 128 >>> us.user.userstat.watcher 129 <User: User, alias = john> 130 >>> len(db.connection.queries) 131 2 132 133 >>> db.reset_queries() 134 >>> us = UserStat.objects.select_related('user__userinfo', 'watcher').get(user__alias='john') 135 >>> us 136 <UserStat: UserStat, posts = 33> 137 >>> len(db.connection.queries) 138 1 139 >>> us.user 140 <User: User, alias = john> 141 >>> len(db.connection.queries) 142 1 143 >>> us.user.userinfo 144 <UserInfo: UserInfo, name = John Smith> 145 >>> len(db.connection.queries) 146 1 147 >>> us.user.userstat 148 <UserStat: UserStat, posts = 33> 149 >>> len(db.connection.queries) 150 1 151 152 >>> us.user.userstat.watcher 153 <User: User, alias = john> 154 >>> len(db.connection.queries) 155 1 156 157 # Show that the # of queries increases as expected when used reverse. 158 >>> db.reset_queries() 159 >>> u = User.objects.select_related('userinfo', 'userstat').get(alias='tom') 160 >>> u.userstat 161 <UserStat: UserStat, posts = 150> 162 >>> len(db.connection.queries) 163 1 164 >>> u.userstat.statdetails 165 <StatDetails: StatDetails, comments = 259> 166 >>> len(db.connection.queries) 167 2 168 >>> u.userstat.watcher 169 <User: User, alias = bob> 170 >>> len(db.connection.queries) 171 3 172 >>> u.userinfo 173 <UserInfo: UserInfo, name = Tom Jones> 174 >>> len(db.connection.queries) 175 3 176 177 # Show that we can follow the watcher too. 178 >>> db.reset_queries() 179 >>> u = User.objects.select_related('userinfo', 'userstat').get(alias='john') 180 >>> u.userstat 181 <UserStat: UserStat, posts = 33> 182 >>> len(db.connection.queries) 183 1 184 >>> u.userstat.watcher 185 <User: User, alias = john> 186 >>> len(db.connection.queries) 187 2 188 >>> u.stat_watcher 189 <UserStat: UserStat, posts = 33> 190 >>> len(db.connection.queries) 191 3 192 193 # Show that we can follow the watcher via the select_related(...) bits. 194 >>> db.reset_queries() 195 >>> u = User.objects.select_related('userstat', 'stat_watcher').get(alias='john') 196 >>> u.userstat 197 <UserStat: UserStat, posts = 33> 198 >>> len(db.connection.queries) 199 1 200 >>> u.userstat.watcher 201 <User: User, alias = john> 202 >>> len(db.connection.queries) 203 2 204 >>> u.stat_watcher 205 <UserStat: UserStat, posts = 33> 206 >>> len(db.connection.queries) 207 2 208 209 # Show that we can follow the watcher via the select_related(...) bits that are nested. 210 >>> db.reset_queries() 211 >>> u = User.objects.select_related('userstat', 'userstat__watcher').get(alias='tom') 212 >>> u.userstat 213 <UserStat: UserStat, posts = 150> 214 >>> len(db.connection.queries) 215 1 216 >>> u.userstat.watcher 217 <User: User, alias = bob> 218 >>> len(db.connection.queries) 219 1 220 221 """} 222