docs: def save_file(result, filename, encoding='utf8', headers=None,
                convertors=None, visitor=None, writer=None, **kwargs):
      """
      save query result to a csv file
      visitor can used to convert values, all value should be convert to string
      visitor function should be defined as:
          def visitor(keys, values, encoding):
              #return new values []
      
      convertors is used to convert single column value, for example:
          
          convertors = {'field1':convert_func1, 'fields2':convert_func2}
          
          def convert_func1(value, data):
              value is value of field1
              data is the record
              
      if visitor and convertors all provided, only visitor is available.
      
      headers used to convert column to a provided value
      """
      import os
      from uliweb.utils.common import simple_value
      
      convertors = convertors or {}
      headers = headers or []
      writer_class = Writer
      if isinstance(filename, (str, unicode)):
          ext = os.path.splitext(filename)[1]
          if ext == '.csv':
              writer_class = Writer
          elif ext == '.dict':
              writer_class = DictWriter
          elif ext == '.xlsx':
              writer_class = XlsxWriter
  
      def convert(k, v, data):
          f = convertors.get(k)
          if f:
              v = f(v, data)
          return v
  
      if isinstance(result, (str, unicode)):
          result = text(safe_unicode(result))
  
      if isinstance(result, (Select, TextClause)):
          result = do_(result)
      _header = []
      for k in result.keys():
          flag = False
          for x in headers:
              if x['name'] == k:
                  _header.append(x)
                  flag = True
                  break
          if not flag:
              _header.append({'name':k, 'title':k})
  
      def _data():
          for row in result:
              if visitor and callable(visitor):
                  _row = visitor(result.keys(), row.values(), encoding)
              else:
                  _row = [convert(k, v, row) for k, v in zip(result.keys(), row.values())]
              yield _row
  
      writer = writer_class(filename, header=_header, data=_data(), **kwargs)
      writer.save()