Utiliza este crédito gratuito para lanzar tus proyectos ahora en Digital Ocean, eres libre de gastarlo cuando quieras en los siguientes 60 días.
Índice del contenido
Categorias en Django usando ForeignKey hacia self
Categorias en Django usando ForeignKey hacia self
La agrupación por categorías es bastante recurrente en aplicaciones web, desde películas, cursos o cualquier otro recurso que presente una relación jerárquica hacía otro objeto. En Django existen diferentes maneras de modelar estas relaciones. Probablemente, la primera que se te vendrá a la mente será crear un objeto categoria, y luego relacionarlo por medio de una ForeignKey con una subcategoria, pero si haces esto estarías cayendo en un error, existe una mejor manera.
¿Qué es una Foreign Key o clave foránea en Django?
En Django, una foreign key (clave foránea) es un campo utilizado para establecer una relación entre dos modelos en una base de datos relacional. Este campo sirve para crear una relación uno a muchos entre dos modelos, donde un modelo tiene una clave que apunta a otro modelo. La foreign key se utiliza como una referencia a la clave primaria de otro modelo.
from django.db import models
class Team(models.Model):
name = models.CharField(max_length=100)
class Member(models.Model):
name = models.CharField(max_length=200)
# Un equipo tiene varios miembros
# models.CASCADE le índica que al borrarse un equipo se borren sus miembros
team = models.ForeignKey(Team, on_delete=models.CASCADE)
Una subcategoría o nivel por modelo
A lo que me refería con una categoría o nivel por modelo es a algo como esto:
# app/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=256)
# otras propiedades
class SubCategory(models.Model):
name = models.CharField(max_length=256)
# otras propiedades
category = models.ForeignKey(Category, related_name="subcategories", blank=True, null=True, on_delete=models.CASCADE)
Esta aproximación al problema de las jerarquias en Django luce bien a primera vista. La estructura que resultará será similar a esta:
El problema de usar un modelo por categoría
Este esquema funcionará en situaciones donde las jerarquias no se aniden muy profundo, pero ¿qué pasa si esas subcategorías tienen a su vez subcategorías?
Imagínate una categoría de películas de terror, con una subcategoría de fantasmas que, a su vez, cuenta con una subcategoría de fantasmas en casa y esta, a su vez, una subcategoría de tipo de final.
Pues, añadimos una clase SubSubCategoría ¿no? Pero… y si esas SubSubCategorías tienen a su vez subcategorías. ¿Ves a donde intento llegar?
Cada vez que necesites crear una subcategoría nueva tendrás que crear un nuevo modelo en el archivo models.py de tu aplicación. Y no solo eso, sino una nueva tabla que probablemente solo cuente con unos cuantos registros. ¿Existe una aproximación mejor al problema? El versátil ORM del Django Framework nos ofrece una solución bastante limpia.
ForeignKey al mismo modelo en Django
Para simplificar el problema de las categorías en Django, creamos un solo modelo, con una propiedad de tipo ForeignKey o llave foránea que apunte al mismo objeto; es decir, a self.
# app/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=256)
parent = models.ForeignKey(
"self",
related_name="subcategories",
on_delete=models.CASCADE,
blank=True,
null=True,
)
De esta manera tendremos una estructura similar a un grafo, donde cada nodo apunta hacía a otro.
Este nuevo acomodo nos permite crear tantas subcategorías como querramos, sin la necesidad de crear nuevos modelos. Para ello, simplemente asignamos la propiedad parent a la instancia de clase Category a la que querramos que pertenezca.
Accediendo al mismo modelo en Django
Mira como funciona el ForeignKey hacía self en la práctica:
from my_app.models import Category
categoria_padre = Category.objects.create(name="Lenguajes de Programacion")
subcategoria = Category.objects.create(name="Python")
subcategoria.parent = categoria_padre
# Guardamos en la base de datos
subcategoria.save()
Te explico lo que sucede. Primero creamos una categoría principal, y una subcategoría. Posteriormente, asignamos la propiedad parent, de esta última, a la categoría principal. Y listo, guardamos.
Para finalizar, creemos una subsubcategoría para nuestra subcategoría
subsubcategoria = Category.objects.create(name="Django")
subsubcategoria.parent = subcategoria
subsubcategoria.save()
Observa como hemos creado otra subsubcategoría (llamada Django), que proviene del mismo modelo que usamos en las otras dos. Para asignarla a una categoría, igualamos su propiedad parent a la subcategoria que habiamos creado (llamada Python).
Como ya viste, un solo modelo nos permite agregar tantas subcategorías como querramos.
Acceder a las subcategorías
Si examinamos la categoría padre, que creamos previamente, observaremos que cuenta con una lista de subcategorías, entre las que ya se encuentra nuestro subcategoría (Llamada Python).
Podemos acceder a ella como haríamos con cualquier otra relación de muchos a uno.
categoria_padre.subcategories.all()
<QuerySet [<Category: Category object (2)>]>
categoria_padre.subcategories.all()[0].name
'Python'
Ir de las subcategorías a las categorías
En cambio, si queremos ir “en reversa” desde la categoría más anidada, hasta la menos anidada, simplemente la vamos recorriendo; accedemos al padre o parent, y luego al padre o parent de la instancia que sigue y así tantas anidaciones como necesitemos.
subsubcategoria.name
'Django'
subsubcategoria.parent.name
'Python'
subsubcategoria.parent.parent.name
'Lenguajes de Programacion'