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)")
class TestRandom(WrapperTestBase): model_class = WrapperStubModel factory = WrapperStubFactory expression = Random() def test_expression_evaluates_to_expected(self, mocker): expected = 0.56 mocker.patch('dj_hybrid.expression_wrapper.wrappers.random.random', return_value=expected) model_instance = self.get_populated_model() python_outcome = self.get_as_python(model_instance) assert python_outcome == expected def test_parity_with_database(self): # we can't actually test parity, however we can check the return types model_instance = self.get_populated_model() python_outcome = self.get_as_python(model_instance) database_outcome = self.get_from_database(model_instance) assert type(python_outcome) is type(database_outcome) def test_cached_per_instance(self): instance_1 = self.factory() instance_2 = self.factory() wrapped = self.get_wrapped() assert wrapped.as_python(instance_1) is wrapped.as_python(instance_1) assert wrapped.as_python(instance_2) is wrapped.as_python(instance_2) assert wrapped.as_python(instance_1) is not wrapped.as_python( instance_2)
def get_queryset(self, request): return super().get_queryset(request).prefetch_related( 'discounts', 'payments', ).select_related( 'subject', 'user', ).annotate(random_number=Random())
def handle(self, *args, **options): count = options.get('count') depth = options.get('depth') if count / depth < 10: raise Exception( 'The count should be at least 10 times larger than the depth') self.stdout.write('Run "%s"...' % __name__.split('.')[-1]) if options.get('cleanup'): self.stdout.write('Cleanup database') User.objects.all().delete() Post.objects.all().delete() Comment.objects.all().delete() users = [UserFactory() for _ in range(10)] entities = users + [PostFactory() for _ in range(10)] roots_count = options.get('roots_count', int(count // depth // 2)) self.stdout.write('Create tree') with tqdm(total=roots_count * depth) as pbar: for root_idx in range(roots_count): parent = None for depth_idx in range(depth): parent = CommentFactory.create( parent=parent, user=random.choice(users), entity=random.choice(entities)) pbar.update() remain_count = count - roots_count * depth remain_comments = [] self.stdout.write('Create remains') with tqdm(total=remain_count) as pbar: for random_comment in range(remain_count): remain_comments.append( CommentFactory.build(parent=Comment.objects.annotate( random=Random()).order_by('random').first(), user=random.choice(users))) pbar.update() Comment.objects.bulk_create(remain_comments, batch_size=100)
def get_order_by(self): """ See original get_group_by at django.db.models.sql.compiler>SQLCompiler """ if self.query.extra_order_by: ordering = self.query.extra_order_by elif not self.query.default_ordering: ordering = self.query.order_by else: ordering = (self.query.order_by or self.query.get_meta().ordering or []) if self.query.standard_ordering: asc, desc = ORDER_DIR['ASC'] else: asc, desc = ORDER_DIR['DESC'] order_by = [] for field in ordering: if hasattr(field, 'resolve_expression'): if not isinstance(field, OrderBy): field = field.asc() if not self.query.standard_ordering: field.reverse_ordering() order_by.append((field, False)) continue if field == '?': # random order_by.append((OrderBy(Random()), False)) continue col, order = get_order_dir(field, asc) descending = True if order == 'DESC' else False if col in self.query.annotation_select: # Reference to expression in SELECT clause order_by.append((OrderBy(Ref(col, self.query.annotation_select[col]), descending=descending), True)) continue if col in self.query.annotations: # References to an expression which is masked out of the SELECT clause order_by.append((OrderBy(self.query.annotations[col], descending=descending), False)) continue if '.' in field: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. table, col = col.split('.', 1) order_by.append((OrderBy(RawSQL( '%s.%s' % (self.quote_name_unless_alias(table), col), []), descending=descending), False)) continue if not self.query._extra or col not in self.query._extra: # 'col' is of the form 'field' or 'field1__field2' or # '-field1__field2__field', etc. order_by.extend( self.find_ordering_name(field, self.query.get_meta(), default_order=asc)) else: if col not in self.query.extra_select: order_by.append((OrderBy(RawSQL(*self.query.extra[col]), descending=descending), False)) else: order_by.append((OrderBy(Ref(col, RawSQL(*self.query.extra[col])), descending=descending), True)) result = [] # changed from set() to [] seen = [] for expr, is_ref in order_by: if self.query.combinator: src = expr.get_source_expressions()[0] # Relabel order by columns to raw numbers if this is a combined # query; necessary since the columns can't be referenced by the # fully qualified name and the simple column names may collide. for idx, (sel_expr, _, col_alias) in enumerate(self.select): if is_ref and col_alias == src.refs: src = src.source elif col_alias: continue if src == sel_expr: expr.set_source_expressions([RawSQL('%d' % (idx + 1), ())]) break else: raise DatabaseError( 'ORDER BY term does not match any column in the result set.' ) resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) sql, params = self.compile(resolved) # Don't add the same column twice, but the order direction is # not taken into account so we strip it. When this entire method # is refactored into expressions, then we can check each part as we # generate it. without_ordering = self.ordering_parts.search(sql).group(1) if (without_ordering, tuple(params)) in seen: continue # changed from add to append seen.append((without_ordering, tuple(params))) result.append((resolved, (sql, params, is_ref))) return result
def get_order_by(self): """ Returns a list of 2-tuples of form (expr, (sql, params)) for the ORDER BY clause. The order_by clause can alter the select clause (for example it can add aliases to clauses that do not yet have one, or it can add totally new select clauses). """ if self.query.extra_order_by: ordering = self.query.extra_order_by elif not self.query.default_ordering: ordering = self.query.order_by else: ordering = (self.query.order_by or self.query.get_meta().ordering or []) if self.query.standard_ordering: asc, desc = ORDER_DIR['ASC'] else: asc, desc = ORDER_DIR['DESC'] order_by = [] for pos, field in enumerate(ordering): if hasattr(field, 'resolve_expression'): if not isinstance(field, OrderBy): field = field.asc() if not self.query.standard_ordering: field.reverse_ordering() order_by.append((field, False)) continue if field == '?': # random order_by.append((OrderBy(Random()), False)) continue col, order = get_order_dir(field, asc) descending = True if order == 'DESC' else False if col in self.query.annotation_select: order_by.append( (OrderBy(Ref(col, self.query.annotation_select[col]), descending=descending), True)) continue if '.' in field: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. table, col = col.split('.', 1) order_by.append((OrderBy(RawSQL( '%s.%s' % (self.quote_name_unless_alias(table), col), []), descending=descending), False)) continue if not self.query._extra or col not in self.query._extra: # 'col' is of the form 'field' or 'field1__field2' or # '-field1__field2__field', etc. order_by.extend( self.find_ordering_name(field, self.query.get_meta(), default_order=asc)) else: if col not in self.query.extra_select: order_by.append((OrderBy(RawSQL(*self.query.extra[col]), descending=descending), False)) else: order_by.append( (OrderBy(Ref(col, RawSQL(*self.query.extra[col])), descending=descending), True)) result = [] seen = set() for expr, is_ref in order_by: resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) sql, params = self.compile(resolved) # Don't add the same column twice, but the order direction is # not taken into account so we strip it. When this entire method # is refactored into expressions, then we can check each part as we # generate it. without_ordering = self.ordering_parts.search(sql).group(1) if (without_ordering, tuple(params)) in seen: continue seen.add((without_ordering, tuple(params))) result.append((resolved, (sql, params, is_ref))) return result
def get_order_by(self): """ Return a list of 2-tuples of form (expr, (sql, params, is_ref)) for the ORDER BY clause. The order_by clause can alter the select clause (for example it can add aliases to clauses that do not yet have one, or it can add totally new select clauses). """ if self.query.extra_order_by: ordering = self.query.extra_order_by elif not self.query.default_ordering: ordering = self.query.order_by elif self.query.order_by: ordering = self.query.order_by elif self.query.get_meta().ordering: ordering = self.query.get_meta().ordering self._meta_ordering = ordering else: ordering = [] if self.query.standard_ordering: asc, desc = ORDER_DIR['ASC'] else: asc, desc = ORDER_DIR['DESC'] order_by = [] for field in ordering: if hasattr(field, 'resolve_expression'): if isinstance(field, Value): # output_field must be resolved for constants. field = Cast(field, field.output_field) if not isinstance(field, OrderBy): field = field.asc() if not self.query.standard_ordering: field = field.copy() field.reverse_ordering() order_by.append((field, True)) else: order_by.append((field, False)) continue if field == '?': # random order_by.append((OrderBy(Random()), False)) continue col, order = get_order_dir(field, asc) descending = order == 'DESC' if col in self.query.annotation_select: # Reference to expression in SELECT clause order_by.append(( OrderBy(Ref(col, self.query.annotation_select[col]), descending=descending), True)) continue if col in self.query.annotations: # References to an expression which is masked out of the SELECT # clause. expr = self.query.annotations[col] if isinstance(expr, Value): # output_field must be resolved for constants. expr = Cast(expr, expr.output_field) order_by.append((OrderBy(expr, descending=descending), False)) continue if '.' in field: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. table, col = col.split('.', 1) order_by.append(( OrderBy( RawSQL('%s.%s' % (self.quote_name_unless_alias(table), col), []), descending=descending ), False)) continue if not self.query.extra or col not in self.query.extra: # 'col' is of the form 'field' or 'field1__field2' or # '-field1__field2__field', etc. order_by.extend(self.find_ordering_name( field, self.query.get_meta(), default_order=asc)) else: if col not in self.query.extra_select: order_by.append(( OrderBy(RawSQL(*self.query.extra[col]), descending=descending), False)) else: order_by.append(( OrderBy(Ref(col, RawSQL(*self.query.extra[col])), descending=descending), True)) result = [] seen = set() for expr, is_ref in order_by: resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) if self.query.combinator: src = resolved.get_source_expressions()[0] expr_src = expr.get_source_expressions()[0] # Relabel order by columns to raw numbers if this is a combined # query; necessary since the columns can't be referenced by the # fully qualified name and the simple column names may collide. for idx, (sel_expr, _, col_alias) in enumerate(self.select): if is_ref and col_alias == src.refs: src = src.source elif col_alias and not ( isinstance(expr_src, F) and col_alias == expr_src.name ): continue if src == sel_expr: resolved.set_source_expressions([RawSQL('%d' % (idx + 1), ())]) break else: if col_alias: raise DatabaseError('ORDER BY term does not match any column in the result set.') # Add column used in ORDER BY clause without an alias to # the selected columns. self.query.add_select_col(src) resolved.set_source_expressions([RawSQL('%d' % len(self.query.select), ())]) sql, params = self.compile(resolved) # Don't add the same column twice, but the order direction is # not taken into account so we strip it. When this entire method # is refactored into expressions, then we can check each part as we # generate it. without_ordering = self.ordering_parts.search(sql)[1] params_hash = make_hashable(params) if (without_ordering, params_hash) in seen: continue seen.add((without_ordering, params_hash)) result.append((resolved, (sql, params, is_ref))) return result
def get_order_by(self): result = super().get_order_by() result.append((OrderBy(Random()), ('RANDOM()', [], False))) return result