def add_select(self, field_name, lookup_type, order='ASC'): """ Converts the query into an extraction query. """ try: result = self.setup_joins( field_name.split(LOOKUP_SEP), self.get_meta(), self.get_initial_alias(), ) except FieldError: raise FieldDoesNotExist("%s has no field named '%s'" % ( self.get_meta().object_name, field_name )) field = result[0] self._check_field(field) # overridden in DateTimeQuery alias = result[3][-1] select = self._get_select((alias, field.column), lookup_type) self.clear_select_clause() self.select = [SelectInfo(select, None)] self.distinct = True self.order_by = [1] if order == 'ASC' else [-1] if field.null: self.add_filter(("%s__isnull" % field_name, False))
def add_fields(self, field_names, allow_m2m=True): """ Adds the given (model) fields to the select set. The field names are added in the order specified. """ alias = self.get_initial_alias() opts = self.get_meta() try: for name in field_names: field, targets, u2, joins, path = self.setup_joins( name.split(LOOKUP_SEP), opts, alias, None, allow_m2m, allow_explicit_fk=True, outer_if_first=True) # Trim last join if possible targets, final_alias, remaining_joins = self.trim_joins( targets, joins[-2:], path) joins = joins[:-2] + remaining_joins self.promote_joins(joins[1:]) for target in targets: col = target.column cols = [] if hasattr(col, "columns") else [col] for col in cols: print "select", final_alias, col, target self.select.append(SelectInfo((final_alias, col), target)) except MultiJoin: raise FieldError("Invalid field name: '%s'" % name) except FieldError: if LOOKUP_SEP in name: # For lookups spanning over relationships, show the error # from the model on which the lookup failed. raise else: names = sorted(opts.get_all_field_names() + list(self.extra) + list(self.aggregate_select)) raise FieldError("Cannot resolve keyword %r into field. " "Choices are: %s" % (name, ", ".join(names))) self.remove_inherited_models()
def date_aggregation(date_qs): """ Performs an aggregation for a supplied DateQuerySet """ # The DateQuerySet gives us a query that we need to clone and hack date_q = date_qs.query.clone() date_q.distinct = False # Replace 'select' to add an alias date_obj = date_q.select[0] # If it is django 1.6 generates new SelectInfo object if SelectInfo and isinstance(date_obj, SelectInfo): date_q.select = [SelectInfo(col=DateWithAlias(date_obj.col.col, date_obj.col.lookup_type), field=None)] else: date_q.select = [DateWithAlias(date_obj.col, date_obj.lookup_type)] # Now use as a subquery to do aggregation query = DateAggregateQuery(date_qs.model) query.add_subquery(date_q, date_qs.db) return query.get_counts(date_qs.db)
def delete_qs(self, query, using): """ Delete the queryset in one SQL query (if possible). For simple queries this is done by copying the query.query.where to self.query, for complex queries by using subquery. """ innerq = query.query # Make sure the inner query has at least one table in use. innerq.get_initial_alias() # The same for our new query. self.get_initial_alias() innerq_used_tables = [ t for t in innerq.tables if innerq.alias_refcount[t] ] if ((not innerq_used_tables or innerq_used_tables == self.tables) and not len(innerq.having)): # There is only the base table in use in the query, and there are # no aggregate filtering going on. self.where = innerq.where else: pk = query.model._meta.pk if not connections[using].features.update_can_self_select: # We can't do the delete using subquery. values = list(query.values_list('pk', flat=True)) if not values: return self.delete_batch(values, using) return else: innerq.clear_select_clause() innerq.select = [ SelectInfo((self.get_initial_alias(), pk.column), None) ] values = innerq where = self.where_class() where.add((Constraint(None, pk.column, pk), 'in', values), AND) self.where = where self.get_compiler(using).execute_sql(None)
def numeric_range_counts(qs, fieldname, ranges): # Build the query: query = qs.values_list(fieldname).query.clone() select_obj = query.select[0] if SelectInfo and isinstance(select_obj, SelectInfo): query.select = [SelectInfo(col=NumericValueRange(select_obj, ranges), field=None)] else: query.select[0] = NumericValueRange(select_obj, ranges) agg_query = NumericAggregateQuery(qs.model) agg_query.add_subquery(query, qs.db) results = agg_query.get_counts(qs.db) count_dict = SortedDict() for val, count in results: try: r = ranges[val] except IndexError: # Include in the top range - this could be a rounding error r = ranges[-1] count_dict[r] = count return count_dict
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1, requested=None, restricted=None, nullable=None): """ Fill in the information needed for a select_related query. The current depth is measured as the number of connections away from the root model (for example, cur_depth=1 means we are looking at models with direct connections to the root model). """ if not restricted and self.query.max_depth and cur_depth > self.query.max_depth: # We've recursed far enough; bail out. return if not opts: opts = self.query.get_meta() root_alias = self.query.get_initial_alias() self.query.related_select_cols = [] only_load = self.query.get_loaded_field_names() # Setup for the case when only particular related fields should be # included in the related selection. if requested is None: if isinstance(self.query.select_related, dict): requested = self.query.select_related restricted = True else: restricted = False for f, model in opts.get_fields_with_model(): # The get_fields_with_model() returns None for fields that live # in the field's local model. So, for those fields we want to use # the f.model - that is the field's local model. field_model = model or f.model if not select_related_descend(f, restricted, requested, only_load.get(field_model)): continue table = f.rel.to._meta.db_table promote = nullable or f.null alias = self.query.join_parent_model(opts, model, root_alias, {}) join_cols = f.get_joining_columns() alias = self.query.join((alias, table, join_cols), outer_if_first=promote, join_field=f) columns, aliases = self.get_default_columns(start_alias=alias, opts=f.rel.to._meta, as_pairs=True) self.query.related_select_cols.extend( SelectInfo(col, field) for col, field in zip(columns, f.rel.to._meta.concrete_fields)) if restricted: next = requested.get(f.name, {}) else: next = False new_nullable = f.null or promote self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, next, restricted, new_nullable) if restricted: related_fields = [(o.field, o.model) for o in opts.get_all_related_objects() if o.field.unique] for f, model in related_fields: if not select_related_descend(f, restricted, requested, only_load.get(model), reverse=True): continue alias = self.query.join_parent_model(opts, f.rel.to, root_alias, {}) table = model._meta.db_table alias = self.query.join( (alias, table, f.get_joining_columns(reverse_join=True)), outer_if_first=True, join_field=f) from_parent = (opts.model if issubclass(model, opts.model) else None) columns, aliases = self.get_default_columns( start_alias=alias, opts=model._meta, as_pairs=True, from_parent=from_parent) self.query.related_select_cols.extend( SelectInfo(col, field) for col, field in zip( columns, model._meta.concrete_fields)) next = requested.get(f.related_query_name(), {}) # Use True here because we are looking at the _reverse_ side of # the relation, which is always nullable. new_nullable = True self.fill_related_selections(model._meta, table, cur_depth + 1, next, restricted, new_nullable)
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1, requested=None, restricted=None): """ Fill in the information needed for a select_related query. The current depth is measured as the number of connections away from the root model (for example, cur_depth=1 means we are looking at models with direct connections to the root model). """ if not restricted and self.query.max_depth and cur_depth > self.query.max_depth: # We've recursed far enough; bail out. return if not opts: opts = self.query.get_meta() root_alias = self.query.get_initial_alias() self.query.related_select_cols = [] only_load = self.query.get_loaded_field_names() # Setup for the case when only particular related fields should be # included in the related selection. if requested is None: if isinstance(self.query.select_related, dict): requested = self.query.select_related restricted = True else: restricted = False for f, model in opts.get_fields_with_model(): # The get_fields_with_model() returns None for fields that live # in the field's local model. So, for those fields we want to use # the f.model - that is the field's local model. field_model = model or f.model if not select_related_descend(f, restricted, requested, only_load.get(field_model)): continue _, _, _, joins, _ = self.query.setup_joins([f.name], opts, root_alias) alias = joins[-1] columns, _ = self.get_default_columns(start_alias=alias, opts=f.rel.to._meta, as_pairs=True) self.query.related_select_cols.extend( SelectInfo((col[0], col[1].column), col[1]) for col in columns) if restricted: next = requested.get(f.name, {}) else: next = False self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, next, restricted) if restricted: related_fields = [(o.field, o.model) for o in opts.get_all_related_objects() if o.field.unique] for f, model in related_fields: if not select_related_descend(f, restricted, requested, only_load.get(model), reverse=True): continue _, _, _, joins, _ = self.query.setup_joins( [f.related_query_name()], opts, root_alias) alias = joins[-1] from_parent = (opts.model if issubclass(model, opts.model) else None) columns, _ = self.get_default_columns(start_alias=alias, opts=model._meta, as_pairs=True, from_parent=from_parent) self.query.related_select_cols.extend( SelectInfo((col[0], col[1].column), col[1]) for col in columns) next = requested.get(f.related_query_name(), {}) self.fill_related_selections(model._meta, alias, cur_depth + 1, next, restricted)
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1, requested=None, restricted=None): """ Fill in the information needed for a select_related query. The current depth is measured as the number of connections away from the root model (for example, cur_depth=1 means we are looking at models with direct connections to the root model). """ def _get_field_choices(): direct_choices = (f.name for (f, _) in opts.get_fields_with_model() if f.rel) reverse_choices = ( f.field.related_query_name() for f in opts.get_all_related_objects() if f.field.unique ) return chain(direct_choices, reverse_choices) if not restricted and self.query.max_depth and cur_depth > self.query.max_depth: # We've recursed far enough; bail out. return if not opts: opts = self.query.get_meta() root_alias = self.query.get_initial_alias() self.query.related_select_cols = [] only_load = self.query.get_loaded_field_names() # Setup for the case when only particular related fields should be # included in the related selection. fields_found = set() if requested is None: if isinstance(self.query.select_related, dict): requested = self.query.select_related restricted = True else: restricted = False for f, model in opts.get_fields_with_model(): fields_found.add(f.name) if restricted: next = requested.get(f.name, {}) if not f.rel: # If a non-related field is used like a relation, # or if a single non-relational field is given. if next or (cur_depth == 1 and f.name in requested): raise FieldError( "Non-relational field given in select_related: '%s'. " "Choices are: %s" % ( f.name, ", ".join(_get_field_choices()) or '(none)', ) ) else: next = False # The get_fields_with_model() returns None for fields that live # in the field's local model. So, for those fields we want to use # the f.model - that is the field's local model. field_model = model or f.model if not select_related_descend(f, restricted, requested, only_load.get(field_model)): continue _, _, _, joins, _ = self.query.setup_joins( [f.name], opts, root_alias) alias = joins[-1] columns, _ = self.get_default_columns(start_alias=alias, opts=f.rel.to._meta, as_pairs=True) self.query.related_select_cols.extend( SelectInfo((col[0], col[1].column), col[1]) for col in columns ) self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, next, restricted) if restricted: related_fields = [ (o.field, o.model) for o in opts.get_all_related_objects() if o.field.unique ] for f, model in related_fields: if not select_related_descend(f, restricted, requested, only_load.get(model), reverse=True): continue related_field_name = f.related_query_name() fields_found.add(related_field_name) _, _, _, joins, _ = self.query.setup_joins([related_field_name], opts, root_alias) alias = joins[-1] from_parent = (opts.model if issubclass(model, opts.model) else None) columns, _ = self.get_default_columns(start_alias=alias, opts=model._meta, as_pairs=True, from_parent=from_parent) self.query.related_select_cols.extend( SelectInfo((col[0], col[1].column), col[1]) for col in columns) next = requested.get(f.related_query_name(), {}) self.fill_related_selections(model._meta, alias, cur_depth + 1, next, restricted) fields_not_found = set(requested.keys()).difference(fields_found) if fields_not_found: invalid_fields = ("'%s'" % s for s in fields_not_found) raise FieldError( 'Invalid field name(s) given in select_related: %s. ' 'Choices are: %s' % ( ', '.join(invalid_fields), ', '.join(_get_field_choices()) or '(none)', ) )
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1, requested=None, restricted=None, nullable=None): """ Fill in the information needed for a select_related query. The current depth is measured as the number of connections away from the root model (for example, cur_depth=1 means we are looking at models with direct connections to the root model). """ if not restricted and self.query.max_depth and cur_depth > self.query.max_depth: # We've recursed far enough; bail out. return if not opts: opts = self.query.get_meta() root_alias = self.query.get_initial_alias() self.query.related_select_cols = [] only_load = self.query.get_loaded_field_names() # Setup for the case when only particular related fields should be # included in the related selection. if requested is None: if isinstance(self.query.select_related, dict): requested = self.query.select_related restricted = True else: restricted = False for f, model in opts.get_fields_with_model(): # The get_fields_with_model() returns None for fields that live # in the field's local model. So, for those fields we want to use # the f.model - that is the field's local model. field_model = model or f.model if not select_related_descend(f, restricted, requested, only_load.get(field_model)): continue table = f.rel.to._meta.db_table promote = nullable or f.null if model: int_opts = opts alias = root_alias alias_chain = [] for int_model in opts.get_base_chain(model): # Proxy model have elements in base chain # with no parents, assign the new options # object and skip to the next base in that # case if not int_opts.parents[int_model]: int_opts = int_model._meta continue lhs_col = int_opts.parents[int_model].column int_opts = int_model._meta alias = self.query.join((alias, int_opts.db_table, lhs_col, int_opts.pk.column), promote=promote) alias_chain.append(alias) else: alias = root_alias alias = self.query.join((alias, table, f.column, f.rel.get_related_field().column), promote=promote) columns, aliases = self.get_default_columns(start_alias=alias, opts=f.rel.to._meta, as_pairs=True) self.query.related_select_cols.extend( SelectInfo(col, field) for col, field in zip(columns, f.rel.to._meta.fields)) if restricted: next = requested.get(f.name, {}) else: next = False new_nullable = f.null or promote self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, next, restricted, new_nullable) if restricted: related_fields = [ (o.field, o.model) for o in opts.get_all_related_objects() if o.field.unique ] for f, model in related_fields: if not select_related_descend(f, restricted, requested, only_load.get(model), reverse=True): continue table = model._meta.db_table int_opts = opts alias = root_alias alias_chain = [] chain = opts.get_base_chain(f.rel.to) if chain is not None: for int_model in chain: # Proxy model have elements in base chain # with no parents, assign the new options # object and skip to the next base in that # case if not int_opts.parents[int_model]: int_opts = int_model._meta continue lhs_col = int_opts.parents[int_model].column int_opts = int_model._meta alias = self.query.join( (alias, int_opts.db_table, lhs_col, int_opts.pk.column), promote=True, ) alias_chain.append(alias) alias = self.query.join( (alias, table, f.rel.get_related_field().column, f.column), promote=True ) columns, aliases = self.get_default_columns(start_alias=alias, opts=model._meta, as_pairs=True, local_only=True) self.query.related_select_cols.extend( SelectInfo(col, field) for col, field in zip(columns, model._meta.fields)) next = requested.get(f.related_query_name(), {}) # Use True here because we are looking at the _reverse_ side of # the relation, which is always nullable. new_nullable = True self.fill_related_selections(model._meta, table, cur_depth+1, next, restricted, new_nullable)