示例#1
0
def _extract_ordering_from_query_18(query):
    from djangae.db.backends.appengine.commands import log_once
    from django.db.models.expressions import OrderBy, F

    # Add any orderings
    if not query.default_ordering:
        result = list(query.order_by)
    else:
        result = list(query.order_by or query.get_meta().ordering or [])

    if query.extra_order_by:
        result = list(query.extra_order_by)

        # we need some extra logic to handle dot seperated ordering
        new_result = []
        cross_table_ordering = set()
        for ordering in result:
            if "." in ordering:
                dot_based_ordering = ordering.split(".")
                if dot_based_ordering[0] == query.model._meta.db_table:
                    ordering = dot_based_ordering[1]
                elif dot_based_ordering[0].lstrip('-') == query.model._meta.db_table:
                    ordering = '-{}'.format(dot_based_ordering[1])
                else:
                    cross_table_ordering.add(ordering)
                    continue # we don't want to add this ordering value
            new_result.append(ordering)

        if len(cross_table_ordering):
            log_once(
                DJANGAE_LOG.warning if environment.is_development_environment() else DJANGAE_LOG.debug,
                "The following orderings were ignored as cross-table orderings are not supported on the datastore: %s", cross_table_ordering
            )

        result = new_result

    final = []

    opts = query.model._meta

    # Apparently expression ordering is absolute and so shouldn't be flipped
    # if the standard_ordering is False. This keeps track of which columns
    # were expressions and so don't need flipping
    expressions = set()

    for col in result:
        if isinstance(col, OrderBy):
            descending = col.descending
            col = col.expression.name
            if descending:
                col = "-" + col
            expressions.add(col)

        elif isinstance(col, F):
            col = col.name

        if isinstance(col, (int, long)):
            # If you do a Dates query, the ordering is set to [1] or [-1]... which is weird
            # I think it's to select the column number but then there is only 1 column so
            # unless the ordinal is one-based I have no idea. So basically if it's an integer
            # subtract 1 from the absolute value and look up in the select for the column (guessing)
            idx = abs(col) - 1
            try:
                field_name = query.select[idx].col.col[-1]
                field = query.model._meta.get_field(field_name)
                final.append("-" + field.column if col < 0 else field.column)
            except IndexError:
                raise NotSupportedError("Unsupported order_by %s" % col)
        elif col.lstrip("-") == "pk":
            pk_col = "__key__"
            final.append("-" + pk_col if col.startswith("-") else pk_col)
        elif col == "?":
            raise NotSupportedError("Random ordering is not supported on the datastore")
        elif col.lstrip("-").startswith("__") and col.endswith("__"):
            # Allow stuff like __scatter__
            final.append(col)
        elif "__" in col:
            continue
        else:
            try:
                column = col.lstrip("-")

                # This is really 1.8 only, but I didn't want to duplicate this function
                # just for this. Suggestions for doing this more cleanly welcome!
                if column in getattr(query, "annotation_select", {}):
                    # It's an annotation, if it's a supported one, return the
                    # original column
                    annotation = query.annotation_select[column]
                    name = annotation.__class__.__name__

                    # We only support a few expressions
                    if name not in ("Col", "Date", "DateTime"):
                        raise NotSupportedError("Tried to order by unsupported expression")
                    else:
                        # Retrieve the original column and use that for ordering
                        if name == "Col":
                            column = annotation.output_field.column
                        else:
                            column = annotation.col.output_field.column

                field = query.model._meta.get_field(column)

                if field.get_internal_type() in (u"TextField", u"BinaryField"):
                    raise NotSupportedError(INVALID_ORDERING_FIELD_MESSAGE)

                # If someone orders by 'fk' rather than 'fk_id' this complains as that should take
                # into account the related model ordering. Note the difference between field.name == column
                # and field.attname (X_id)
                if field.related_model and field.name == column and field.related_model._meta.ordering:
                    raise NotSupportedError("Related ordering is not supported on the datastore")

                column = "__key__" if field.primary_key else field.column
                final.append("-" + column if col.startswith("-") else column)
            except FieldDoesNotExist:
                if col in query.extra_select:
                    # If the column is in the extra select we transform to the original
                    # column
                    try:
                        field = opts.get_field(query.extra_select[col][0])
                        column = "__key__" if field.primary_key else field.column
                        final.append("-" + column if col.startswith("-") else column)
                        continue
                    except FieldDoesNotExist:
                        # Just pass through to the exception below
                        pass

                available = opts.get_all_field_names()
                raise FieldError("Cannot resolve keyword %r into field. "
                    "Choices are: %s" % (col, ", ".join(available))
                )

    # Reverse if not using standard ordering
    def swap(col):
        if col.startswith("-"):
            return col.lstrip("-")
        else:
            return "-{}".format(col)

    if not query.standard_ordering:
        final = [ x if x in expressions else swap(x) for x in final ]

    if len(final) != len(result):
        diff = set(result) - set(final)
        log_once(
            DJANGAE_LOG.warning if environment.is_development_environment() else DJANGAE_LOG.debug,
            "The following orderings were ignored as cross-table and random orderings are not supported on the datastore: %s", diff
        )

    return final
示例#2
0
def _extract_ordering_from_query_17(query):
    from djangae.db.backends.appengine.commands import log_once

    # Add any orderings
    if not query.default_ordering:
        result = list(query.order_by)
    else:
        result = list(query.order_by or query.get_meta().ordering or [])

    if query.extra_order_by:
        result = list(query.extra_order_by)

    final = []

    opts = query.model._meta

    for col in result:
        if isinstance(col, (int, long)):
            # If you do a Dates query, the ordering is set to [1] or [-1]... which is weird
            # I think it's to select the column number but then there is only 1 column so
            # unless the ordinal is one-based I have no idea. So basically if it's an integer
            # subtract 1 from the absolute value and look up in the select for the column (guessing)
            idx = abs(col) - 1
            try:
                field_name = query.select[idx].col.col[-1]
                field = query.model._meta.get_field_by_name(field_name)[0]
                final.append("-" + field.column if col < 0 else field.column)
            except IndexError:
                raise NotSupportedError("Unsupported order_by %s" % col)

        elif col.lstrip("-") == "pk":
            pk_col = "__key__"
            final.append("-" + pk_col if col.startswith("-") else pk_col)
        elif col == "?":
            raise NotSupportedError("Random ordering is not supported on the datastore")
        elif col.lstrip("-").startswith("__") and col.endswith("__"):
            # Allow stuff like __scatter__
            final.append(col)
        elif "__" in col:
            continue
        else:
            try:
                column = col.lstrip("-")
                field = query.model._meta.get_field_by_name(column)[0]
                if field.get_internal_type()  in (u"TextField", u"BinaryField"):
                    raise NotSupportedError(INVALID_ORDERING_FIELD_MESSAGE)
                column = "__key__" if field.primary_key else field.column
                final.append("-" + column if col.startswith("-") else column)
            except FieldDoesNotExist:
                if col in query.extra_select:
                    # If the column is in the extra select we transform to the original
                    # column
                    try:
                        field = opts.get_field_by_name(query.extra_select[col][0])[0]
                        column = "__key__" if field.primary_key else field.column
                        final.append("-" + column if col.startswith("-") else column)
                        continue
                    except FieldDoesNotExist:
                        # Just pass through to the exception below
                        pass

                available = opts.get_all_field_names()
                raise FieldError("Cannot resolve keyword %r into field. "
                    "Choices are: %s" % (col, ", ".join(available))
                )

    # Reverse if not using standard ordering
    def swap(col):
        if col.startswith("-"):
            return col.lstrip("-")
        else:
            return "-{}".format(col)

    if not query.standard_ordering:
        final = [ swap(x) for x in final ]

    if len(final) != len(result):
        diff = set(result) - set(final)
        log_once(
            DJANGAE_LOG.warning if not on_production() else DJANGAE_LOG.debug,
            "The following orderings were ignored as cross-table and random orderings are not supported on the datastore: %s", diff
        )

    return final