Construct a Mapping (dict) from field names to values from a row of data

    Args:
      row (list or dict): Data (values) to be assigned to field_names in the dict.
        If `row` is a list, then the column names (header row) can be provided in `field_names`.
        If `row` is a list and no field_names are provided, then `field_names` will be taken from the 
        Django model class field names, in the order they appear within the class definition.
      model (django.db.models.Model): The model class to be constructed with data from `row`
      field_names (list or tuple of str): The field names to place the row values in. 
        Defaults to the keys of the dict of `row` (if `row` is a `dict`) or the names of the fields
        in the Django model being constructed.
      ignore_fields (list or tuple of str): The field names to ignore if place the row values in. 

    Returns:
      dict: Mapping from fields to values compatible with a Django model constructor kwargs, `model(**kwargs)`

def field_dict_from_row(row, model,
                        field_names=None, ignore_fields=('id', 'pk'), 
                        strip=True, 
                        blank_none=True, 
                        ignore_related=True, 
                        ignore_values=(None,), 
                        ignore_errors=True, 
                        verbosity=0):
    """Construct a Mapping (dict) from field names to values from a row of data

    Args:
      row (list or dict): Data (values) to be assigned to field_names in the dict.
        If `row` is a list, then the column names (header row) can be provided in `field_names`.
        If `row` is a list and no field_names are provided, then `field_names` will be taken from the 
        Django model class field names, in the order they appear within the class definition.
      model (django.db.models.Model): The model class to be constructed with data from `row`
      field_names (list or tuple of str): The field names to place the row values in. 
        Defaults to the keys of the dict of `row` (if `row` is a `dict`) or the names of the fields
        in the Django model being constructed.
      ignore_fields (list or tuple of str): The field names to ignore if place the row values in. 

    Returns:
      dict: Mapping from fields to values compatible with a Django model constructor kwargs, `model(**kwargs)`
    """
    errors = collections.Counter()
    if not field_names:
        field_classes = [f for f in model._meta._fields() if (not ignore_fields or (f.name not in ignore_fields))]
        field_names = [f.name for f in field_classes]
    else:
        field_classes = [f for f in model._meta._fields() if (f.name in field_names and (not ignore_fields or (f.name not in ignore_fields)))]
    field_dict = {}
    if isinstance(row, collections.Mapping):
        row = [row.get(field_name, None) for field_name in field_names]
    # if most of the destination field names exist in the source object then 
    elif sum(hasattr(row, field_name) for field_name in field_names) / (len(field_names) / 2. + 1):
        row = [getattr(row, field_name, None) for field_name in field_names]
    for field_name, field_class, value in zip(field_names, field_classes, row):
        clean_value = None
        if verbosity >= 3:
            print field_name, field_class, value
        if isinstance(field_class, related.RelatedField):
            if not ignore_related:
                try:
                    clean_value = field_class.related.parent_model.objects.get(value)
                except:
                    try:
                        clean_value = field_class.related.parent_model.objects.get_by_natural_key(value)
                    except:
                        errors += collections.Counter(['num_unlinked_fks'])
                        if verbosity > 1:
                            print 'Unable to connect related field %r using value %r' % (field_class, value)
        # FIXME: lots of redundancy and potential for error here and below
        if isinstance(value, basestring) and not value:
            if verbosity >= 3:
                print 'String field %r setting value %r to None' % (field_class, value)
            value = None
            if blank_none and (
                not isinstance(field_class, related.RelatedField) or field_class.blank or not field_class.null):
                try:
                    if isinstance(field_class.to_python(''), basestring):
                        value = ''
                    else:
                        value = None
                except:
                    value = None
            else:
                value = None
        if not clean_value:
            try:
                # get a clean python value from a string, etc
                clean_value = field_class.to_python(value)
            except:  # ValidationError
                try:
                    clean_value = str(field_class.to_python(util.clean_wiki_datetime(value)))
                except:
                    try:
                        clean_value = field_class.to_python(util.make_float(value))
                    except:
                        try:
                            clean_value = field_class.to_python(value)  # FIXME: this has already been tried!
                        except:
                            if verbosity > 0:
                                print
                                print "The row below has a value (%r) that can't be coerced by %r:" % (value, field_class.to_python)
                                print row
                                print_exc()
                            clean_value = None
                            errors += collections.Counter(['num_uncoercible'])
                            if not ignore_errors:
                                raise
        if isinstance(clean_value, basestring):
            if strip:
                clean_value = clean_value.strip()
            # don't forget to decode the utf8 before doing a max_length truncation!
            clean_value = clean_utf8(clean_value, verbosity=verbosity).decode('utf8')
            max_length = getattr(field_class, 'max_length')
            if max_length:
                try:
                    assert(len(clean_value) <= field_class.max_length)
                except:
                    if verbosity > 0:
                        print
                        print "The row below has a string (%r) that is too long (> %d):" % (clean_value, max_length)
                        print row
                        print_exc()
                        errors += collections.Counter(['num_truncated'])
                    clean_value = clean_value[:max_length]
                    if not ignore_errors:
                        raise  
        if not ignore_values or clean_value not in ignore_values:
            field_dict[field_name] = clean_value
    return field_dict, errors