Ticket #7270: django_select_related_onetoone.patch
File django_select_related_onetoone.patch, 9.1 KB (added by , 15 years ago) |
---|
-
django/db/models/sql/query.py
1441 1441 self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, 1442 1442 used, next, restricted, new_nullable, dupe_set, avoid) 1443 1443 1444 # Do reverse columns, but only the ones in the requested list. 1445 if restricted: 1446 related_fields = [(x.field, x.model) for x in opts.get_all_related_objects() if x.field.unique] 1447 for f, model in related_fields: 1448 if f.rel.parent_link or f.related_query_name() not in requested: 1449 continue 1450 table = model._meta.db_table 1451 alias = self.join((root_alias, table, f.rel.get_related_field().column, 1452 f.column), exclusions=used, 1453 promote=True, reuse=used) 1454 used.add(alias) 1455 1456 self.related_select_cols.extend([(alias, f2.column) 1457 for f2 in model._meta.fields]) 1458 self.related_select_fields.extend(model._meta.fields) 1459 1460 next = requested.get(f.related_query_name(), {}) 1461 #if nullable is not None: 1462 # new_nullable = nullable 1463 #else: 1464 # new_nullable = f.null 1465 if f.null is not None: 1466 new_nullable = f.null 1467 else: 1468 new_nullable = None 1469 self.fill_related_selections(model._meta, table, cur_depth + 1, 1470 used, next, restricted, new_nullable) 1471 1472 1473 1444 1474 def add_aggregate(self, aggregate, model, alias, is_summary): 1445 1475 """ 1446 1476 Adds a single aggregate expression to the Query … … 1736 1766 raise FieldError("Cannot resolve keyword %r into field. " 1737 1767 "Choices are: %s" % (name, ", ".join(names))) 1738 1768 1739 if not allow_many and (m2m or not direct):1769 if not allow_many and m2m: 1740 1770 for alias in joins: 1741 1771 self.unref_alias(alias) 1742 1772 raise MultiJoin(pos + 1) -
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 … … 179 180 # SingleRelatedObjectDescriptor instance. 180 181 def __init__(self, related): 181 182 self.related = related 182 self.cache_name = '_%s_cache' % related.get_accessor_name() 183 #self.cache_name = '_%s_cache' % related.get_accessor_name() 184 cache_name = '_%s_cache' % related.field.related_query_name() 185 # Contribute to the parent model for later lookup. 186 related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name 187 self.cache_name = cache_name 183 188 184 189 def __get__(self, instance, instance_type=None): 185 190 if instance is None: … … 315 320 # attribute is a ForeignRelatedObjectsDescriptor instance. 316 321 def __init__(self, related): 317 322 self.related = related # RelatedObject instance 323 if related.field.unique: 324 cache_name = '_%s_cache' % related.field.related_query_name() 325 # Contribute to the parent model for later lookup. 326 related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name 327 self.cache_name = cache_name 318 328 319 329 def __get__(self, instance, instance_type=None): 320 330 if instance is None: … … 333 343 if self.related.field.null: 334 344 manager.clear() 335 345 manager.add(*value) 346 # Cache the value specially if from a unique set. 347 if self.related.field.unique: 348 self.cache_name = value 336 349 337 350 def delete_manager(self, instance): 338 351 """ … … 349 362 """ 350 363 rel_field = self.related.field 351 364 rel_model = self.related.model 365 if rel_field.unique: 366 cache_name = self.cache_name 352 367 353 368 class RelatedManager(superclass): 354 369 def get_query_set(self): … … 393 408 obj.save() 394 409 clear.alters_data = True 395 410 411 if rel_field.unique: 412 def all(self): 413 try: 414 result = getattr(instance, cache_name) 415 if isinstance(result, (types.TupleType, types.ListType)): 416 return result 417 else: 418 return [result] 419 except AttributeError, ae: 420 return superclass.get_query_set(self) 421 396 422 manager = RelatedManager() 397 423 attname = rel_field.rel.get_related_field().name 398 424 manager.core_filters = {'%s__%s' % (rel_field.name, attname): -
django/db/models/query.py
987 987 obj = klass(*fields) 988 988 989 989 index_end = index_start + field_count + offset 990 990 991 for f in klass._meta.fields: 991 992 if not select_related_descend(f, restricted, requested): 992 993 continue … … 1000 1001 rel_obj, index_end = cached_row 1001 1002 if obj is not None: 1002 1003 setattr(obj, f.get_cache_name(), rel_obj) 1004 1005 # Do the reverse cache if the field is a unique relation. 1006 if f.unique: 1007 cache_var_name = rel_obj._meta.reverse_field_cache[f.related_query_name()] 1008 setattr(rel_obj, cache_var_name, obj) 1009 1010 if restricted: 1011 related_fields = [(x.field, x.model) for x in klass._meta.get_all_related_objects() if x.field.unique] 1012 for f, model in related_fields: 1013 if f.related_query_name() not in requested: 1014 continue 1015 1016 next = requested.get(f.related_query_name(), {}) 1017 cached_row = get_cached_row(model, row, index_end, max_depth, 1018 cur_depth+1, next) 1019 if cached_row: 1020 rel_obj, index_end = cached_row 1021 if rel_obj is not None: 1022 setattr(rel_obj, f.get_cache_name(), obj) 1023 1024 # Now do the reverse cache. 1025 cache_var_name = obj._meta.reverse_field_cache[f.related_query_name()] 1026 setattr(obj, cache_var_name, rel_obj) 1027 1003 1028 return obj, index_end 1004 1029 1005 1030 def delete_objects(seen_objs): -
tests/modeltests/select_related_onetoone/models.py
1 from django.db import models 2 from django import db 3 4 class User(models.Model): 5 username = models.CharField(max_length=100) 6 email = models.CharField(max_length=100) 7 def __unicode__(self): 8 return self.username 9 10 class UserProfile(models.Model): 11 user = models.OneToOneField(User, related_name='userprofile') 12 city = models.CharField(max_length=100) 13 state = models.CharField(max_length=2) 14 15 def __unicode__(self): 16 return '%s, %s' % (self.city, self.state) 17 18 19 __test__ = {'API_TESTS':""" 20 >>> from django.conf import settings 21 >>> settings.DEBUG = True 22 23 >>> user = User.objects.create(username='test') 24 >>> userprofile = UserProfile.objects.create(user=user,state='KS',city='Lawrence') 25 >>> user 26 <User: test> 27 >>> user.userprofile 28 <UserProfile: Lawrence, KS> 29 30 # select_related works as usual, using forward relationship 31 >>> db.reset_queries() 32 >>> u = UserProfile.objects.select_related('user').get(state='KS') 33 >>> u.user.username 34 u'test' 35 >>> len(db.connection.queries) 36 1 37 38 # Now we can select reverse from the user to userprofile 39 >>> db.reset_queries() 40 >>> u = User.objects.select_related('userprofile').get(username='test') 41 >>> u.userprofile.state 42 u'KS' 43 >>> len(db.connection.queries) 44 1 45 46 # Also test w/ values() 47 >>> db.reset_queries() 48 >>> User.objects.values('username', 'userprofile__state') 49 [{'username': u'test', 'userprofile__state': u'KS'}] 50 >>> len(db.connection.queries) 51 1 52 53 settings.DEBUG = False 54 """}