Django

Code

Ticket #7270: django_select_related_onetoone.patch

File django_select_related_onetoone.patch, 9.1 kB (added by bendavis78, 7 months ago)

patches against trunk, r11479

  • django/db/models/sql/query.py

    old new  
    14411441            self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, 
    14421442                    used, next, restricted, new_nullable, dupe_set, avoid) 
    14431443 
     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 
    14441474    def add_aggregate(self, aggregate, model, alias, is_summary): 
    14451475        """ 
    14461476        Adds a single aggregate expression to the Query 
     
    17361766                    raise FieldError("Cannot resolve keyword %r into field. " 
    17371767                            "Choices are: %s" % (name, ", ".join(names))) 
    17381768 
    1739             if not allow_many and (m2m or not direct)
     1769            if not allow_many and m2m
    17401770                for alias in joins: 
    17411771                    self.unref_alias(alias) 
    17421772                raise MultiJoin(pos + 1) 
  • django/db/models/options.py

    old new  
    4747        self.proxy_for_model = None 
    4848        self.parents = SortedDict() 
    4949        self.duplicate_targets = {} 
     50        self.reverse_field_cache = {} 
    5051 
    5152        # To handle various inheritance situations, we need to track where 
    5253        # managers came from (concrete or abstract base classes). 
  • django/db/models/fields/related.py

    old new  
     1import types 
    12from django.db import connection, transaction 
    23from django.db.backends import util 
    34from django.db.models import signals, get_model 
     
    179180    # SingleRelatedObjectDescriptor instance. 
    180181    def __init__(self, related): 
    181182        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  
    183188 
    184189    def __get__(self, instance, instance_type=None): 
    185190        if instance is None: 
     
    315320    # attribute is a ForeignRelatedObjectsDescriptor instance. 
    316321    def __init__(self, related): 
    317322        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  
    318328 
    319329    def __get__(self, instance, instance_type=None): 
    320330        if instance is None: 
     
    333343        if self.related.field.null: 
    334344            manager.clear() 
    335345        manager.add(*value) 
     346        # Cache the value specially if from a unique set.  
     347        if self.related.field.unique: 
     348            self.cache_name = value 
    336349 
    337350    def delete_manager(self, instance): 
    338351        """ 
     
    349362        """ 
    350363        rel_field = self.related.field 
    351364        rel_model = self.related.model 
     365        if rel_field.unique: 
     366            cache_name = self.cache_name  
    352367 
    353368        class RelatedManager(superclass): 
    354369            def get_query_set(self): 
     
    393408                        obj.save() 
    394409                clear.alters_data = True 
    395410 
     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 
    396422        manager = RelatedManager() 
    397423        attname = rel_field.rel.get_related_field().name 
    398424        manager.core_filters = {'%s__%s' % (rel_field.name, attname): 
  • django/db/models/query.py

    old new  
    987987            obj = klass(*fields) 
    988988 
    989989    index_end = index_start + field_count + offset 
     990 
    990991    for f in klass._meta.fields: 
    991992        if not select_related_descend(f, restricted, requested): 
    992993            continue 
     
    10001001            rel_obj, index_end = cached_row 
    10011002            if obj is not None: 
    10021003                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 
    10031028    return obj, index_end 
    10041029 
    10051030def delete_objects(seen_objs): 
  • tests/modeltests/select_related_onetoone/models.py

    old new  
     1from django.db import models 
     2from django import db 
     3 
     4class 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 
     10class 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 
     34u'test' 
     35>>> len(db.connection.queries) 
     361 
     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 
     42u'KS' 
     43>>> len(db.connection.queries) 
     441 
     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) 
     511 
     52 
     53settings.DEBUG = False 
     54"""}