¿Qué vamos a ver aquí? Diferentes tipos de campos para nuestros modelos (que hay muchos) y opciones universales que aceptan, relaciones muchos-a-uno y muchos-a-muchos y cómo cruzarlas para extraer datos y alguna que otra cosa más.
Este tutorial va a ser más teórico que práctico porque si no se haría realmente extenso. También "oiréis" hablar de formularios de Django y de la web de administración. En otros tutos hablaremos de ellos.
Campos
- AutoField : entero autoincremental
- BooleanField : true/false
- CharField : para cadenas cortas de texto. Requiere el argumento max_length.
- CommaSeparatedIntegerField : enteros separados por comas.
- DateField : campo fecha. Argumentos opcionales:
auto_now -> asigna automáticamente la fecha actual cuando se actualiza el objeto del modelo. No se puede sobrescribir.
auto_now_add -> asigna automáticamente la fecha actual cuando se crea el objeto del modelo. No se puede sobrescribir. - DateTimeField : campo fecha y hora. Tiene las mismas opciones que DateField.
- EmailField : es un CharField que chequea que el valor sea una dirección de mail válida. No acepta max_length y su máximo es de 75 caracteres.
- FileField : campo de carga de ficheros. Tiene un argumento requerido:
upload_to -> ruta del sistema de archivos que se complementará con la configuración de la variable MEDIA_ROOT de settings.py.
Para FileField y para ImageField tenemos que tener en cuenta:
- En la configuración (settings.py) es necesario definir la variable MEDIA_ROOT con la ruta completa donde quieres que se comiencen a subir los ficheros y MEDIA_URL con la URL pública base de ese directorio.
- Agregar el campo al modelo definiendo la opción upload_to para decirle a django a cual subdirectorio de MEDIA_ROOT subir el archivo.
- En BBDD se almacena la ruta relativa a partir de MEDIA_ROOT del archivo.
- La opción upload_to acepta formato strftime. Por ejemplo si tu MEDIA_ROOT es '/home/media' y upload_to es '%Y/%m/%d', si subes un archivo el día 20/01/2011 se mandara a '/home/media/2011/01/20'. ¡Cuidado con las fechas! Revisad la variable TIME_ZONE del fichero settings.py para evitar sustos.
Tenemos 3 funciones extras para este campo del modelo:
get_<nombre_campo>_filename()
get_<nombre_campo>_url()
get_<nombre_campo>_size()
path -> Es obligatorio. Contiene la ruta absoluta del directorio del cual debe tomar sus valores.
match -> Opcional. Expresión regular como string que usará para filtrar los nombres de los archivos.
recursive -> Opcional. Es un booleano y por defecto es False.
Ejemplo:
Código :
FilePathField(path="/home/images",match="avatar.*",recursive=True)
max_digits -> cantidad máxima de dígitos permitidos incluyendo los decimales.
decimal_places -> cantidad de posiciones decimales.
Ejemplo si queremos almacenar números hasta 999 sería:
Código :
FloatField(max_digits=5,decimal_places=2)
height_field
width_field
Tenemos 2 funciones más que FileField:
get_<nombre_campo>_height()
get_<nombre_campo>_width()
Este campo requiere la biblioteca Python Imaging Library.
max_length -> por defecto son 200 caracteres.
verify_exists -> booleano que indica si se debe chequear la existencia de la url.
schema_path -> ruta a un esquema del tipo RELAX NG.
Se requiere jing para validar el XML.
Argumentos universales
- null : booleano con valor por defecto False. Si lo indicamos a True django almacenará valores NULL en BBDD. Únicamente sirve para campos que no sean string.
- blank : booleano con valor por defecto False(fuerza que el campo sea obligatorio). Si lo indicamos a True django permitirá que el campo esté vacío.
La mayor parte de las veces vienen juntos de la mano. Diferencias:
null -> es para base de datos.
blank -> es para validación de datos.
Ejemplo:
LANGUAGES_CHOICES=(
('SP','Spanish'),
('FR','French'),
('EN','English'),
)
class Language(models.Model):
lang = models.CharField(max_length=2,choices=LANGUAGES_CHOICES)
Relaciones
Dejo link a la documentación oficial ya que todo no se puede contar en esta vida.
Uno-a-uno
Para esta relación se utiliza el campo OneToOneField. También podríamos usar el campo ForeignField y el argumento unique=True. Para más información a la documentación oficial.
Muchos-a-uno
Para esta se utiliza el campo ForeignKey. Requiere como primer argumento el modelo con el que está relacionado.
Código :
class Comentarios(models.Model): tema = models.ForeignKey(Temas)
Django creará un campo en la tabla del modelo con el patrón <atributo>_id.
Si lo que necesitamos es hacer una relación de este tipo al mismo modelo lo indicaremos con la palabra reservada self como string:
Código :
class Comentarios(models.Model): quoted = models.ForeignKey('self')
También podemos crear una relación a un modelo aun no definido. Para esto deberemos de indicar el modelo como string y no como referencia del modelo.
Esta última posibilidad solo puede usarse para relacionar modelos de la misma aplicación. Si queremos relacionarlo con un modelo de otra aplicación tendremos que usar el primer método.
Tiene argumentos extras los cuales la mayoría se utilizan básicamente para la web de administración de django. Más información a la documentación oficial.
Muchos-a-muchos
Para esta se utiliza el campo ManyToManyField. Requiere como primer argumento el modelo con el que está relacionado. También podemos crear la relación con el mismo modelo ('self') o con un modelo aun no definido('modelo').
Django creará una tabla intermedia entre los modelos relacionados con las claves primarias (se puede especificar el nombre de esta tabla con el argumento db_table).
Tiene argumentos extras los cuales la mayoría se utilizan básicamente para la web de administración de django. Más información en la documentación oficial.
Búsquedas avanzadas con objetos Q
Un objeto Q (del paquete django.db.models) se utiliza para encapsular una colección de argumentos de palabra clave. Estas palabras clave son ni más ni menos que patrones de búsqueda.
Los objetos Q pueden combinarse con & (AND) y | (OR).
Código :
Temas.objetcts.filter(Q(titulo__startswith="Primer") | Q(titulo__startswith="Segundo"))
Se pueden utilizar paréntesis para agrupar. Estos objetos deben preceder a los argumentos normales para los filtros.
Objetos relacionados
Cuando se define una relación en un modelo, tenemos una API de acceso al objeto del modelo relacionado. Por ejemplo un objeto c de nuestro modelo Comentarios puede acceder a su tema relacionado mediante el atributo tema-> c.tema
También tenemos una API para el modelo inverso, es decir, si queremos que el modelo que no tiene definida la relación acceda al objeto del modelo con el que está relacionado. Para esto usamos el manejador <nombre_modelo>_set -> t.comentarios_set.all()
Consultas que cruzan relaciones
Para cruzar una relación en un filtro únicamente hace falta utilizar el nombre del atributo relación y los campos por los que queramos filtrar separados por guiones bajos.
Por ejemplo, quiero todos los Comentarios que estén relacionados con temas que contengan la palabra "primer" en el titulo. Para esto haríamos:
Código :
c_list = Comentarios.objects.filter(tema__titulo__icontains="primer")
Como podéis ver uso tema (nombre del atributo del modelo Comentarios) y no temas (nombre del modelo).
También podemos hacer el filtro al contrario, desde el modelo que no define la relación al modelo que sí. Para ello tenemos que indicar el nombre del modelo en minúsculas seguido de los campos para el filtrado. Por ejemplo:
Código :
t_list = Temas.objects.filter(comentarios__texto__icontains="cristalab")
Relaciones de clave foránea
Si un modelo contiene un ForeignKey, las instancias tendrán acceso al objeto relacionado como un simple atributo:
Código :
c = Comentarios.objects.get(id=1) c.tema
De esta manera podemos modificar la relación creada así:
Código :
c = Comentarios.objects.get(id=1) t = Temas.objects.get(id=1) c.tema = t c.save()
Relaciones de clave foránea "inversas"
Si antes hablábamos de relacionar el modelo con el campo ForeignField al modelo relacionado, ahora hablamos de la relación inversa. Podemos tener acceso al manager del modelo con el que tiene relación por lo que podríamos filtrar la consulta sin problemas.
Para acceder al manager basta con escribir <nombre_modelo_minusculas>_set. Por ejemplo:
Código :
t = Temas.objects.get(id=1) t.comentarios_set.all()
¿Por qué aquí ponemos el nombre del modelo y no el atributo?
Porque no tenemos ningún atributo en el modelo Temas que lo relacione con Comentarios
Este manager, a parte de las funciones ya vistas, tiene funciones adicionales:
- add(obj1, obj2, ...) -> agrega los objetos del modelo pasado por argumento al conjunto de objetos relacionados.
Código :
t = Temas.objects.get(id=1) c = Comentarios.objects.get(id=1) t.comentarios_set.add(c) #Creamos la relación entre el tema con id=1 y el comentario con id=1
- create(<argumentos>) -> crea el objeto, lo guarda y lo relaciona. En argumentos debemos indicar todos los atributos con sus valores correspondientes para crear el modelo sin incluir el de la relación (en el ejemplo solo indicamos el atributo texto porque es el único atributo del modelo Comentarios). Esta función devuelve el objeto Comentarios creado.
Código :
t = Temas.objects.get(id=1) t.comentarios_set.create(texto="Texto de 1 comentario nuevo")
- remove(obj1,obj2,...) -> si add agregaba los modelos a la relación, remove los quita de la relación.
- clear() -> quita todos los objetos relacionados.
Las funciones remove y clear sólo se pueden usar para atributos ForeignField que acepten nulos (null=True). Estas funciones no eliminan datos, sólo pasan la relación a null, de ahí que sea obligatorio que acepten nulos.
Relaciones muchos a muchos
También tenemos una API de acceso a ambos extremos de la relación.
Para el modelo que define el campo ManyToManyField tenemos que usar el nombre del atributo.
Para el modelo que no define el campo ManyToManyField usaremos <nombre_modelo_minusculas>_set.
Nota sobre consultas de relaciones
Cuando hacemos una consulta en las que hay relaciones de por medio podemos realizar esa consulta pasando como argumento una instancia del modelo. Con un ejemplo se ve perfecto:
Código :
t = Temas.objects.get(id=1) c = Comentarios.objects.filter(tema=t) c = Comentarios.objects.filter(tema=t.id) c = Comentarios.objects.filter(tema=1)
Los tres filtrados son distintas maneras de conseguir el mismo resultado. Úsalas según te convenga.
ATAJOS
Los atajos más usados para modelos de datos son los siguientes (en el paquete django.shortcuts como no):
get_object_or_404()
Equivale a llamar a get y en caso de no existir el objeto lanza Http404(por defecto se lanzaría la excepción DoesNotExist).
Toma como argumentos el modelo o manager del modelo y los filtros que quieras realizar.
Código :
c = get_object_or_404(Comentarios,id=20) Traceback (most recent call last): File "<console>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/django/shortcuts/__init__.py", line 115, in get_object_or_404 raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) Http404: No Comentarios matches the given query. c = Comentarios.objects.get(id=20) Traceback (most recent call last): File "<console>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 132, in get return self.get_query_set().get(*args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 349, in get % self.model._meta.object_name) DoesNotExist: Comentarios matching query does not exist.
get_list_or_404()
Se comporta como el anterior pero llama a filter en vez de a get.
Código :
t = get_list_or_404(Temas,titulo__contains="tema") t [<Temas: Temas object>, <Temas: Temas object>] t = get_list_or_404(Temas,titulo__contains="CRISTALAB") Traceback (most recent call last): File "<console>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/django/shortcuts/__init__.py", line 128, in get_list_or_404 raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) Http404: No Temas matches the given query.
Bueno por fin lo terminé. Espero que no me haya comido nada importante y si es así y alguien sabio lee el tutorial que lo diga.
En el siguiente tutorial vamos a dar una pincelada a la web de administración de django y en el siguiente a ese hablaremos de cookies, sesiones y como django nos ayuda con la creación y validación de usuarios.
¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.
Por alejo8591 el 29 de Febrero de 2012
Por agares el 01 de Marzo de 2012
Por @diegoug el 19 de Junio de 2012
Por Ramiro Vergara el 26 de Mayo de 2013
Por fernando_clab el 15 de Julio de 2014
Osea una clase agregue a 1 o varias de otras clases, en UML sería la relacion de agregacion.
Supongamos el caso:
Quiero crear un directorio de archivos y carpetas...
en 'carpeta' debo tener un atributo 'tipo' array(o parecido, en php se hace asi) que contenga o agregue a otra carpeta(osea, se aplicaria algo recursivo), y ademas otro atributo(tipo array nuevamente) que agregue o contenga archivos.
En definitiba serian 2 atributos que agreguen a otras de sus tipos, pero la de carpeta es recursiva para poder armar el arbol, ya que carpeta puede agregar carpeta o archivo....
En php seria algo asi
class carpeta{
var arrayCarpeta;
var arrayArchivo
function add(Objetc obj){
....
}
}
y lo usarias algo asi:
$raiz = new Carpeta()
$carpetaX = new Carpeta()
$archivoX = new Archivo()
$carpetaX->add($archivoX)
$raiz->add($carpetaX)
/raiz
--->carpetaX
---->archivoX
---->carpetaZ
---->archivoZ
Osea, la duda radica en como representar en models el tema del array, porque en php lo soluciona con un simple array...pero en Django se trata de forenkey ...etc...etc, a eso me refiero.
Gracias.