Ticket #7270: 121_reverse_r8985.patch

File 121_reverse_r8985.patch, 21.5 KB (added by gav, 6 years ago)

121 reverse patch updated to r8985.

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

     
    10381038            self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
    10391039                    used, next, restricted, new_nullable, dupe_set, avoid)
    10401040
     1041           
     1042        # Do reverse columns, but only the ones in the requested list.
     1043        if restricted:
     1044            related_fields = [(x.field, x.model) for x in opts.get_all_related_objects() if x.field.unique]
     1045            for f, model in related_fields:
     1046                if f.rel.parent_link or f.related_query_name() not in requested:
     1047                    continue
     1048                table = model._meta.db_table
     1049                if nullable or f.null:
     1050                    promote = True
     1051                else:
     1052                    promote = False
     1053                alias = self.join((root_alias, table, f.rel.get_related_field().column,
     1054                        f.column), exclusions=used,
     1055                        promote=promote, reuse=used)
     1056                used.add(alias)
     1057
     1058                self.related_select_cols.extend([(alias, f2.column)
     1059                        for f2 in model._meta.fields])
     1060                self.related_select_fields.extend(model._meta.fields)
     1061
     1062                next = requested.get(f.related_query_name(), {})
     1063                #if nullable is not None:
     1064                #    new_nullable = nullable
     1065                #else:
     1066                #    new_nullable = f.null
     1067                if f.null is not None:
     1068                    new_nullable = f.null
     1069                else:
     1070                    new_nullable = None
     1071                self.fill_related_selections(model._meta, table, cur_depth + 1,
     1072                        used, next, restricted, new_nullable)
     1073
    10411074    def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
    10421075            can_reuse=None, process_extras=True):
    10431076        """
  • django/db/models/options.py

     
    4545        self.abstract = False
    4646        self.parents = SortedDict()
    4747        self.duplicate_targets = {}
     48        self.reverse_field_cache = {}
    4849        # Managers that have been inherited from abstract base classes. These
    4950        # are passed onto any children.
    5051        self.abstract_managers = []
  • django/db/models/fields/related.py

     
     1import types
    12from django.db import connection, transaction
    23from django.db.models import signals, get_model
    34from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist
     
    170171    # SingleRelatedObjectDescriptor instance.
    171172    def __init__(self, related):
    172173        self.related = related
    173         self.cache_name = '_%s_cache' % related.get_accessor_name()
     174        #self.cache_name = '_%s_cache' % related.get_accessor_name()
     175        cache_name = '_%s_cache' % related.field.related_query_name()
     176        # Contribute to the parent model for later lookup.
     177        related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name
     178        self.cache_name = cache_name
    174179
    175180    def __get__(self, instance, instance_type=None):
    176181        if instance is None:
     
    283288    # attribute is a ForeignRelatedObjectsDescriptor instance.
    284289    def __init__(self, related):
    285290        self.related = related   # RelatedObject instance
     291        if related.field.unique:
     292            cache_name = '_%s_cache' % related.field.related_query_name()
     293            # Contribute to the parent model for later lookup.
     294            related.parent_model._meta.reverse_field_cache[related.field.related_query_name()] = cache_name
     295            self.cache_name = cache_name
    286296
    287297    def __get__(self, instance, instance_type=None):
    288298        if instance is None:
     
    290300
    291301        rel_field = self.related.field
    292302        rel_model = self.related.model
     303        if rel_field.unique:
     304            cache_name = self.cache_name
    293305
    294306        # Dynamically create a class that subclasses the related
    295307        # model's default manager.
     
    336348                        obj.save()
    337349                clear.alters_data = True
    338350
     351            if rel_field.unique:
     352                def all(self):
     353                    try:
     354                        result = getattr(instance, cache_name)
     355                        if isinstance(result, (types.TupleType, types.ListType)):
     356                            return result
     357                        else:
     358                            return [result]
     359                    except AttributeError, ae:
     360                        return superclass.get_query_set(self)
     361
    339362        manager = RelatedManager()
    340363        attname = rel_field.rel.get_related_field().name
    341364        manager.core_filters = {'%s__%s' % (rel_field.name, attname):
     
    354377        if self.related.field.null:
    355378            manager.clear()
    356379        manager.add(*value)
     380        # Cache the value specially if from a unique set.
     381        if self.related.field.unique:
     382            self.cache_name = value
    357383
    358384def create_many_related_manager(superclass, through=False):
    359385    """Creates a manager that subclasses 'superclass' (which is a Manager)
  • django/db/models/query.py

     
    812812            rel_obj, index_end = cached_row
    813813            if obj is not None:
    814814                setattr(obj, f.get_cache_name(), rel_obj)
     815
     816            # Do the reverse cache if the field is a unique relation.
     817            if f.unique:
     818                cache_var_name = rel_obj._meta.reverse_field_cache[f.related_query_name()]
     819                setattr(rel_obj, cache_var_name, obj)
     820
     821    if restricted:
     822        related_fields = [(x.field, x.model) for x in klass._meta.get_all_related_objects() if x.field.unique]
     823        for f, model in related_fields:
     824            if f.related_query_name() not in requested:
     825                continue
     826
     827            next = requested.get(f.related_query_name(), {})
     828            cached_row = get_cached_row(model, row, index_end, max_depth,
     829                    cur_depth+1, next)
     830            if cached_row:
     831                rel_obj, index_end = cached_row
     832                setattr(rel_obj, f.get_cache_name(), obj)
     833
     834                # Now do the reverse cache.
     835                cache_var_name = obj._meta.reverse_field_cache[f.related_query_name()]
     836                setattr(obj, cache_var_name, rel_obj)
     837
    815838    return obj, index_end
    816839
    817840
  • tests/modeltests/select_related_reverse/models.py

     
     1"""
     2``select_related()`` follows all forward relationships, but should also follow
     3reverse relationships where it is appropriate (currently for OneToOneFields
     4only).
     5"""
     6
     7from django.db import models
     8
     9# OneToOneField tests only.
     10
     11class User(models.Model):
     12    alias = models.CharField(max_length=20)
     13   
     14    def __unicode__(self):
     15        return 'User, alias = %s' % (self.alias,)
     16
     17
     18class 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
     26class UserStatResults(models.Model):
     27    results = models.CharField(max_length=50)
     28
     29    def __unicode__(self):
     30        return 'UserStatResults, results = %s' % (self.results,)
     31   
     32
     33class 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
     42class 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)
     751
     76>>> u.userstat.statdetails
     77<StatDetails: StatDetails, comments = 259>
     78>>> len(db.connection.queries)
     792
     80>>> u.userinfo
     81<UserInfo: UserInfo, name = Tom Jones>
     82>>> len(db.connection.queries)
     832
     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)
     891
     90>>> u.userstat.statdetails
     91<StatDetails: StatDetails, comments = 259>
     92>>> len(db.connection.queries)
     931
     94>>> u.userstat.results
     95<UserStatResults: UserStatResults, results = first results>
     96>>> len(db.connection.queries)
     971
     98>>> u.userinfo
     99<UserInfo: UserInfo, name = Tom Jones>
     100>>> len(db.connection.queries)
     1012
     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)
     1081
     109>>> us.user
     110<User: User, alias = john>
     111>>> len(db.connection.queries)
     1121
     113>>> us.user.userinfo
     114<UserInfo: UserInfo, name = John Smith>
     115>>> len(db.connection.queries)
     1161
     117>>> us.user.userstat
     118<UserStat: UserStat, posts = 33>
     119>>> len(db.connection.queries)
     1201
     121
     122"""}
     123   
     124 No newline at end of file
  • tests/modeltests/select_related_reverse2/models.py

     
     1"""
     2``select_related()`` follows all forward relationships, but should also follow
     3reverse relationships where it is appropriate (currently for OneToOneFields
     4only).
     5"""
     6
     7from django.db import models
     8
     9# OneToOneField tests only.
     10
     11class User(models.Model):
     12    alias = models.CharField(max_length=20)
     13   
     14    def __unicode__(self):
     15        return 'User, alias = %s' % (self.alias,)
     16
     17
     18class 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
     26class UserStatResults(models.Model):
     27    results = models.CharField(max_length=50)
     28
     29    def __unicode__(self):
     30        return 'UserStatResults, results = %s' % (self.results,)
     31   
     32
     33class 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
     42class 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)
     751
     76>>> u.userstat.all()[0].statdetails
     77<StatDetails: StatDetails, comments = 259>
     78>>> len(db.connection.queries)
     792
     80>>> u.userinfo
     81<UserInfo: UserInfo, name = Tom Jones>
     82>>> len(db.connection.queries)
     832
     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)
     891
     90>>> u.userstat.all()[0].statdetails
     91<StatDetails: StatDetails, comments = 259>
     92>>> len(db.connection.queries)
     931
     94>>> u.userstat.all()[0].results
     95<UserStatResults: UserStatResults, results = first results>
     96>>> len(db.connection.queries)
     971
     98>>> u.userinfo
     99<UserInfo: UserInfo, name = Tom Jones>
     100>>> len(db.connection.queries)
     1012
     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)
     1081
     109>>> us.user
     110<User: User, alias = john>
     111>>> len(db.connection.queries)
     1121
     113>>> us.user.userinfo
     114<UserInfo: UserInfo, name = John Smith>
     115>>> len(db.connection.queries)
     1161
     117>>> us.user.userstat.all()
     118[<UserStat: UserStat, posts = 33>]
     119>>> len(db.connection.queries)
     1201
     121
     122"""}
     123   
     124 No newline at end of file
  • tests/modeltests/select_related_reverse3/models.py

     
     1"""
     2``select_related()`` follows all forward relationships, but should also follow
     3reverse relationships where it is appropriate (currently for OneToOneFields
     4only).
     5"""
     6
     7from django.db import models
     8
     9# OneToOneField tests only.
     10
     11class User(models.Model):
     12    alias = models.CharField(max_length=20)
     13   
     14    def __unicode__(self):
     15        return 'User, alias = %s' % (self.alias,)
     16
     17
     18class 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
     26class UserStatResults(models.Model):
     27    results = models.CharField(max_length=50)
     28
     29    def __unicode__(self):
     30        return 'UserStatResults, results = %s' % (self.results,)
     31   
     32
     33class 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
     43class 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)
     781
     79>>> u.userstat.statdetails
     80<StatDetails: StatDetails, comments = 259>
     81>>> len(db.connection.queries)
     822
     83>>> u.userinfo
     84<UserInfo: UserInfo, name = Tom Jones>
     85>>> len(db.connection.queries)
     862
     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)
     921
     93>>> u.userstat.statdetails
     94<StatDetails: StatDetails, comments = 259>
     95>>> len(db.connection.queries)
     961
     97>>> u.userstat.results
     98<UserStatResults: UserStatResults, results = first results>
     99>>> len(db.connection.queries)
     1001
     101>>> u.userinfo
     102<UserInfo: UserInfo, name = Tom Jones>
     103>>> len(db.connection.queries)
     1042
     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)
     1111
     112>>> us.user
     113<User: User, alias = john>
     114>>> len(db.connection.queries)
     1151
     116>>> us.user.userinfo
     117<UserInfo: UserInfo, name = John Smith>
     118>>> len(db.connection.queries)
     1191
     120>>> us.user.userstat
     121<UserStat: UserStat, posts = 33>
     122>>> len(db.connection.queries)
     1231
     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)
     1312
     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)
     1381
     139>>> us.user
     140<User: User, alias = john>
     141>>> len(db.connection.queries)
     1421
     143>>> us.user.userinfo
     144<UserInfo: UserInfo, name = John Smith>
     145>>> len(db.connection.queries)
     1461
     147>>> us.user.userstat
     148<UserStat: UserStat, posts = 33>
     149>>> len(db.connection.queries)
     1501
     151
     152>>> us.user.userstat.watcher
     153<User: User, alias = john>
     154>>> len(db.connection.queries)
     1551
     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)
     1631
     164>>> u.userstat.statdetails
     165<StatDetails: StatDetails, comments = 259>
     166>>> len(db.connection.queries)
     1672
     168>>> u.userstat.watcher
     169<User: User, alias = bob>
     170>>> len(db.connection.queries)
     1713
     172>>> u.userinfo
     173<UserInfo: UserInfo, name = Tom Jones>
     174>>> len(db.connection.queries)
     1753
     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)
     1831
     184>>> u.userstat.watcher
     185<User: User, alias = john>
     186>>> len(db.connection.queries)
     1872
     188>>> u.stat_watcher
     189<UserStat: UserStat, posts = 33>
     190>>> len(db.connection.queries)
     1913
     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)
     1991
     200>>> u.userstat.watcher
     201<User: User, alias = john>
     202>>> len(db.connection.queries)
     2032
     204>>> u.stat_watcher
     205<UserStat: UserStat, posts = 33>
     206>>> len(db.connection.queries)
     2072
     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)
     2151
     216>>> u.userstat.watcher
     217<User: User, alias = bob>
     218>>> len(db.connection.queries)
     2191
     220
     221"""}
     222   
     223 No newline at end of file
Back to Top