def build_filter(self, filter_expr, *args, **kwargs): if isinstance(filter_expr, Q): return super().build_filter(filter_expr, *args, **kwargs) if filter_expr[0].startswith('old__'): alias = 'OLD' elif filter_expr[0].startswith('new__'): alias = 'NEW' else: # pragma: no cover raise ValueError( 'Filter expression on trigger.Q object must reference' ' old__ or new__') filter_expr = (filter_expr[0][5:], filter_expr[1]) node, _ = super().build_filter(filter_expr, *args, **kwargs) self.alias_map[alias] = BaseTable(alias, alias) for child in node.children: child.lhs = Col( alias=alias, target=child.lhs.target, output_field=child.lhs.output_field, ) return node, {alias}
def test_expressions(self): self.assertEqual( repr(Case(When(a=1))), "<Case: CASE WHEN <Q: (AND: ('a', 1))> THEN Value(None), ELSE Value(None)>" ) self.assertEqual(repr(Col('alias', 'field')), "Col(alias, field)") self.assertEqual(repr(Date('published', 'exact')), "Date(published, exact)") self.assertEqual(repr(DateTime('published', 'exact', utc)), "DateTime(published, exact, %s)" % utc) self.assertEqual(repr(F('published')), "F(published)") self.assertEqual(repr(F('cost') + F('tax')), "<CombinedExpression: F(cost) + F(tax)>") self.assertEqual( repr(ExpressionWrapper( F('cost') + F('tax'), models.IntegerField())), "ExpressionWrapper(F(cost) + F(tax))") self.assertEqual(repr(Func('published', function='TO_CHAR')), "Func(F(published), function=TO_CHAR)") self.assertEqual(repr(OrderBy(Value(1))), 'OrderBy(Value(1), descending=False)') self.assertEqual(repr(Random()), "Random()") self.assertEqual(repr(RawSQL('table.col', [])), "RawSQL(table.col, [])") self.assertEqual(repr(Ref('sum_cost', Sum('cost'))), "Ref(sum_cost, Sum(F(cost)))") self.assertEqual(repr(Value(1)), "Value(1)")
def add_select(self, *fields, **kwargs): "Replaces the `SELECT` columns with the ones provided." if 'queryset' in kwargs: queryset = kwargs.pop('queryset') else: queryset = self.get_queryset() queryset.query.default_cols = False include_pk = kwargs.pop('include_pk', True) if include_pk: fields = [self.root_model._meta.pk] + list(fields) aliases = [] for pair in fields: if isinstance(pair, (list, tuple)): model, field = pair else: field = pair model = field.model queryset, alias = self.add_joins(model, queryset) aliases.append(Col(alias, field, field)) if aliases: queryset.query.select = aliases return queryset
def _create_index_sql(self, model, *, fields=None, **kwargs): if fields is None or len(fields) != 1 or not hasattr( fields[0], 'geodetic'): return super()._create_index_sql(model, fields=fields, **kwargs) field = fields[0] expressions = None opclasses = None if field.geom_type == 'RASTER': # For raster fields, wrap index creation SQL statement with ST_ConvexHull. # Indexes on raster columns are based on the convex hull of the raster. expressions = Func(Col(None, field), template=self.rast_index_template) fields = None elif field.dim > 2 and not field.geography: # Use "nd" ops which are fast on multidimensional cases opclasses = [self.geom_index_ops_nd] name = kwargs.get('name') if not name: name = self._create_index_name(model._meta.db_table, [field.column], '_id') return super()._create_index_sql( model, fields=fields, name=name, using=' USING %s' % self.geom_index_type, opclasses=opclasses, expressions=expressions, )
def get_extra_restriction(self, where_class, alias, remote_alias): field = self.rel.to._meta.get_field_by_name(self.content_type_field_name)[0] contenttype_pk = self.get_content_type().pk cond = where_class() lookup = field.get_lookup('exact')(Col(remote_alias, field, field), contenttype_pk) cond.add(lookup, 'AND') return cond
def get_col(self, alias, output_field=None): if output_field is None: output_field = self if alias != self.model._meta.db_table or output_field != self: from django.db.models.expressions import Col return Col(alias, self, output_field) else: return self.cached_col
def as_sql(self, compiler, connection): """ Generate SQL for the language constraint. Use the language set by the queryset onto the query object. Replace None with current language, providing lazy evaluation of language(None) """ language = compiler.query.language_code or translation.get_language() if language == 'all': assert hasattr(compiler.query.model._meta, 'shared_model') value = Col(compiler.query.get_initial_alias(), compiler.query.model._meta.get_field('language_code'), models.CharField()) else: value = Value(language) col_sql, col_params = self.col.as_sql(compiler, connection) val_sql, val_params = value.as_sql(compiler, connection) return ('%s = %s' % (col_sql, val_sql), col_params + val_params)
def get_extra_restriction(self, where_class, alias, related_alias): """ Inject the LanguageConstraint into the join clause. Actual language will be resolved by the constraint itself. """ related_model = self.related_model return LanguageConstraint( Col(alias, related_model._meta.get_field('language_code'), models.CharField()))
def get_select(self): """ Special case for if we are preparing a count() query """ if self.is_count_qry is True: col = (Col("__count", None), ("COUNT(*)", []), None) klass_info = {"model": self.query.model, "select_fields": ["__count"]} annotations = dict() return (col,), klass_info, annotations else: return super().get_select()
def add_alias_constraints(queryset, alias, **kwargs): model, alias = alias clause = queryset.query.where_class() for lookup, value in kwargs.items(): field_name, lookup = lookup.split('__') clause.add( queryset.query.build_lookup( [lookup], Col(alias, model._meta.get_field(field_name)), value), AND) queryset.query.where.add(clause, AND)
def _resolve_ref(self, name): """ Gets called when a column reference is accessed via the CTE instance `.col.name` """ if name not in self.fields: raise RuntimeError("No field with name `{}`".format(name)) field = self.fields.get(name) field.set_attributes_from_name(name) return Col(self.name, field, output_field=field)
def resolve_ref(name: str, *_: Any, **__: Any) -> 'Col': from django.db.models.expressions import Col from django.db.models.fields import Field # We need to do some faking of the ref resolution. # This essentially enables us to have a bit more complete # workings of F(). # An interesting point to raise here is, we need to pass a Field in. # However, it doesn't need to be the "correct" field. At this point, # all conversion has been done, so now we just need to get a valid # target in. return Col(name, Field())
def add_alias_constraints(queryset, alias, **kwargs): """ Add constraints to queryset, for given alias. Bypass high-level API to prevent ORM from creating a new JOIN. alias - table name or alias to modify; must already be in query kwargs - Django-style lookup=value conditions. """ model, alias = alias clause = queryset.query.where_class() for lookup, value in kwargs.items(): field_name, lookup = lookup.split('__') clause.add(queryset.query.build_lookup( [lookup], Col(alias, model._meta.get_field(field_name)), value ), AND) queryset.query.where.add(clause, AND)
def add_select(self, field_name, lookup_type, order='ASC'): """ Converts the query into an extraction query. """ try: field, _, _, joins, _ = 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)) self._check_field(field) # overridden in DateTimeQuery alias = joins[-1] select = self._get_select(Col(alias, field), 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 get_fields(self, query, keys_only=False): """ Get the fields to select, this returns the SELECT -> XXX <- part This will also popular the 'fieldstrs' field :return: The list of fields to query, properly quoted, as a string """ # Field names to place back into the database q = query pk_field = q.get_meta().pk opts = q.get_meta() self.pk_col_name = pk_field.column if q.distinct_fields: raise Exception("Can't handle distinct_fields yet") columns_str = [] fields = [] if not self.aggregate_only: if q.select: fields = q.select elif q.default_cols: fields = [Col('_dummy_', x, x) for x in opts.fields] # pprint(vars(q)) for col in fields: field = col.output_field column = field.column # Get the document field to select. if column == pk_field.column: sel_field = 'META({}).id'.format( n1ql_escape(BUCKET_PLACEHOLDER)) elif keys_only: continue else: sel_field = n1ql_escape(column) # See if there's a lookup type. if hasattr(col, 'lookup_type'): # pprint(vars(q)) col_alias = self._gen_alias() selstr, convfld = Transforms.transform(col.lookup_type, sel_field) if q.distinct: self._nopk_where = True selstr = 'DISTINCT({0})'.format(selstr) selstr += ' AS ' + col_alias columns_str.append(selstr) self.queried_fields.append((col_alias, convfld)) else: if column == pk_field.column: columns_str.append(sel_field + ' AS ' + column) else: columns_str.append(sel_field) self.queried_fields.append((column, field)) for alias, annotation in q.annotation_select.items(): colspec = annotation.input_field.value if not alias: alias = self._gen_alias() fn = annotation.function agstr = '{0}({1}) AS {2}'.format(fn, colspec, n1ql_escape(alias)) self.queried_fields.append((alias, None)) columns_str.append(agstr) columns_str += self.handle_extra_select(query) return ','.join(columns_str)
def get_col(self, alias, output_field=None): output_field = output_field or self.output_field return Col(alias, self, output_field)
def resolve_expression(self, query=None, *args, **kwargs): return Col( alias=self.row_alias, target=query.model._meta.get_field(self.col_name), )
def cached_col(self): from django.db.models.expressions import Col return Col(self.model._meta.db_table, self)
def __init__(self, alias, target, output_field=None): super().__init__(alias, target, output_field) self.children = [Col(alias, key, output_field) for key in target.keys]