Ticket #7895: query_splitted_methods.diff

File query_splitted_methods.diff, 16.9 KB (added by Fabio Corneti, 16 years ago)
  • django/db/models/sql/query.py

     
    986986            self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
    987987                    used, next, restricted, new_nullable, dupe_set)
    988988
     989    def parse_filter_expr(self, filter_expr):
     990        """
     991        Parses the given filter expression and returns a tuple containing
     992        the field names, the lookup type and the value.
     993        """
     994        arg, value = filter_expr
     995        parts = arg.split(LOOKUP_SEP)
     996        if not parts:
     997            raise FieldError("Cannot parse keyword query %r" % arg)
     998
     999        # Work out the lookup type and remove it from 'parts', if necessary.
     1000        if len(parts) == 1 or parts[-1] not in self.query_terms:
     1001            lookup_type = 'exact'
     1002        else:
     1003            lookup_type = parts.pop()
     1004
     1005        # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
     1006        # uses of None as a query value.
     1007        if value is None:
     1008            if lookup_type != 'exact':
     1009                raise ValueError("Cannot use None as a query value")
     1010            lookup_type = 'isnull'
     1011            value = True
     1012        elif callable(value):
     1013            value = value()
     1014
     1015        return (parts, lookup_type, value)
     1016
    9891017    def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
    9901018            can_reuse=None):
    9911019        """
     
    10081036        if we would otherwise force the creation of new aliases for a join
    10091037        (needed for nested Q-filters). The set is updated by this method.
    10101038        """
    1011         arg, value = filter_expr
    1012         parts = arg.split(LOOKUP_SEP)
    1013         if not parts:
    1014             raise FieldError("Cannot parse keyword query %r" % arg)
     1039        names, lookup_type, value = self.parse_filter_expr(filter_expr)
    10151040
    1016         # Work out the lookup type and remove it from 'parts', if necessary.
    1017         if len(parts) == 1 or parts[-1] not in self.query_terms:
    1018             lookup_type = 'exact'
    1019         else:
    1020             lookup_type = parts.pop()
    1021 
    1022         # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
    1023         # uses of None as a query value.
    1024         if value is None:
    1025             if lookup_type != 'exact':
    1026                 raise ValueError("Cannot use None as a query value")
    1027             lookup_type = 'isnull'
    1028             value = True
    1029         elif callable(value):
    1030             value = value()
    1031 
    10321041        opts = self.get_meta()
    10331042        alias = self.get_initial_alias()
    10341043        allow_many = trim or not negate
    10351044
    10361045        try:
    1037             field, target, opts, join_list, last = self.setup_joins(parts, opts,
     1046            field, target, opts, join_list, last = self.setup_joins(names, opts,
    10381047                    alias, True, allow_many, can_reuse=can_reuse)
    10391048        except MultiJoin, e:
    1040             self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]))
     1049            self.split_exclude(filter_expr, LOOKUP_SEP.join(names[:e.level]))
    10411050            return
     1051
     1052        self.setup_where(field, target, join_list, last, connector, lookup_type,
     1053            value, negate, trim, can_reuse)
     1054
     1055    def setup_where(self, field, target, join_list, last, connector,
     1056        lookup_type, value, negate, trim, can_reuse):
    10421057        final = len(join_list)
    10431058        penultimate = last.pop()
    10441059        if penultimate == final:
     
    11641179        if subtree:
    11651180            self.where.end_subtree()
    11661181
     1182    def setup_join(self, opts, field, model, direct, m2m, name, joins,
     1183        exclusions, dupe_set, alias, dupe_multis, can_reuse=None):
     1184        """
     1185        Compute a single table join.
     1186        """
     1187        int_alias = None
     1188       
     1189        if model:
     1190            # The field lives on a base class of the current model.
     1191            for int_model in opts.get_base_chain(model):
     1192                lhs_col = opts.parents[int_model].column
     1193                dedupe = lhs_col in opts.duplicate_targets
     1194                if dedupe:
     1195                    exclusions.update(self.dupe_avoidance.get(
     1196                            (id(opts), lhs_col), ()))
     1197                    dupe_set.add((opts, lhs_col))
     1198                opts = int_model._meta
     1199                alias = self.join((alias, opts.db_table, lhs_col,
     1200                        opts.pk.column), exclusions=exclusions)
     1201                joins.append(alias)
     1202                exclusions.add(alias)
     1203                for (dupe_opts, dupe_col) in dupe_set:
     1204                    self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
     1205        cached_data = opts._join_cache.get(name)
     1206        orig_opts = opts
     1207        dupe_col = direct and field.column or field.field.column
     1208        dedupe = dupe_col in opts.duplicate_targets
     1209        if dupe_set or dedupe:
     1210            if dedupe:
     1211                dupe_set.add((opts, dupe_col))
     1212            exclusions.update(self.dupe_avoidance.get((id(opts), dupe_col),
     1213                    ()))
     1214
     1215        if direct:
     1216            if m2m:
     1217                # Many-to-many field defined on the current model.
     1218                if cached_data:
     1219                    (table1, from_col1, to_col1, table2, from_col2,
     1220                            to_col2, opts, target) = cached_data
     1221                else:
     1222                    table1 = field.m2m_db_table()
     1223                    from_col1 = opts.pk.column
     1224                    to_col1 = field.m2m_column_name()
     1225                    opts = field.rel.to._meta
     1226                    table2 = opts.db_table
     1227                    from_col2 = field.m2m_reverse_name()
     1228                    to_col2 = opts.pk.column
     1229                    target = opts.pk
     1230                    orig_opts._join_cache[name] = (table1, from_col1,
     1231                            to_col1, table2, from_col2, to_col2, opts,
     1232                            target)
     1233
     1234                int_alias = self.join((alias, table1, from_col1, to_col1),
     1235                        dupe_multis, exclusions, nullable=True,
     1236                        reuse=can_reuse)
     1237                alias = self.join((int_alias, table2, from_col2, to_col2),
     1238                        dupe_multis, exclusions, nullable=True,
     1239                        reuse=can_reuse)
     1240                joins.extend([int_alias, alias])
     1241            elif field.rel:
     1242                # One-to-one or many-to-one field
     1243                if cached_data:
     1244                    (table, from_col, to_col, opts, target) = cached_data
     1245                else:
     1246                    opts = field.rel.to._meta
     1247                    target = field.rel.get_related_field()
     1248                    table = opts.db_table
     1249                    from_col = field.column
     1250                    to_col = target.column
     1251                    orig_opts._join_cache[name] = (table, from_col, to_col,
     1252                            opts, target)
     1253                alias = self.join((alias, table, from_col, to_col),
     1254                        exclusions=exclusions, nullable=field.null)
     1255                joins.append(alias)
     1256            else:
     1257                # Non-relation fields.
     1258                target = field
     1259                return True, int_alias, alias, field, target, opts
     1260        else:
     1261            orig_field = field
     1262            field = field.field
     1263            if m2m:
     1264                # Many-to-many field defined on the target model.
     1265                if cached_data:
     1266                    (table1, from_col1, to_col1, table2, from_col2,
     1267                            to_col2, opts, target) = cached_data
     1268                else:
     1269                    table1 = field.m2m_db_table()
     1270                    from_col1 = opts.pk.column
     1271                    to_col1 = field.m2m_reverse_name()
     1272                    opts = orig_field.opts
     1273                    table2 = opts.db_table
     1274                    from_col2 = field.m2m_column_name()
     1275                    to_col2 = opts.pk.column
     1276                    target = opts.pk
     1277                    orig_opts._join_cache[name] = (table1, from_col1,
     1278                            to_col1, table2, from_col2, to_col2, opts,
     1279                            target)
     1280
     1281                int_alias = self.join((alias, table1, from_col1, to_col1),
     1282                        dupe_multis, exclusions, nullable=True,
     1283                        reuse=can_reuse)
     1284                alias = self.join((int_alias, table2, from_col2, to_col2),
     1285                        dupe_multis, exclusions, nullable=True,
     1286                        reuse=can_reuse)
     1287                joins.extend([int_alias, alias])
     1288            else:
     1289                # One-to-many field (ForeignKey defined on the target model)
     1290                if cached_data:
     1291                    (table, from_col, to_col, opts, target) = cached_data
     1292                else:
     1293                    local_field = opts.get_field_by_name(
     1294                            field.rel.field_name)[0]
     1295                    opts = orig_field.opts
     1296                    table = opts.db_table
     1297                    from_col = local_field.column
     1298                    to_col = field.column
     1299                    target = opts.pk
     1300                    orig_opts._join_cache[name] = (table, from_col, to_col,
     1301                            opts, target)
     1302
     1303                alias = self.join((alias, table, from_col, to_col),
     1304                        dupe_multis, exclusions, nullable=True,
     1305                        reuse=can_reuse)
     1306                joins.append(alias)
     1307
     1308        for (dupe_opts, dupe_col) in dupe_set:
     1309            if int_alias is None:
     1310                self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
     1311            else:
     1312                self.update_dupe_avoidance(dupe_opts, dupe_col, int_alias)
     1313
     1314        return False, int_alias, alias, field, target, opts
     1315
    11671316    def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
    11681317            allow_explicit_fk=False, can_reuse=None):
    11691318        """
     
    11841333        last = [0]
    11851334        dupe_set = set()
    11861335        exclusions = set()
     1336        int_alias = None
    11871337        for pos, name in enumerate(names):
    1188             try:
     1338            if int_alias:
    11891339                exclusions.add(int_alias)
    1190             except NameError:
    1191                 pass
    11921340            exclusions.add(alias)
    11931341            last.append(len(joins))
    11941342            if name == 'pk':
     
    12131361                for alias in joins:
    12141362                    self.unref_alias(alias)
    12151363                raise MultiJoin(pos + 1)
    1216             if model:
    1217                 # The field lives on a base class of the current model.
    1218                 for int_model in opts.get_base_chain(model):
    1219                     lhs_col = opts.parents[int_model].column
    1220                     dedupe = lhs_col in opts.duplicate_targets
    1221                     if dedupe:
    1222                         exclusions.update(self.dupe_avoidance.get(
    1223                                 (id(opts), lhs_col), ()))
    1224                         dupe_set.add((opts, lhs_col))
    1225                     opts = int_model._meta
    1226                     alias = self.join((alias, opts.db_table, lhs_col,
    1227                             opts.pk.column), exclusions=exclusions)
    1228                     joins.append(alias)
    1229                     exclusions.add(alias)
    1230                     for (dupe_opts, dupe_col) in dupe_set:
    1231                         self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
    1232             cached_data = opts._join_cache.get(name)
    1233             orig_opts = opts
    1234             dupe_col = direct and field.column or field.field.column
    1235             dedupe = dupe_col in opts.duplicate_targets
    1236             if dupe_set or dedupe:
    1237                 if dedupe:
    1238                     dupe_set.add((opts, dupe_col))
    1239                 exclusions.update(self.dupe_avoidance.get((id(opts), dupe_col),
    1240                         ()))
    12411364
    1242             if direct:
    1243                 if m2m:
    1244                     # Many-to-many field defined on the current model.
    1245                     if cached_data:
    1246                         (table1, from_col1, to_col1, table2, from_col2,
    1247                                 to_col2, opts, target) = cached_data
    1248                     else:
    1249                         table1 = field.m2m_db_table()
    1250                         from_col1 = opts.pk.column
    1251                         to_col1 = field.m2m_column_name()
    1252                         opts = field.rel.to._meta
    1253                         table2 = opts.db_table
    1254                         from_col2 = field.m2m_reverse_name()
    1255                         to_col2 = opts.pk.column
    1256                         target = opts.pk
    1257                         orig_opts._join_cache[name] = (table1, from_col1,
    1258                                 to_col1, table2, from_col2, to_col2, opts,
    1259                                 target)
    1260 
    1261                     int_alias = self.join((alias, table1, from_col1, to_col1),
    1262                             dupe_multis, exclusions, nullable=True,
    1263                             reuse=can_reuse)
    1264                     alias = self.join((int_alias, table2, from_col2, to_col2),
    1265                             dupe_multis, exclusions, nullable=True,
    1266                             reuse=can_reuse)
    1267                     joins.extend([int_alias, alias])
    1268                 elif field.rel:
    1269                     # One-to-one or many-to-one field
    1270                     if cached_data:
    1271                         (table, from_col, to_col, opts, target) = cached_data
    1272                     else:
    1273                         opts = field.rel.to._meta
    1274                         target = field.rel.get_related_field()
    1275                         table = opts.db_table
    1276                         from_col = field.column
    1277                         to_col = target.column
    1278                         orig_opts._join_cache[name] = (table, from_col, to_col,
    1279                                 opts, target)
    1280 
    1281                     alias = self.join((alias, table, from_col, to_col),
    1282                             exclusions=exclusions, nullable=field.null)
    1283                     joins.append(alias)
    1284                 else:
    1285                     # Non-relation fields.
    1286                     target = field
    1287                     break
    1288             else:
    1289                 orig_field = field
    1290                 field = field.field
    1291                 if m2m:
    1292                     # Many-to-many field defined on the target model.
    1293                     if cached_data:
    1294                         (table1, from_col1, to_col1, table2, from_col2,
    1295                                 to_col2, opts, target) = cached_data
    1296                     else:
    1297                         table1 = field.m2m_db_table()
    1298                         from_col1 = opts.pk.column
    1299                         to_col1 = field.m2m_reverse_name()
    1300                         opts = orig_field.opts
    1301                         table2 = opts.db_table
    1302                         from_col2 = field.m2m_column_name()
    1303                         to_col2 = opts.pk.column
    1304                         target = opts.pk
    1305                         orig_opts._join_cache[name] = (table1, from_col1,
    1306                                 to_col1, table2, from_col2, to_col2, opts,
    1307                                 target)
    1308 
    1309                     int_alias = self.join((alias, table1, from_col1, to_col1),
    1310                             dupe_multis, exclusions, nullable=True,
    1311                             reuse=can_reuse)
    1312                     alias = self.join((int_alias, table2, from_col2, to_col2),
    1313                             dupe_multis, exclusions, nullable=True,
    1314                             reuse=can_reuse)
    1315                     joins.extend([int_alias, alias])
    1316                 else:
    1317                     # One-to-many field (ForeignKey defined on the target model)
    1318                     if cached_data:
    1319                         (table, from_col, to_col, opts, target) = cached_data
    1320                     else:
    1321                         local_field = opts.get_field_by_name(
    1322                                 field.rel.field_name)[0]
    1323                         opts = orig_field.opts
    1324                         table = opts.db_table
    1325                         from_col = local_field.column
    1326                         to_col = field.column
    1327                         target = opts.pk
    1328                         orig_opts._join_cache[name] = (table, from_col, to_col,
    1329                                 opts, target)
    1330 
    1331                     alias = self.join((alias, table, from_col, to_col),
    1332                             dupe_multis, exclusions, nullable=True,
    1333                             reuse=can_reuse)
    1334                     joins.append(alias)
    1335 
    1336             for (dupe_opts, dupe_col) in dupe_set:
    1337                 try:
    1338                     self.update_dupe_avoidance(dupe_opts, dupe_col, int_alias)
    1339                 except NameError:
    1340                     self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
    1341 
     1365            final, int_alias, alias, field, target, opts = self.setup_join(opts,
     1366                field, model, direct, m2m, name, joins, exclusions,
     1367                dupe_set, alias, dupe_multis, can_reuse=can_reuse)
     1368            if final:
     1369                break
     1370       
    13421371        if pos != len(names) - 1:
    13431372            raise FieldError("Join on field %r not permitted." % name)
    13441373
Back to Top