docs: def insert(self, rows, replace=False, skip_duplicates=False, ignore_extra_fields=False, allow_direct_insert=None):
          """
          Insert a collection of rows.
  
          :param rows: An iterable where an element is a numpy record, a dict-like object, a pandas.DataFrame, a sequence,
              or a query expression with the same heading as table self.
          :param replace: If True, replaces the existing tuple.
          :param skip_duplicates: If True, silently skip duplicate inserts.
          :param ignore_extra_fields: If False, fields that are not in the heading raise error.
          :param allow_direct_insert: applies only in auto-populated tables. Set True to insert outside populate calls.
  
          Example::
          >>> relation.insert([
          >>>     dict(subject_id=7, species="mouse", date_of_birth="2014-09-01"),
          >>>     dict(subject_id=8, species="mouse", date_of_birth="2014-09-02")])
          """
  
          if isinstance(rows, pandas.DataFrame):
              rows = rows.to_records()
  
          # prohibit direct inserts into auto-populated tables
          if not (allow_direct_insert or getattr(self, '_allow_insert', True)):  # _allow_insert is only present in AutoPopulate
              raise DataJointError(
                  'Auto-populate tables can only be inserted into from their make methods during populate calls. (see allow_direct_insert)')
  
          heading = self.heading
          if inspect.isclass(rows) and issubclass(rows, QueryExpression):   # instantiate if a class
              rows = rows()
          if isinstance(rows, QueryExpression):
              # insert from select
              if not ignore_extra_fields:
                  try:
                      raise DataJointError(
                          "Attribute %s not found.  To ignore extra attributes in insert, set ignore_extra_fields=True." %
                          next(name for name in rows.heading if name not in heading))
                  except StopIteration:
                      pass
              fields = list(name for name in rows.heading if name in heading)
              query = '{command} INTO {table} ({fields}) {select}{duplicate}'.format(
                  command='REPLACE' if replace else 'INSERT',
                  fields='`' + '`,`'.join(fields) + '`',
                  table=self.full_table_name,
                  select=rows.make_sql(select_fields=fields),
                  duplicate=(' ON DUPLICATE KEY UPDATE `{pk}`={table}.`{pk}`'.format(
                      table=self.full_table_name, pk=self.primary_key[0])
                             if skip_duplicates else ''))
              self.connection.query(query)
              return
  
          if heading.attributes is None:
              logger.warning('Could not access table {table}'.format(table=self.full_table_name))
              return
  
          field_list = None  # ensures that all rows have the same attributes in the same order as the first row.
  
          def make_row_to_insert(row):
              """
              :param row:  A tuple to insert
              :return: a dict with fields 'names', 'placeholders', 'values'
              """
  
              def make_placeholder(name, value):
                  """
                  For a given attribute `name` with `value`, return its processed value or value placeholder
                  as a string to be included in the query and the value, if any, to be submitted for
                  processing by mysql API.
                  :param name:
                  :param value:
                  """
                  if ignore_extra_fields and name not in heading:
                      return None
                  if heading[name].is_external:
                      placeholder, value = '%s', self.external_table.put(heading[name].type, value)
                  elif heading[name].is_blob:
                      if value is None:
                          placeholder, value = 'NULL', None
                      else:
                          placeholder, value = '%s', pack(value)
                  elif heading[name].numeric:
                      if value is None or value == '' or np.isnan(np.float(value)):  # nans are turned into NULLs
                          placeholder, value = 'NULL', None
                      else:
                          placeholder, value = '%s', (str(int(value) if isinstance(value, bool) else value))
                  else:
                      placeholder = '%s'
                  return name, placeholder, value
  
              def check_fields(fields):
                  """
                  Validates that all items in `fields` are valid attributes in the heading
                  :param fields: field names of a tuple
                  """
                  if field_list is None:
                      if not ignore_extra_fields:
                          for field in fields:
                              if field not in heading:
                                  raise KeyError(u'`{0:s}` is not in the table heading'.format(field))
                  elif set(field_list) != set(fields).intersection(heading.names):
                      raise DataJointError('Attempt to insert rows with different fields')
  
              if isinstance(row, np.void):  # np.array
                  check_fields(row.dtype.fields)
                  attributes = [make_placeholder(name, row[name])
                                for name in heading if name in row.dtype.fields]
              elif isinstance(row, collections.abc.Mapping):  # dict-based
                  check_fields(row)
                  attributes = [make_placeholder(name, row[name]) for name in heading if name in row]
              else:  # positional
                  try:
                      if len(row) != len(heading):
                          raise DataJointError(
                              'Invalid insert argument. Incorrect number of attributes: '
                              '{given} given; {expected} expected'.format(
                                  given=len(row), expected=len(heading)))
                  except TypeError:
                      raise DataJointError('Datatype %s cannot be inserted' % type(row))
                  else:
                      attributes = [make_placeholder(name, value) for name, value in zip(heading, row)]
              if ignore_extra_fields:
                  attributes = [a for a in attributes if a is not None]
  
              assert len(attributes), 'Empty tuple'
              row_to_insert = dict(zip(('names', 'placeholders', 'values'), zip(*attributes)))
              nonlocal field_list
              if field_list is None:
                  # first row sets the composition of the field list
                  field_list = row_to_insert['names']
              else:
                  #  reorder attributes in row_to_insert to match field_list
                  order = list(row_to_insert['names'].index(field) for field in field_list)
                  row_to_insert['names'] = list(row_to_insert['names'][i] for i in order)
                  row_to_insert['placeholders'] = list(row_to_insert['placeholders'][i] for i in order)
                  row_to_insert['values'] = list(row_to_insert['values'][i] for i in order)
  
              return row_to_insert
  
          rows = list(make_row_to_insert(row) for row in rows)
          if rows:
              try:
                  query = "{command} INTO {destination}(`{fields}`) VALUES {placeholders}{duplicate}".format(
                      command='REPLACE' if replace else 'INSERT',
                      destination=self.from_clause,
                      fields='`,`'.join(field_list),
                      placeholders=','.join('(' + ','.join(row['placeholders']) + ')' for row in rows),
                      duplicate=(' ON DUPLICATE KEY UPDATE `{pk}`=`{pk}`'.format(pk=self.primary_key[0])
                                 if skip_duplicates else ''))
                  self.connection.query(query, args=list(
                      itertools.chain.from_iterable((v for v in r['values'] if v is not None) for r in rows)))
              except (OperationalError, InternalError, IntegrityError) as err:
                  if err.args[0] == server_error_codes['command denied']:
                      raise DataJointError('Command denied:  %s' % err.args[1]) from None
                  elif err.args[0] == server_error_codes['unknown column']:
                      # args[1] -> Unknown column 'extra' in 'field list'
                      raise DataJointError(
                          '{} : To ignore extra fields, set ignore_extra_fields=True in insert.'.format(err.args[1])
                      ) from None
                  elif err.args[0] == server_error_codes['duplicate entry']:
                      raise DuplicateError(
                          '{} : To ignore duplicate entries, set skip_duplicates=True in insert.'.format(err.args[1])
                      ) from None
                  else:
                      raise