'Dump a set of Model objects to a file as text, ask the user
to edit it, and apply any changes to the objects.
Return a boolean indicating whether the edit succeeded.'

def edit_objects(self, objs, fields):

    old_data = [flatten(o, fields) for o in objs]
    if six.PY2:
        new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False)
    else:
        new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False, encoding='utf-8')
    old_str = dump(old_data)
    new.write(old_str)
    if six.PY2:
        old_str = old_str.decode('utf-8')
    new.close()
    try:
        while True:
            edit(new.name, self._log)
            with codecs.open(new.name, encoding='utf-8') as f:
                new_str = f.read()
            if (new_str == old_str):
                ui.print_(u'No    changes;    aborting.')
                return False
            try:
                new_data = load(new_str)
            except ParseError as e:
                ui.print_(u'Could    not    read    data:    {}'.format(e))
                if ui.input_yn(u'Edit    again    to    fix?    (Y/n)', True):
                    continue
                else:
                    return False
            objs_old = [(deepcopy(obj) if (not obj._db) else None) for obj in objs]
            self.apply_data(objs, old_data, new_data)
            changed = False
            for (obj, obj_old) in zip(objs, objs_old):
                changed |= ui.show_model_changes(obj, obj_old)
            if (not changed):
                ui.print_(u'No    changes    to    apply.')
                return False
            choice = ui.input_options((u'continue    Editing', u'apply', u'cancel'))
            if (choice == u'a'):
                return True
            elif (choice == u'c'):
                return False
            elif (choice == u'e'):
                for obj in objs:
                    obj.read()
                continue
    finally:
        os.remove(new.name)
