Crear e imprimir datos en estructura de árbol con Django

Cómo crear e imprimir por pantalla los datos contenidos en una estructura de datos de árbol. Ejemplo aplicado a mostrar carpetas.

Mostrar estructura carpetas django, resultado final

En este post voy a explicar cómo crear y mostrar por pantalla un árbol de carpetas con Django, ya que es un problema que me he encontrado en un proyecto reciente, y puede que a alguien le sirva de ayuda.

Este ejemplo es aplicable a cualquier estructura de árbol, aunque yo lo voy a aplicar a una estructura de carpetas.

El primer paso es crear el modelo:

models.py

from django.db import models
 
class Folder(models.Model):
   name = models.CharField(max_length=128)
   parent = models.ForeignKey("self",null=True,related_name='children')
 
   class Meta:
        verbose_name = "Carpeta"
        verbose_name_plural = "Carpetas"

El atributo parent es el que utilizo como su propio nombre indica para indicar que carpeta es la carpeta padre.
Esta clave foránea se referencia a su propio modelo, que se indica con el valor “self” .
Como nombre relacionado pongo “children”, esto me servirá para obtener todas las carpetas que tengan como clave foránea la carpeta padre.
Esta clave foránea sólo será nula cuando nos refiramos a la carpeta raíz.

Una vez creado el modelo, vamos a la vista:

views.py

def folders(request):
    template = loader.get_template('website/folders.html')
    folders = Folder.objects.filter(parent = None)
 
    context = RequestContext(request, {
        'folders':folders,
    })
 
    return HttpResponse(template.render(context))

En la vista cargamos en la variable folders todas las carpetas que sean raíz y las pasamos a la plantilla.

Os añado una función inicializar para probarlo con un ejemplo:

views.py

def _inicializar():
    Folder.objects.all().delete()
    folder_raiz = Folder(
        name = 'Raiz'
    )
    folder_raiz.save()
    folder = Folder(
        name = 'Docs',
        parent=folder_raiz
    )
    folder.save()
    folder_trab = Folder(
        name = 'Trabajo',
        parent=folder
    )
    folder_trab.save()
 
    folder = Folder(
        name = 'Música',
        parent=folder_raiz
    )
    folder.save()
    folder_photos = Folder(
        name = 'Fotos',
        parent=folder_raiz
    )
    folder_photos.save()
    folder = Folder(
        name = 'Vacaciones',
        parent=folder_photos
    )
    folder.save()
    folder = Folder(
        name = 'Trabajo',
        parent=folder_photos
    )
    folder.save()
 
def folders(request):
    _inicializar()
    template = loader.get_template('website/folders.html')
    folders = Folder.objects.filter(parent = None)
 
 
    context = RequestContext(request, {
        'folders':folders,
    })
 
    return HttpResponse(template.render(context))

Finalmente nos falta la plantilla.

Lo que he hecho básicamente es ejecución recursiva con la propia plantilla.

La plantilla folders.html:

<div id="jstree_cats">
    {% include "website/parts/tree_cats.html" %}
</div>

Y una plantilla en /templates/parts/folders.html:

<ul>
    {% for folder in folders %}
        <li id="child_node-{{ folder.pk }}">{{ folder.name }}
        {% if folder.children.exists %}
            {% with folder.children.all as folders %}
                {% include "website/parts/tree_cats.html" %}
            {% endwith %}
        {% endif %}
        </li>
    {% endfor %}
</ul>

Lo que hacemos aquí es recorrer la lista de carpetas a partir de las carpetas raíz.

Para cada carpeta, consulta las carpetas hijas, y si existen hijas, recorre la lista de carpetas y repite el proceso para cada una.

Gracias a que habíamos especificado en el campo “parent” de la carpeta el atributo “related-name” como children, podemos hacer la consulta directamente con:

folder.children.all

(sinó también podemos buscar los relacionados con “folder.parent_set.all()”, pero me parece más claro hacerlo de esta forma)

El resultado que obtenemos es el siguiente:

Lista de carpetas

Finalmente, vamos a darle un poco de formato y la capacidad de navegar por las carpetas (desplegar, seleccionar carpeta).

He utilizado esta librería (requiere jquery):
https://www.jstree.com/

Así pues, le añado al <head> jquery, y jstree de para que me cargue estas librerías:

base.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="es_ES">
<head>

    <title>Tuto | Programante.com </title>
    <meta charset="UTF-8">


    {% block headcontent %}
        <link rel="stylesheet" type="text/css" href="{% static 'js/libs/vakata-jstree/dist/themes/default/style.min.css' %}">
        <script src='{% static 'js/libs/jquery/jquery-2.0.3.min.js' %}'></script>
        <script src='{% static 'js/libs/vakata-jstree/dist/jstree.min.js' %}'></script>


        <script type="text/javascript">
            $(function () {
                $('#jstree_cats').jstree();
            });
        </script>
    {% endblock %}
</head>
<body>
<div id="wrapper_page">
    {% block content %}
    {% endblock %}
</div>
</body>
</html>

y hago que la plantilla folders.html herede de base.html, para añadir el contenido del <head>:

{% extends "base.html" %}
{% block content %}
<div id="jstree_cats">
    {% include "website/parts/tree_cats.html" %}
</div>
{% endblock %}

El resultado final debería ser este:

crearcarpetas_django2

Junto con la librería “jstree” podríamos cargar estas carpetas con JSON, borrar carpetas, mostrar archivos de las carpetas seleccionadas…

Pero bueno, esto ha sido un introducción para poder resolver el problema de mostrar elementos con almacenados en una estructura de árbol.

Espero que a alguien le sirva de ayuda.

Aquí dejo los archivos:

Acerca del autor : Ivan Díaz

Ivan Díaz

Programador web afincado en Barcelona

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *