def add_projected_column(self, column): self.init_list.append(column) if not self.projection_possible: # If we previously tried to add a column that couldn't be # projected, we don't try and add any more return field = get_field_from_column(self.model, column) if field is None: raise NotSupportedError( "{} is not a valid column for the queried model. Did you try to join?" .format(column)) if field.db_type(self.connection) in ("bytes", "text", "list", "set"): DJANGAE_LOG.warn( "Disabling projection query as %s is an unprojectable type", column) self.columns = None self.projection_possible = False return if not self.columns: self.columns = set([column]) else: self.columns.add(column)
def add_projected_column(self, column): self.init_list.append(column) if not self.projection_possible: # If we previously tried to add a column that couldn't be # projected, we don't try and add any more return field = get_field_from_column(self.model, column) if field is None: raise NotSupportedError("{} is not a valid column for the queried model. Did you try to join?".format(column)) if field.db_type(self.connection) in ("bytes", "text", "list", "set"): DJANGAE_LOG.warn("Disabling projection query as %s is an unprojectable type", column) self.columns = None self.projection_possible = False return if not self.columns: self.columns = set([ column ]) else: self.columns.add(column)
def _build_query(self): self._sanity_check() queries = [] projection = self._exclude_pk(self.query.columns) or None query_kwargs = { "kind": self.query.concrete_model._meta.db_table, "distinct": self.query.distinct or None, "keys_only": self.keys_only or None, "projection": projection, } ordering = convert_django_ordering_to_gae(self.query.order_by) if self.query.distinct and not ordering: # If we specified we wanted a distinct query, but we didn't specify # an ordering, we must set the ordering to the distinct columns, otherwise # App Engine shouts at us. Nastily. And without remorse. ordering = self.query.columns[:] # Deal with the no filters case if self.query.where is None: query = Query(**query_kwargs) try: query.Order(*ordering) except datastore_errors.BadArgumentError as e: raise NotSupportedError(e) return query assert self.query.where # Go through the normalized query tree for and_branch in self.query.where.children: query = Query(**query_kwargs) # This deals with the oddity that the root of the tree may well be a leaf filters = [and_branch] if and_branch.is_leaf else and_branch.children for filter_node in filters: lookup = "{} {}".format(filter_node.column, filter_node.operator) value = filter_node.value # This is a special case. Annoyingly Django's decimal field doesn't # ever call ops.get_prep_save or lookup or whatever when you are filtering # on a query. It *does* do it on a save, so we basically need to do a # conversion here, when really it should be handled elsewhere if isinstance(value, decimal.Decimal): field = get_field_from_column(self.query.model, filter_node.column) value = self.connection.ops.value_to_db_decimal(value, field.max_digits, field.decimal_places) elif isinstance(value, basestring): value = coerce_unicode(value) # If there is already a value for this lookup, we need to make the # value a list and append the new entry if lookup in query and not isinstance(query[lookup], (list, tuple)) and query[lookup] != value: query[lookup] = [query[lookup]] + [value] else: # If the value is a list, we can't just assign it to the query # which will treat each element as its own value. So in this # case we nest it. This has the side effect of throwing a BadValueError # which we could throw ourselves, but the datastore might start supporting # list values in lookups.. you never know! if isinstance(value, (list, tuple)): query[lookup] = [value] else: # Common case: just add the raw where constraint query[lookup] = value if ordering: try: query.Order(*ordering) except datastore_errors.BadArgumentError as e: # This is the easiest way to detect unsupported orderings # ideally we'd detect this at the query normalization stage # but it's a lot of hassle, this is much easier and seems to work OK raise NotSupportedError(e) queries.append(query) if can_perform_datastore_get(self.query): # Yay for optimizations! return QueryByKeys(self.query.model, queries, ordering) if len(queries) == 1: identifier = query_is_unique(self.query.model, queries[0]) if identifier: # Yay for optimizations! return UniqueQuery(identifier, queries[0], self.query.model) return queries[0] else: return datastore.MultiQuery(queries, ordering)
def _build_query(self): self._sanity_check() queries = [] projection = self._exclude_pk(self.query.columns) or None query_kwargs = { "kind": self.query.concrete_model._meta.db_table, "distinct": self.query.distinct or None, "keys_only": self.keys_only or None, "projection": projection } ordering = convert_django_ordering_to_gae(self.query.order_by) if self.query.distinct and not ordering: # If we specified we wanted a distinct query, but we didn't specify # an ordering, we must set the ordering to the distinct columns, otherwise # App Engine shouts at us. Nastily. And without remorse. ordering = self.query.columns[:] # Deal with the no filters case if self.query.where is None: query = Query(**query_kwargs) try: query.Order(*ordering) except datastore_errors.BadArgumentError as e: raise NotSupportedError(e) return query assert self.query.where # Go through the normalized query tree for and_branch in self.query.where.children: query = Query(**query_kwargs) # This deals with the oddity that the root of the tree may well be a leaf filters = [and_branch ] if and_branch.is_leaf else and_branch.children for filter_node in filters: lookup = "{} {}".format(filter_node.column, filter_node.operator) value = filter_node.value # This is a special case. Annoyingly Django's decimal field doesn't # ever call ops.get_prep_save or lookup or whatever when you are filtering # on a query. It *does* do it on a save, so we basically need to do a # conversion here, when really it should be handled elsewhere if isinstance(value, decimal.Decimal): field = get_field_from_column(self.query.model, filter_node.column) value = self.connection.ops.value_to_db_decimal( value, field.max_digits, field.decimal_places) elif isinstance(value, basestring): value = unicode(value) # If there is already a value for this lookup, we need to make the # value a list and append the new entry if lookup in query and not isinstance( query[lookup], (list, tuple)) and query[lookup] != value: query[lookup] = [query[lookup]] + [value] else: # If the value is a list, we can't just assign it to the query # which will treat each element as its own value. So in this # case we nest it. This has the side effect of throwing a BadValueError # which we could throw ourselves, but the datastore might start supporting # list values in lookups.. you never know! if isinstance(value, (list, tuple)): query[lookup] = [value] else: # Common case: just add the raw where constraint query[lookup] = value if ordering: try: query.Order(*ordering) except datastore_errors.BadArgumentError as e: # This is the easiest way to detect unsupported orderings # ideally we'd detect this at the query normalization stage # but it's a lot of hassle, this is much easier and seems to work OK raise NotSupportedError(e) queries.append(query) if can_perform_datastore_get(self.query): # Yay for optimizations! return QueryByKeys(self.query.model, queries, ordering) if len(queries) == 1: identifier = query_is_unique(self.query.model, queries[0]) if identifier: # Yay for optimizations! return UniqueQuery(identifier, queries[0], self.query.model) return queries[0] else: return datastore.MultiQuery(queries, ordering)