'This is slow. Caching must be used.
One menu is built per language and per site.
Namespaces: they are ID prefixes to avoid node ID clashes when plugging
multiple trees together.
- We iterate on the list of nodes.
- We store encountered nodes in a dict (with namespaces):
done_nodes[<namespace>][<node\'s id>] = node
- When a node has a parent defined, we lookup that parent in done_nodes
if it\'s found:
set the node as the node\'s parent\'s child (re-read this)
else:
the node is put at the bottom of the list'

def _build_nodes(self, site_id):

    if site_id:
        warnings.warn('The    site_id    parameter    to    _build_nodes    has    been    deprecated.    It    will    be    removed    in    django    CMS    3.5', PendingDeprecationWarning)
    else:
        site_id = self.site.pk
    key = self._get_cache_key(site_id)
    cached_nodes = cache.get(key, None)
    if (cached_nodes and self._is_cached(site_id)):
        return cached_nodes
    final_nodes = []
    toolbar = getattr(self.request, 'toolbar', None)
    for menu_class_name in self.menus:
        menu = self.get_menu(menu_class_name)
        try:
            nodes = menu.get_nodes(self.request)
        except NoReverseMatch:
            nodes = []
            if (toolbar and toolbar.is_staff):
                messages.error(self.request, (_('Menu    %s    cannot    be    loaded.    Please,    make    sure    all    its    urls    exist    and    can    be    resolved.') % menu_class_name))
            logger.error(('Menu    %s    could    not    be    loaded.' % menu_class_name), exc_info=True)
        final_nodes += _build_nodes_inner_for_one_menu(nodes, menu_class_name)
    cache.set(key, final_nodes, get_cms_setting('CACHE_DURATIONS')['menus'])
    if (not self._is_cached(site_id)):
        setattr(self, '_is_cached_{}'.format(site_id), True)
        CacheKey.objects.create(key=key, language=self.language, site=site_id)
    return final_nodes
