Esempio n. 1
0
def test_nested_query():
    expected = Exact(
        ExpressionWrapper(F('char_field'), output_field=models.CharField()),
        Value(''))
    query = Q(Q(char_field=''))
    expanded = expand_query(FakeModel, query)
    assert are_equal(expected, expanded)
Esempio n. 2
0
def test_negated_empty_as_a_sibling():
    expected = Exact(
        ExpressionWrapper(F('int_field'), output_field=IntegerField()),
        Value(1))
    query = Q(~Q(), Q(int_field=1))
    expanded = expand_query(FakeModel, query)
    assert are_equal(expected, expanded)
    assert wrap(expanded).as_python(dict(int_field=1))
Esempio n. 3
0
def test_combining_empty_with_query():
    expected = Exact(
        ExpressionWrapper(F('int_field'), output_field=IntegerField()),
        Value(1))
    query = Q() & Q(int_field=1)
    expanded = expand_query(FakeModel, query)
    assert are_equal(expected, expanded)
    assert wrap(expanded).as_python(dict(int_field=1))
Esempio n. 4
0
def test_and_query():
    expected = And(
        Exact(
            ExpressionWrapper(F('char_field'),
                              output_field=models.CharField()), Value('')),
        Exact(
            ExpressionWrapper(F('int_field'),
                              output_field=models.IntegerField()), Value(1)),
    )
    queries = [
        # Q(char_field='', int_field=1),  # Order is not guaranteed!
        Q(Q(char_field=''), int_field=1),
        Q(Q(char_field=''), Q(int_field=1)),
        Q(char_field='') & Q(int_field=1),
    ]
    for query in queries:
        expanded = expand_query(FakeModel, query)
        assert are_equal(expected, expanded)
Esempio n. 5
0
 def validate(self, model, instance, exclude=None, using=DEFAULT_DB_ALIAS):
     queryset = model._default_manager.using(using)
     if self.fields:
         lookup_kwargs = {}
         for field_name in self.fields:
             if exclude and field_name in exclude:
                 return
             field = model._meta.get_field(field_name)
             lookup_value = getattr(instance, field.attname)
             if lookup_value is None or (
                 lookup_value == ""
                 and connections[using].features.interprets_empty_strings_as_nulls
             ):
                 # A composite constraint containing NULL value cannot cause
                 # a violation since NULL != NULL in SQL.
                 return
             lookup_kwargs[field.name] = lookup_value
         queryset = queryset.filter(**lookup_kwargs)
     else:
         # Ignore constraints with excluded fields.
         if exclude:
             for expression in self.expressions:
                 for expr in expression.flatten():
                     if isinstance(expr, F) and expr.name in exclude:
                         return
         replacement_map = instance._get_field_value_map(
             meta=model._meta, exclude=exclude
         )
         expressions = [
             Exact(expr, expr.replace_references(replacement_map))
             for expr in self.expressions
         ]
         queryset = queryset.filter(*expressions)
     model_class_pk = instance._get_pk_val(model._meta)
     if not instance._state.adding and model_class_pk is not None:
         queryset = queryset.exclude(pk=model_class_pk)
     if not self.condition:
         if queryset.exists():
             if self.expressions:
                 raise ValidationError(self.get_violation_error_message())
             # When fields are defined, use the unique_error_message() for
             # backward compatibility.
             for model, constraints in instance.get_constraints():
                 for constraint in constraints:
                     if constraint is self:
                         raise ValidationError(
                             instance.unique_error_message(model, self.fields)
                         )
     else:
         against = instance._get_field_value_map(meta=model._meta, exclude=exclude)
         try:
             if (self.condition & Exists(queryset.filter(self.condition))).check(
                 against, using=using
             ):
                 raise ValidationError(self.get_violation_error_message())
         except FieldError:
             pass
Esempio n. 6
0
def test_negated_query():
    expected = Not(
        Exact(
            ExpressionWrapper(F('char_field'),
                              output_field=models.CharField()), Value('')))
    query_1 = ~Q(char_field='')
    query_2 = Q(~Q(char_field=''))
    expanded_1 = expand_query(FakeModel, query_1)
    expanded_2 = expand_query(FakeModel, query_2)
    assert are_equal(expected, expanded_1)
    assert are_equal(expected, expanded_2)
Esempio n. 7
0
def test_lookup_comparison() -> None:
    q1 = Exact(
        Lower(
            ExpressionWrapper(F('char_field'),
                              output_field=models.CharField())), Value(''))
    q2 = Exact(
        Lower(
            ExpressionWrapper(F('char_field'),
                              output_field=models.CharField())), Value(''))
    q3 = GreaterThan(
        ExpressionWrapper(F('int_field'), output_field=models.IntegerField()),
        Value(2))
    q4 = Exact(
        Lower(
            ExpressionWrapper(F('char_field'),
                              output_field=models.TextField())), Value(''))

    assert are_equal(q1, q2)
    assert not are_equal(q1, q3)
    assert not are_equal(q1, q4)
Esempio n. 8
0
def test_char_expression() -> None:
    expected = Exact(
        Lower(
            ExpressionWrapper(F('char_field'),
                              output_field=models.CharField())), Value(''))
    query_1 = Q(char_field__lower='')
    query_2 = Q(char_field__lower__exact='')

    expanded_1 = expand_query(FakeModel, query_1)
    expanded_2 = expand_query(FakeModel, query_2)

    assert are_equal(expected, expanded_1)
    assert are_equal(expected, expanded_2)
Esempio n. 9
0
 def is_single_row_update(self):
     where = self.query.where
     match = getattr(self.query, 'match', {})
     node = None
     if len(where.children) == 1:
         node = where.children[0]
     elif match:
         meta = self.query.model._meta
         pk_match = match.get(meta.pk.attname)
         if pk_match is not None:
             pk_value = list(pk_match.dict.keys())[0]
             return Exact(meta.pk.get_col(meta.db_table), pk_value)
     if not isinstance(node, Exact):
         node = None
     elif not node.lhs.field.primary_key:
         node = None
     return node
Esempio n. 10
0
    def as_sql(self, compiler, connection):
        """
        Return the SQL version of the where clause and the value to be
        substituted in. Return '', [] if this node matches everything,
        None, [] if this node is empty, and raise EmptyResultSet if this
        node can't match anything.
        """
        result = []
        result_params = []
        if self.connector == AND:
            full_needed, empty_needed = len(self.children), 1
        else:
            full_needed, empty_needed = 1, len(self.children)

        if self.connector == XOR and not connection.features.supports_logical_xor:
            # Convert if the database doesn't support XOR:
            #   a XOR b XOR c XOR ...
            # to:
            #   (a OR b OR c OR ...) AND (a + b + c + ...) == 1
            lhs = self.__class__(self.children, OR)
            rhs_sum = reduce(
                operator.add,
                (Case(When(c, then=1), default=0) for c in self.children),
            )
            rhs = Exact(1, rhs_sum)
            return self.__class__([lhs, rhs], AND,
                                  self.negated).as_sql(compiler, connection)

        for child in self.children:
            try:
                sql, params = compiler.compile(child)
            except EmptyResultSet:
                empty_needed -= 1
            else:
                if sql:
                    result.append(sql)
                    result_params.extend(params)
                else:
                    full_needed -= 1
            # Check if this node matches nothing or everything.
            # First check the amount of full nodes and empty nodes
            # to make this node empty/full.
            # Now, check if this node is full/empty using the
            # counts.
            if empty_needed == 0:
                if self.negated:
                    return "", []
                else:
                    raise EmptyResultSet
            if full_needed == 0:
                if self.negated:
                    raise EmptyResultSet
                else:
                    return "", []
        conn = " %s " % self.connector
        sql_string = conn.join(result)
        if sql_string:
            if self.negated:
                # Some backends (Oracle at least) need parentheses
                # around the inner SQL in the negated case, even if the
                # inner SQL contains just a single expression.
                sql_string = "NOT (%s)" % sql_string
            elif len(result) > 1 or self.resolved:
                sql_string = "(%s)" % sql_string
        return sql_string, result_params