Exemple #1
0
 def test_expressions(self):
     self.assertEqual(
         repr(Case(When(a=1))),
         "<Case: CASE WHEN <Q: (AND: ('a', 1))> THEN Value(None), ELSE Value(None)>"
     )
     self.assertEqual(repr(Col('alias', 'field')), "Col(alias, field)")
     self.assertEqual(repr(Date('published', 'exact')),
                      "Date(published, exact)")
     self.assertEqual(repr(DateTime('published', 'exact', utc)),
                      "DateTime(published, exact, %s)" % utc)
     self.assertEqual(repr(F('published')), "F(published)")
     self.assertEqual(repr(F('cost') + F('tax')),
                      "<CombinedExpression: F(cost) + F(tax)>")
     self.assertEqual(
         repr(ExpressionWrapper(
             F('cost') + F('tax'), models.IntegerField())),
         "ExpressionWrapper(F(cost) + F(tax))")
     self.assertEqual(repr(Func('published', function='TO_CHAR')),
                      "Func(F(published), function=TO_CHAR)")
     self.assertEqual(repr(OrderBy(Value(1))),
                      'OrderBy(Value(1), descending=False)')
     self.assertEqual(repr(Random()), "Random()")
     self.assertEqual(repr(RawSQL('table.col', [])),
                      "RawSQL(table.col, [])")
     self.assertEqual(repr(Ref('sum_cost', Sum('cost'))),
                      "Ref(sum_cost, Sum(F(cost)))")
     self.assertEqual(repr(Value(1)), "Value(1)")
Exemple #2
0
def resolve_ref_temp_fix(self,
                         name,
                         allow_joins=True,
                         reuse=None,
                         summarize=False):
    if not allow_joins and LOOKUP_SEP in name:
        raise FieldError(
            "Joined field references are not permitted in this query")
    if name in self.annotations:
        if summarize:
            # Summarize currently means we are doing an aggregate() query
            # which is executed as a wrapped subquery if any of the
            # aggregate() elements reference an existing annotation. In
            # that case we need to return a Ref to the subquery's annotation.
            return Ref(name, self.annotation_select[name])
        else:
            return self.annotations[name]
    else:
        field_list = name.split(LOOKUP_SEP)
        field, sources, opts, join_list, path = self.setup_joins(
            field_list, self.get_meta(), self.get_initial_alias(), reuse)
        targets, _, join_list = self.trim_joins(sources, join_list, path)
        if len(targets) > 1:
            raise FieldError("Referencing multicolumn fields with F() objects "
                             "isn't supported")
        if reuse is not None:
            reuse.update(join_list)
        col = targets[0].get_col(join_list[-1], sources[0])

        return col
Exemple #3
0
 def resolve_ref(self,
                 name,
                 allow_joins=True,
                 reuse=None,
                 summarize=False,
                 *args,
                 **kwargs):
     # This method is used to resolve field names in complex expressions. If
     # a queryable property is used in such an expression, it needs to be
     # auto-annotated (while taking the stack into account) and returned.
     query_path = QueryPath(name)
     if self._queryable_property_stack:
         query_path = self._queryable_property_stack[
             -1].relation_path + query_path
     property_annotation = self._auto_annotate(query_path,
                                               full_group_by=ValuesQuerySet
                                               is not None)
     if property_annotation:
         if summarize:
             # Outer queries for aggregations need refs to annotations of
             # the inner queries.
             from django.db.models.expressions import Ref
             return Ref(name, property_annotation)
         return property_annotation
     return super(QueryablePropertiesQueryMixin,
                  self).resolve_ref(name, allow_joins, reuse, summarize,
                                    *args, **kwargs)
def get_order_by(self):
    """
    See original get_group_by at django.db.models.sql.compiler>SQLCompiler
    """
    if self.query.extra_order_by:
        ordering = self.query.extra_order_by
    elif not self.query.default_ordering:
        ordering = self.query.order_by
    else:
        ordering = (self.query.order_by or self.query.get_meta().ordering
                    or [])
    if self.query.standard_ordering:
        asc, desc = ORDER_DIR['ASC']
    else:
        asc, desc = ORDER_DIR['DESC']

    order_by = []
    for field in ordering:
        if hasattr(field, 'resolve_expression'):
            if not isinstance(field, OrderBy):
                field = field.asc()
            if not self.query.standard_ordering:
                field.reverse_ordering()
            order_by.append((field, False))
            continue
        if field == '?':  # random
            order_by.append((OrderBy(Random()), False))
            continue

        col, order = get_order_dir(field, asc)
        descending = True if order == 'DESC' else False

        if col in self.query.annotation_select:
            # Reference to expression in SELECT clause
            order_by.append((OrderBy(Ref(col,
                                         self.query.annotation_select[col]),
                                     descending=descending), True))
            continue
        if col in self.query.annotations:
            # References to an expression which is masked out of the SELECT clause
            order_by.append((OrderBy(self.query.annotations[col],
                                     descending=descending), False))
            continue

        if '.' in field:
            # This came in through an extra(order_by=...) addition. Pass it
            # on verbatim.
            table, col = col.split('.', 1)
            order_by.append((OrderBy(RawSQL(
                '%s.%s' % (self.quote_name_unless_alias(table), col), []),
                                     descending=descending), False))
            continue

        if not self.query._extra or col not in self.query._extra:
            # 'col' is of the form 'field' or 'field1__field2' or
            # '-field1__field2__field', etc.
            order_by.extend(
                self.find_ordering_name(field,
                                        self.query.get_meta(),
                                        default_order=asc))
        else:
            if col not in self.query.extra_select:
                order_by.append((OrderBy(RawSQL(*self.query.extra[col]),
                                         descending=descending), False))
            else:
                order_by.append((OrderBy(Ref(col,
                                             RawSQL(*self.query.extra[col])),
                                         descending=descending), True))
    result = []
    # changed from set() to []
    seen = []

    for expr, is_ref in order_by:
        if self.query.combinator:
            src = expr.get_source_expressions()[0]
            # Relabel order by columns to raw numbers if this is a combined
            # query; necessary since the columns can't be referenced by the
            # fully qualified name and the simple column names may collide.
            for idx, (sel_expr, _, col_alias) in enumerate(self.select):
                if is_ref and col_alias == src.refs:
                    src = src.source
                elif col_alias:
                    continue
                if src == sel_expr:
                    expr.set_source_expressions([RawSQL('%d' % (idx + 1), ())])
                    break
            else:
                raise DatabaseError(
                    'ORDER BY term does not match any column in the result set.'
                )
        resolved = expr.resolve_expression(self.query,
                                           allow_joins=True,
                                           reuse=None)
        sql, params = self.compile(resolved)
        # Don't add the same column twice, but the order direction is
        # not taken into account so we strip it. When this entire method
        # is refactored into expressions, then we can check each part as we
        # generate it.
        without_ordering = self.ordering_parts.search(sql).group(1)
        if (without_ordering, tuple(params)) in seen:
            continue
        # changed from add to append
        seen.append((without_ordering, tuple(params)))
        result.append((resolved, (sql, params, is_ref)))
    return result
Exemple #5
0
    def get_order_by(self):
        """
        Returns a list of 2-tuples of form (expr, (sql, params)) for the
        ORDER BY clause.

        The order_by clause can alter the select clause (for example it
        can add aliases to clauses that do not yet have one, or it can
        add totally new select clauses).
        """
        if self.query.extra_order_by:
            ordering = self.query.extra_order_by
        elif not self.query.default_ordering:
            ordering = self.query.order_by
        else:
            ordering = (self.query.order_by or self.query.get_meta().ordering
                        or [])
        if self.query.standard_ordering:
            asc, desc = ORDER_DIR['ASC']
        else:
            asc, desc = ORDER_DIR['DESC']

        order_by = []
        for pos, field in enumerate(ordering):
            if hasattr(field, 'resolve_expression'):
                if not isinstance(field, OrderBy):
                    field = field.asc()
                if not self.query.standard_ordering:
                    field.reverse_ordering()
                order_by.append((field, False))
                continue
            if field == '?':  # random
                order_by.append((OrderBy(Random()), False))
                continue

            col, order = get_order_dir(field, asc)
            descending = True if order == 'DESC' else False

            if col in self.query.annotation_select:
                order_by.append(
                    (OrderBy(Ref(col, self.query.annotation_select[col]),
                             descending=descending), True))
                continue

            if '.' in field:
                # This came in through an extra(order_by=...) addition. Pass it
                # on verbatim.
                table, col = col.split('.', 1)
                order_by.append((OrderBy(RawSQL(
                    '%s.%s' % (self.quote_name_unless_alias(table), col), []),
                                         descending=descending), False))
                continue

            if not self.query._extra or col not in self.query._extra:
                # 'col' is of the form 'field' or 'field1__field2' or
                # '-field1__field2__field', etc.
                order_by.extend(
                    self.find_ordering_name(field,
                                            self.query.get_meta(),
                                            default_order=asc))
            else:
                if col not in self.query.extra_select:
                    order_by.append((OrderBy(RawSQL(*self.query.extra[col]),
                                             descending=descending), False))
                else:
                    order_by.append(
                        (OrderBy(Ref(col, RawSQL(*self.query.extra[col])),
                                 descending=descending), True))
        result = []
        seen = set()

        for expr, is_ref in order_by:
            resolved = expr.resolve_expression(self.query,
                                               allow_joins=True,
                                               reuse=None)
            sql, params = self.compile(resolved)
            # Don't add the same column twice, but the order direction is
            # not taken into account so we strip it. When this entire method
            # is refactored into expressions, then we can check each part as we
            # generate it.
            without_ordering = self.ordering_parts.search(sql).group(1)
            if (without_ordering, tuple(params)) in seen:
                continue
            seen.add((without_ordering, tuple(params)))
            result.append((resolved, (sql, params, is_ref)))
        return result
Exemple #6
0
    def get_order_by(self):
        """
        Return a list of 2-tuples of form (expr, (sql, params, is_ref)) for the
        ORDER BY clause.

        The order_by clause can alter the select clause (for example it
        can add aliases to clauses that do not yet have one, or it can
        add totally new select clauses).
        """
        if self.query.extra_order_by:
            ordering = self.query.extra_order_by
        elif not self.query.default_ordering:
            ordering = self.query.order_by
        elif self.query.order_by:
            ordering = self.query.order_by
        elif self.query.get_meta().ordering:
            ordering = self.query.get_meta().ordering
            self._meta_ordering = ordering
        else:
            ordering = []
        if self.query.standard_ordering:
            asc, desc = ORDER_DIR['ASC']
        else:
            asc, desc = ORDER_DIR['DESC']

        order_by = []
        for field in ordering:
            if hasattr(field, 'resolve_expression'):
                if isinstance(field, Value):
                    # output_field must be resolved for constants.
                    field = Cast(field, field.output_field)
                if not isinstance(field, OrderBy):
                    field = field.asc()
                if not self.query.standard_ordering:
                    field = field.copy()
                    field.reverse_ordering()
                    order_by.append((field, True))
                else:
                    order_by.append((field, False))
                continue
            if field == '?':  # random
                order_by.append((OrderBy(Random()), False))
                continue

            col, order = get_order_dir(field, asc)
            descending = order == 'DESC'

            if col in self.query.annotation_select:
                # Reference to expression in SELECT clause
                order_by.append(
                    (OrderBy(Ref(col, self.query.annotation_select[col]),
                             descending=descending), True))
                continue
            if col in self.query.annotations:
                # References to an expression which is masked out of the SELECT
                # clause.
                if self.query.combinator and self.select:
                    # Don't use the resolved annotation because other
                    # combined queries might define it differently.
                    expr = F(col)
                else:
                    expr = self.query.annotations[col]
                    if isinstance(expr, Value):
                        # output_field must be resolved for constants.
                        expr = Cast(expr, expr.output_field)
                order_by.append((OrderBy(expr, descending=descending), False))
                continue

            if '.' in field:
                # This came in through an extra(order_by=...) addition. Pass it
                # on verbatim.
                table, col = col.split('.', 1)
                order_by.append((OrderBy(RawSQL(
                    '%s.%s' % (self.quote_name_unless_alias(table), col), []),
                                         descending=descending), False))
                continue

            if not self.query.extra or col not in self.query.extra:
                if self.query.combinator and self.select:
                    # Don't use the first model's field because other
                    # combined queries might define it differently.
                    order_by.append((OrderBy(F(col),
                                             descending=descending), False))
                else:
                    # 'col' is of the form 'field' or 'field1__field2' or
                    # '-field1__field2__field', etc.
                    order_by.extend(
                        self.find_ordering_name(field,
                                                self.query.get_meta(),
                                                default_order=asc))
            else:
                if col not in self.query.extra_select:
                    order_by.append((OrderBy(RawSQL(*self.query.extra[col]),
                                             descending=descending), False))
                else:
                    order_by.append(
                        (OrderBy(Ref(col, RawSQL(*self.query.extra[col])),
                                 descending=descending), True))
        result = []
        seen = set()

        for expr, is_ref in order_by:
            resolved = expr.resolve_expression(self.query,
                                               allow_joins=True,
                                               reuse=None)
            if self.query.combinator and self.select:
                src = resolved.get_source_expressions()[0]
                expr_src = expr.get_source_expressions()[0]
                # Relabel order by columns to raw numbers if this is a combined
                # query; necessary since the columns can't be referenced by the
                # fully qualified name and the simple column names may collide.
                for idx, (sel_expr, _, col_alias) in enumerate(self.select):
                    if is_ref and col_alias == src.refs:
                        src = src.source
                    elif col_alias and not (isinstance(expr_src, F)
                                            and col_alias == expr_src.name):
                        continue
                    if src == sel_expr:
                        resolved.set_source_expressions(
                            [RawSQL('%d' % (idx + 1), ())])
                        break
                else:
                    if col_alias:
                        raise DatabaseError(
                            'ORDER BY term does not match any column in the result set.'
                        )
                    # Add column used in ORDER BY clause without an alias to
                    # the selected columns.
                    order_by_idx = len(self.query.select) + 1
                    col_name = f'__orderbycol{order_by_idx}'
                    for q in self.query.combined_queries:
                        q.add_annotation(expr_src, col_name)
                    self.query.add_select_col(src, col_name)
                    resolved.set_source_expressions(
                        [RawSQL(f'{order_by_idx}', ())])

            sql, params = self.compile(resolved)
            # Don't add the same column twice, but the order direction is
            # not taken into account so we strip it. When this entire method
            # is refactored into expressions, then we can check each part as we
            # generate it.
            without_ordering = self.ordering_parts.search(sql)[1]
            params_hash = make_hashable(params)
            if (without_ordering, params_hash) in seen:
                continue
            seen.add((without_ordering, params_hash))
            result.append((resolved, (sql, params, is_ref)))
        return result