docs: 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)