Create dict {obj.<field>: obj.pk} for all field_values in a model or queryset.

def index_model_field(model, field, value_field='pk', key_formatter=str.strip, value_formatter=str.strip, batch_len=10000, limit=10000000, verbosity=1):
    '''Create dict {obj.<field>: obj.pk} for all field_values in a model or queryset.
    '''
    try:
        qs = model.objects
    except:
        qs = model

    N = qs.count()
    if verbosity > 0:
        print 'Indexing %d rows to aid in finding %s.%s values using %s.%s.' % (N, qs.model.__name__, value_field, qs.model.__name__, field)

    index, dupes, rownum = {}, {}, 0

    pbar, rownum = None, 0
    if verbosity and N > min(1000000, max(0, 100000**(1./verbosity))):
        widgets = [pb.Counter(), '/%d rows: ' % N, pb.Percentage(), ' ', pb.RotatingMarker(), ' ', pb.Bar(),' ', pb.ETA()]
        pbar = pb.ProgressBar(widgets=widgets, maxval=N).start()

    # to determine the type of the field value and decide whether to strip() or normalize in any way
    #obj0 = qs.filter(**{field + '__isnull': False}).all()[0]

    for obj in qs.all():
        field_value = getattr(obj, field)
        try:
            field_value = key_formatter(field_value)
        except:
            pass
        if value_field:
            entry_value = getattr(obj, value_field)
        else:
            entry_value = obj
        try:
            entry_value = value_formatter(entry_value)
        except:
            pass
        if field_value in index:
            dupes[field_value] = dupes.get(field_value, []) + [entry_value]
        else:
            index[field_value] = entry_value
        rownum += 1
        if rownum >= limit:
            break
        if pbar:
            pbar.update(rownum)
    if pbar:
        pbar.finish()
    if verbosity > 0:
        print 'Found %d duplicate %s values among the %d records or %g%%' % (len(dupes), field, len(index), len(dupes)*100./(len(index) or 1.))
    return index, dupes