def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
                     can_reuse=None, connector=AND, allow_joins=True, split_subq=True):
        if isinstance(filter_expr, dict):
            raise FieldError("Cannot parse keyword query as dict")
        arg, value = filter_expr
        if not arg:
            raise FieldError("Cannot parse keyword query %r" % arg)
        lookups, parts, reffed_expression = self.solve_lookup_type(arg)
        if not allow_joins and len(parts) > 1:
            raise FieldError("Joined field references are not permitted in this query")

        # Work out the lookup type and remove it from the end of 'parts',
        # if necessary.
        value, lookups, used_joins = self.prepare_lookup_value(value, lookups, can_reuse, allow_joins)

        clause = self.where_class()
        if reffed_expression:
            condition = self.build_lookup(lookups, reffed_expression, value)
            clause.add(condition, AND)
            return clause, []

        opts = self.get_meta()
        alias = self.get_initial_alias()
        allow_many = not branch_negated or not split_subq

        try:
            field, sources, opts, join_list, path = self.setup_joins(
                parts, opts, alias, can_reuse=can_reuse, allow_many=allow_many)

            # Prevent iterator from being consumed by check_related_objects()
            if isinstance(value, Iterator):
                value = list(value)
            self.check_related_objects(field, value, opts)

            # split_exclude() needs to know which joins were generated for the
            # lookup parts
            self._lookup_joins = join_list
        except MultiJoin as e:
            return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
                                      can_reuse, e.names_with_path)

        if can_reuse is not None:
            can_reuse.update(join_list)
        used_joins = set(used_joins).union(set(join_list))
        targets, alias, join_list = self.trim_joins(sources, join_list, path)

        if field.is_relation:
            # No support for transforms for relational fields
            num_lookups = len(lookups)
            if num_lookups > 1:
                raise FieldError('Related Field got invalid lookup: {}'.format(lookups[0]))
            assert num_lookups > 0  # Likely a bug in Django if this fails.
            lookup_class = field.get_lookup(lookups[0])
            if len(targets) == 1:
                lhs = targets[0].get_col(alias, field)
            else:
                lhs = MultiColSource(alias, targets, sources, field)
            condition = lookup_class(lhs, value)
            lookup_type = lookup_class.lookup_name
        else:
            col = targets[0].get_col(alias, field)
            condition = self.build_lookup(lookups, col, value)
            lookup_type = condition.lookup_name

        clause.add(condition, AND)

        require_outer = lookup_type == 'isnull' and value is True and not current_negated
        if current_negated and (lookup_type != 'isnull' or value is False):
            require_outer = True
            if (lookup_type != 'isnull' and (
                    self.is_nullable(targets[0]) or
                    self.alias_map[join_list[-1]].join_type == LOUTER)):
                # The condition added here will be SQL like this:
                # NOT (col IS NOT NULL), where the first NOT is added in
                # upper layers of code. The reason for addition is that if col
                # is null, then col != someval will result in SQL "unknown"
                # which isn't the same as in Python. The Python None handling
                # is wanted, and it can be gotten by
                # (col IS NULL OR col != someval)
                #   <=>
                # NOT (col IS NOT NULL AND col = someval).
                lookup_class = targets[0].get_lookup('isnull')
                clause.add(lookup_class(targets[0].get_col(alias, sources[0]), False), AND)
        return clause, used_joins if not require_outer else ()
Builds a WhereNode for a single filter clause, but doesn't add it
dataset/ETHPy150Open django/django/django/db/models/sql/query.py/Query.build_filter