Ticket #7270: django_select_related_onetoone.patch

File django_select_related_onetoone.patch, 9.1 KB (added by bendavis78, 5 years ago)

patches against trunk, r11479

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

     
    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

     
    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

     
     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

     
    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

     
     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"""}
Back to Top