Move the `Part` model to target parent.

    .. versionadded:: 2.3

    :param part: `Part` object to be moved
    :type part: :class:`Part`
    :param target_parent: `Part` object under which the desired `Part` is moved
    :type target_parent: :class:`Part`
    :param name: how the moved top-level `Part` should be called
    :type name: basestring
    :param include_children: True to move also the descendants of `Part`. If False, the children will be lost.
    :type include_children: bool
    :return: moved :class: Part model.
    :raises IllegalArgumentError: if target_parent is descendant of part

def relocate_model(part, target_parent, name=None, include_children=True):
    """
    Move the `Part` model to target parent.

    .. versionadded:: 2.3

    :param part: `Part` object to be moved
    :type part: :class:`Part`
    :param target_parent: `Part` object under which the desired `Part` is moved
    :type target_parent: :class:`Part`
    :param name: how the moved top-level `Part` should be called
    :type name: basestring
    :param include_children: True to move also the descendants of `Part`. If False, the children will be lost.
    :type include_children: bool
    :return: moved :class: Part model.
    :raises IllegalArgumentError: if target_parent is descendant of part
    """
    if target_parent.id in get_illegal_targets(part, include={part.id}):
        raise IllegalArgumentError('cannot relocate part "{}" under target parent "{}", because the target is part of '
                                   'its descendants'.format(part.name, target_parent.name))

    # First, if the user doesn't provide the name, then just use the default "Clone - ..." name
    if not name:
        name = "CLONE - {}".format(part.name)

    # The description cannot be added when creating a model, so edit the model after creation.
    part_desc = part._json_data['description']
    moved_part_model = target_parent.add_model(name=name, multiplicity=part.multiplicity)
    if part_desc:
        moved_part_model.edit(description=str(part_desc))

    # Map the current part model id with newly created part model Object
    get_mapping_dictionary().update({part.id: moved_part_model})

    # Loop through properties and retrieve their type, description and unit
    list_of_properties_sorted_by_order = part.properties
    list_of_properties_sorted_by_order.sort(key=lambda x: x._json_data['order'])
    for prop in list_of_properties_sorted_by_order:
        prop_type = prop._json_data.get('property_type')
        desc = prop._json_data.get('description')
        unit = prop._json_data.get('unit')
        options = prop._json_data.get('options')

        # On "Part references" properties, the models referenced also need to be added
        if prop_type == PropertyType.REFERENCES_VALUE:
            referenced_part_ids = [referenced_part.id for referenced_part in prop.value]
            moved_prop = moved_part_model.add_property(name=prop.name, description=desc, property_type=prop_type,
                                                       default_value=referenced_part_ids)

        # On "Attachment" properties, attachments needs to be downloaded and re-uploaded to the new property.
        elif prop_type == PropertyType.ATTACHMENT_VALUE:
            moved_prop = moved_part_model.add_property(name=prop.name, description=desc, property_type=prop_type)
            if prop.value:
                attachment_name = prop._json_data['value'].split('/')[-1]
                with temp_chdir() as target_dir:
                    full_path = os.path.join(target_dir or os.getcwd(), attachment_name)
                    prop.save_as(filename=full_path)
                    moved_prop.upload(full_path)

        # Other properties are quite straightforward
        else:
            moved_prop = moved_part_model.add_property(name=prop.name, description=desc, property_type=prop_type,
                                                       default_value=prop.value, unit=unit, options=options)

        # Map the current property model id with newly created property model Object
        get_mapping_dictionary()[prop.id] = moved_prop

    # Now copy the sub-tree of the part
    if include_children:
        # Populate the part so multiple children retrieval is not needed
        part.populate_descendants()
        # For each part, recursively run this function
        for sub_part in part._cached_children:
            relocate_model(part=sub_part, target_parent=moved_part_model, name=sub_part.name,
                           include_children=include_children)
    return moved_part_model