Recursive method that builds the where parts. Any Q objects that have children will
        also be built with ``self.build_where_part()``

        :rtype: str
        :return: The composed where string

def build_where_part(self, wheres):
        """
        Recursive method that builds the where parts. Any Q objects that have children will
        also be built with ``self.build_where_part()``

        :rtype: str
        :return: The composed where string
        """
        where_parts = []

        # loop through each child of the Q condition
        for where in wheres.children:

            # if this child is another Q object, recursively build the where part
            if type(where) is Q:
                where_parts.append(self.build_where_part(where))
            elif type(where) is tuple:
                # build the condition for this where part
                # get the field name and value
                field_name = where[0]
                value = where[1]

                # set the default operator
                operator_str = 'eq'
                operator = '='

                # break apart the field name on double underscores
                field_parts = field_name.split('__')
                if len(field_parts) > 1:
                    # get the operator based on the last element split from the double underscores
                    operator_str = field_parts[-1]
                    operator = self.get_condition_operator(operator_str)
                    if operator is None:
                        operator = '='
                        field_name = '__'.join(field_parts)
                    else:
                        field_name = '__'.join(field_parts[:-1])

                # check if we are comparing to null
                if value is None:
                    # change the operator syntax to IS
                    operator = 'IS'

                # set up the condition string format
                condition = '{0} {1} ?'.format(field_name, operator)

                # apply the NOT if this condition is negated
                if wheres.negated:
                    condition = 'NOT({0})'.format(condition)

                # check if this value is multiple values
                if operator_str == 'in':

                    # make sure value is a list
                    if type(value) is not list:

                        # convert to string in case it is a number
                        value = str(value)

                        # split on commas
                        value = value.split(',')

                    # Ensure that we have a value in the list
                    if len(value) == 0:
                        value = [None]

                    if type(value) is Expression:
                        condition = condition.replace('?', value.str)
                    else:
                        # assign each query param to a named arg
                        named_args = []
                        for value_item in value:
                            named_arg = self.set_arg(value_item)
                            named_args.append('%({0})s'.format(named_arg))
                        # replace the ? in the query with the arg placeholder
                        condition = condition.replace('?', '({0})'.format(','.join(named_args)), 1)
                else:
                    # get the value based on the operator
                    value = self.get_condition_value(operator_str, value)

                    if type(value) is Expression:
                        condition = condition.replace('?', value.str)
                    else:
                        named_arg = self.set_arg(value)
                        # replace the ? in the query with the arg placeholder
                        condition = condition.replace('?', '%({0})s'.format(named_arg), 1)

                # add the condition to the where sql
                where_parts.append(condition)

        # join all where parts together
        joined_parts = ' {0} '.format(wheres.connector).join(where_parts)

        # wrap the where parts in parentheses
        return '({0})'.format(joined_parts)