Esempio n. 1
0
    def get_extracted_ordering(self):
        from djangae.db.backends.appengine.commands import log_once
        from django.db.models.expressions import OrderBy, F

        query = self.django_query

        # 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(
                    logger.warning if environment.is_development_environment()
                    else logger.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 ("Trunc", "Col", "Date", "DateTime"):
                            raise NotSupportedError(
                                "Tried to order by unsupported expression")
                        elif name == "Trunc":
                            column = annotation.lhs.output_field.column
                        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(
                logger.warning
                if environment.is_development_environment() else logger.debug,
                "The following orderings were ignored as cross-table and random orderings are not supported on the datastore: %s",
                diff)

        return final
Esempio n. 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 "__" in col:
            continue
        else:
            try:
                column = col.lstrip("-")
                field = query.model._meta.get_field_by_name(column)[0]
                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
Esempio n. 3
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
Esempio n. 4
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