(location_stubs, types_by_code, old_collection,
                   excel_importer=None, chunk_size=100)
:param location_stubs: (list) List of LocationStub objects with
        attributes like 'db_object', 'needs_save', 'do_delete' set
    :param types_by_code: (dict) Mapping of 'code' to LocationType SQL objects
    :param excel_importer: Used for providing progress feedback. Disabled on None

    This recursively saves tree top to bottom.
:param location_stubs: (list) List of LocationStub objects with
        attributes like 'db_object', 'needs_save', 'do_delete' set
    :param types_by_code: (dict) Mapping of 'code' to LocationType SQL objects
    :param excel_importer: Used for providing progress feedback. Disabled on None
: param location_stubs : ( list ) List of LocationStub objects with attributes like db_object needs_save do_delete set : param types_by_code : ( dict ) Mapping of code to LocationType SQL objects : param excel_importer : Used for providing progress feedback . Disabled on None
def save_locations(location_stubs, types_by_code, old_collection,
                   excel_importer=None, chunk_size=100):
    """
    :param location_stubs: (list) List of LocationStub objects with
        attributes like 'db_object', 'needs_save', 'do_delete' set
    :param types_by_code: (dict) Mapping of 'code' to LocationType SQL objects
    :param excel_importer: Used for providing progress feedback. Disabled on None

    This recursively saves tree top to bottom.
    """

    def order_by_location_type():
        # returns locations in the order from top to bottom
        types_by_parent = defaultdict(list)
        for _type in types_by_code.values():
            key = _type.parent_type.code if _type.parent_type else ROOT_LOCATION_TYPE
            types_by_parent[key].append(_type)

        location_stubs_by_type = defaultdict(list)
        for l in location_stubs:
            location_stubs_by_type[l.location_type].append(l)

        top_to_bottom_locations = []

        def append_at_bottom(parent_type):
            top_to_bottom_locations.extend(location_stubs_by_type[parent_type.code])
            for child_type in types_by_parent[parent_type.code]:
                append_at_bottom(child_type)

        for top_type in types_by_parent[ROOT_LOCATION_TYPE]:
            append_at_bottom(top_type)

        return top_to_bottom_locations

    # Go through all locations and either flag for deletion or save
    location_stubs_by_code = {stub.site_code: stub for stub in location_stubs}
    to_delete = []
    for stubs in chunked(order_by_location_type(), chunk_size):
        with transaction.atomic():
            for loc in stubs:
                if loc.do_delete:
                    if loc.is_new:
                        if excel_importer:
                            excel_importer.add_progress()
                    else:
                        to_delete.append(loc)
                    continue
                if excel_importer:
                    excel_importer.add_progress()
                if loc.needs_save:
                    # attach location type and parent to location, then save
                    loc_object = loc.db_object
                    loc_object.location_type = types_by_code.get(loc.location_type)
                    parent_code = loc.parent_code
                    if parent_code == ROOT_LOCATION_TYPE:
                        loc_object.parent = None
                    elif parent_code:
                        if parent_code in location_stubs_by_code:
                            loc_object.parent = location_stubs_by_code[parent_code].db_object
                        else:
                            loc_object.parent = old_collection.locations_by_site_code[parent_code]
                    loc_object.save()

    _delete_locations(to_delete, old_collection, excel_importer, chunk_size)
def save_locations ( location_stubs , types_by_code , old_collection , excel_importer = None , chunk_size = 100 ) : def order_by_location_type ( ) : # returns locations in the order from top to bottom types_by_parent = defaultdict ( list ) for _type in types_by_code . values ( ) : key = _type . parent_type . code if _type . parent_type else ROOT_LOCATION_TYPE types_by_parent [ key ] . append ( _type ) location_stubs_by_type = defaultdict ( list ) for l in location_stubs : location_stubs_by_type [ l . location_type ] . append ( l ) top_to_bottom_locations = [ ] def append_at_bottom ( parent_type ) : top_to_bottom_locations . extend ( location_stubs_by_type [ parent_type . code ] ) for child_type in types_by_parent [ parent_type . code ] : append_at_bottom ( child_type ) for top_type in types_by_parent [ ROOT_LOCATION_TYPE ] : append_at_bottom ( top_type ) return top_to_bottom_locations # Go through all locations and either flag for deletion or save location_stubs_by_code = { stub . site_code : stub for stub in location_stubs } to_delete = [ ] for stubs in chunked ( order_by_location_type ( ) , chunk_size ) : with transaction . atomic ( ) : for loc in stubs : if loc . do_delete : if loc . is_new : if excel_importer : excel_importer . add_progress ( ) else : to_delete . append ( loc ) continue if excel_importer : excel_importer . add_progress ( ) if loc . needs_save : # attach location type and parent to location, then save loc_object = loc . db_object loc_object . location_type = types_by_code . get ( loc . location_type ) parent_code = loc . parent_code if parent_code == ROOT_LOCATION_TYPE : loc_object . parent = None elif parent_code : if parent_code in location_stubs_by_code : loc_object . parent = location_stubs_by_code [ parent_code ] . db_object else : loc_object . parent = old_collection . locations_by_site_code [ parent_code ] loc_object . save ( ) _delete_locations ( to_delete , old_collection , excel_importer , chunk_size )