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
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
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
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