Un modelo de datos es una descripción de los datos de una base de datos. Es una representación de la estructura de tu base de datos (tablas, campos, relaciones) en clases, en este caso escritas en Python.
¿Cómo no debemos conectarnos a nuestra base de datos?
Un ejemplo de cómo nos conectaríamos a base de datos sin modelo de datos y mostrar el resultado en una vista podría ser:
Código :
from django.shortcuts import render_to_response import MySQLdb def book_list(request): db = MySQLdb.connect(user='myuser',db='mydb',passwd='mypassword',host='localhost') cursor = db.cursor() cursor.execute('SELECT NAME FROM BOOKS ORDER BY NAME') names = [row[0] for row in cursor.fetchall()] db.close() return render_to_response('booklist.html',{'names':names})
¿Qué os parece? A mí me parece poco segur. Demasiados pasos para un simple select, demasiadas variables con lo que eso conlleva y 100 cosas más que podría decir.
Ahora veamos el mismo ejemplo usando modelo de datos:
Código :
from django.shortcuts import render_ro_response from mysite.books.models import Book def book_list(request): books = Book.objetcts.order_by('name') return render_to_response('booklist.html',{'books':books})
Mucho mejor así ¿verdad? Empecemos.
Configurando la base de datos
Tendremos que tener ya el servidor de base de datos activo y una base de datos creada (la mía se llama foroTutorial).
¿Dónde pensáis que debemos configurar la base de datos para Django? Como no, en el fichero settings.py. Lo abrimos y buscamos la variable DATABASES:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial$ view settings.py [...] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 'NAME': '', # Or path to database file if using sqlite3. 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 'PORT': '', # Set to empty string for default. Not used with sqlite3. } }
Esta variable es un diccionario que incluye la configuración de nuestras bases de datos. Por defecto se usará la que configuremos con el nombre default (que ya viene indicada). Vamos a explicar cada par de clave-valor que tenemos que configurar:
ENGINE:
indica a django que servidor de base de datos usamos. El valor de esta clave tendra que ser "django.db.backends." y uno de los siguientes valores:- MySQL -> "django.db.backends.mysql" y el adaptador MySQLdb
- Oracle -> "django.db.backends.oracle" y el adaptador cx_Oracle
- SQLite -> "django.db.backends.sqlite3" y el adaptador pysqlite
- PostgreSQL -> "django.db.backends.postgresql" y el adaptador psycopg version 1.x
- PostgreSQL -> "django.db.backends.postgresql_psycopg2" y el adaptador psycopg version 2.x
NAME:
nombre de la base de datos. Si usas SQLite tendremos que especificar la ruta al archivo de base de datos ('/home/alberto/database.db').USER:
usuario de acceso. Si usas SQLite déjala en blanco.PASSWORD:
password del usuario de acceso. Si usas SQLite o no tiene password déjala en blanco.HOST:
como su nombre indica, es el host donde se encuentra el servidor de base de datos. Si está en el mismo que Django o usas SQLite déjala en blanco.PORT:
puerto para conectarse a la base de datos. Si lo dejamos en blanco usará el puerto por defecto dependiendo del ENGINE. No hay que indicarlo para SQLite.Bueno pues el nuestro quedara más o menos así (yo uso el usuario root, cosa para nada recomendable y menos con el password que tiene...):
Código :
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 'NAME': 'foroTutorial', # Or path to database file if using sqlite3. 'USER': 'root', # Not used with sqlite3. 'PASSWORD': 'alberto', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 'PORT': '', # Set to empty string for default. Not used with sqlite3. } }
Probando la conexión
Vamos a la terminal al directorio de nuestro proyecto y ejecutamos "python manage.py shell". Este comando abrirá una shell de python pero con todo nuestro proyecto configurado. Es muy útil para pruebas, cambios de configuraciones y otras tareas que veremos más adelante.
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial$ python manage.py shell Python 2.7.2+ (default, Oct 4 2011, 20:03:08) [GCC 4.6.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.db import connection >>> cursor = connection.cursor()
Si no aparece ningún error es que tenemos todo perfecto. Si aparece algún error revisadlo porque las excepciones son muy claras y te indican donde está el error.
Creando nuestra primera aplicación
Una aplicación de Django es un conjunto portable de una funcionalidad, en este caso de nuestro proyecto web. Normalmente incluyen modelos de datos y vistas que conviven en el mismo paquete. Por ejemplo, Cristalab sería el proyecto y las aplicaciones serian blog, foro, tutoriales, etc.
En tutoriales anteriores hemos trabajado sin aplicación, pero ya es hora de coger un buen hábito que es utilizar siempre al menos una aplicación. Si quieres utilizar modelos de datos es obligatorio así que ¿por qué no vamos a trabajar como es debido?
Para crearla basta con lanzar el comando en el directorio de nuestro proyecto: django-admin startapp <nombre_aplicacion>. Vamos a crear un mini-foro por lo que llamamos a nuestra aplicación foro. Vamos a ver qué pasa, porque creo que tenemos un nuevo directorio:
Código :
tutorial/foro: total 12 -rw-r--r-- 1 alberto alberto 57 2012-02-19 12:45 models.py -rw-r--r-- 1 alberto alberto 0 2012-02-19 12:45 __init__.py -rw-r--r-- 1 alberto alberto 383 2012-02-19 12:45 tests.py -rw-r--r-- 1 alberto alberto 26 2012-02-19 12:45 views.py
- __init__.py : indica a python que aqui hay código suyo.
- views.py : este os sonará ya
- models.py : es el fichero donde definiremos nuestro modelo de datos
- tests.py : se utiliza para escribir y lanzar pruebas sobre nuestra aplicación.
Definir modelos
Como hemos dicho en la descripción, el modelo de datos son clases Python que representan la estructura de base de datos.
Vamos a editar el fichero models.py y dejarlo de la siguiente manera:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial/foro$ view models.py from django.db import models # Create your models here. class Temas(models.Model): titulo = models.CharField(max_length=40) cuerpo = models.CharField(max_length=300) class Comentarios(models.Model): texto = models.CharField(max_length=150) tema = models.ForeignKey(Temas)
- Cada modelo es una subclase de Model que se encuentra en el paquete django.db.models.
- Cada modelo generalmente corresponde a una tabla y cada atributo del modelo se corresponde a un campo de la tabla.
- Para establecer atributos (o campos) escribiremos: <nombre_atributo> = <tipo_de_campo>
- El tipo de campo se corresponderá con la instancia de una clase del paquete models dependiendo de cual queramos o necesitemos.
- Por defecto django pone un campo id como clave primaria a todos los modelos y será un dígito autoincremental.
En el próximo tutorial hablaremos de los diferentes tipos de campo y de relaciones entre tablas.
Ahora hemos creado un atributo tema en el modelo Comentarios que apunta al modelo Temas. Esto lo he hecho para ir preparándome el terreno para el segundo tutorial y básicamente es una relación 1-1 de las tablas Comentarios-Temas, porque un comentario sólo pertenece a un tema.
Instalando nuestros modelos
Una vez tenemos definidos los modelos es hora de que la base de datos se entere.
Primero tendremos que modificar nuestro fichero settings.py una vez más para indicarle que tenemos una aplicación nueva:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial$ view settings.py [...] INSTALLED_APPS = ( #'django.contrib.auth', #'django.contrib.contenttypes', #'django.contrib.sessions', #'django.contrib.sites', #'django.contrib.messages', #'django.contrib.staticfiles', 'tutorial.foro', # Uncomment the next line to enable the admin: # 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', )
Como veis hay muchas aplicaciones "instaladas" por defecto. Todas las que veis ya insertadas se utilizan para gestión de usuarios y sesiones web entre otras funciones. Yo las tengo comentadas porque de momento no las necesitamos. Cuando toque el tutorial de sesiones en python volveremos a añadirlas.
Añadimos nuestra aplicación indicando tutorial.foro (proyecto.aplicacion) y OJO con la coma al final, no me canso de decirlo. En las tuplas es buena práctica dejar siempre una coma al final.
Django ya sabe que tenemos una aplicación nueva. Ahora vamos a usarla para facilitarnos la vida.
Podemos obtener toda la sentencia sql para crear la base de datos y no tener que escribirla a mano. Para eso lanzamos el comando "python manage.py sqlall <nombre_aplicacion>" en nuestro caso foro. Pero antes de crear nada lee el siguiente punto.
Syncdb
Podemos pedirle a django que cree toda la estructura sin tener que conectarnos nosotros y copiar y pegar el sql. Para eso lanzamos el comando "python manage.py syncdb" y veremos algo así:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial$ python manage.py syncdb Creating tables ... Creating table foro_temas Creating table foro_comentarios Installing custom SQL ... Installing indexes ... No fixtures found.
Cosas a tener en cuenta sobre syncdb:
- Este comando recorre todas las aplicaciones de la variable INSTALLED_APPS y crea la estructura en nuestra base de datos si no está creada ya, por lo que si no hubiéramos descomentado las aplicaciones de django, hubiéramos visto una salida diferente a la nuestra y tendríamos muchas más tablas creadas.
- Syncdb sólo crea, no modifica ni elimina. ¿Qué quiere decir esto? Si ahora agregáramos un atributo al modelo Temas y ejecutáramos syncdb no pasaría nada. Si eliminásemos el modelo Comentarios y ejecutáramos syncdb no haría nada. Esto es por seguridad, nos tocan esas tareas a nosotros. En cambio si añadiéramos un modelo más, este cambio sí lo realizaría en base de datos.
- Las tablas las creará en minúsculas y con el patrón <aplicacion>_<nombre_modelo>.
- Creará los campos id como claves primarias autoincrementales.
- El campo clave foránea seguirá el patrón <tabla_a_la_que_apunta>_id.
Si revisamos nuestra base de datos esto es lo que veremos:
Código :
mysql> show tables; +------------------------+ | Tables_in_foroTutorial | +------------------------+ | foro_comentarios | | foro_temas | +------------------------+ 2 rows in set (0.00 sec) mysql> desc foro_temas; +--------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | titulo | varchar(40) | NO | | NULL | | | cuerpo | varchar(300) | NO | | NULL | | +--------+--------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec) mysql> desc foro_comentarios; +---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | texto | varchar(150) | NO | | NULL | | | tema_id | int(11) | NO | MUL | NULL | | +---------+--------------+------+-----+---------+----------------+ 3 rows in set (0.01 sec)
Insertando datos
A partir de ahora voy a utilizar la shell de nuestro proyecto por lo que no hace falta que salgáis.
Para insertar datos tenemos dos maneras de hacerlo:
Código :
>>> from tutorial.foro.models import Temas >>> prueba = Temas(titulo='Titulo primer tema',cuerpo='Este es el cuerpo de nuestro primer tema del foro') >>> prueba.save()
O esta:
Código :
>>> from tutorial.foro.models import Temas >>> prueba = Temas.objects.create(titulo='Titulo segundo tema',cuerpo='Cuerpazo de nuestro segundo tema del foro') >>>
Atentos al import: <proyecto>.<aplicacion>.models import <modelos>
La diferencia es que en el primero puedes modificarlo antes de guardarlo en base de datos con .save() y, en el segundo, se inserta directamente.
Seleccionando datos
Nos acordamos de importar los modelos que vamos a usar, en este caso Temas: from tutorial.foro.models import Temas
Para seleccionar todos los objetos del modelo Temas haremos:
Código :
>>> from tutorial.foro.models import Temas >>> lista = Temas.objects.all() >>> lista [<Temas: Temas object>, <Temas: Temas object>] >>> type(lista) <class 'django.db.models.query.QuerySet'>
Objects es un administrador que se encarga de todas las operaciones a nivel de tablas. Todos los modelos llevan asociado este administrador y lo usaremos para trabajar con los datos.
all() nos extraerá todos los datos de la tabla foro_temas. Todas estas funciones devuelven un QuerySet que contiene el listado de objetos de nuestro modelo. Funcionan igual que las listas por lo que si quisiéramos acceder al primero haríamos:
Código :
>>> lista[0].id,lista[0].cuerpo (1L, u'Este es el cuerpo de nuestro primer tema del foro') >>> type(lista[0].id) <type 'long'> >>> type(lista[0].cuerpo) <type 'unicode'> >>> str(lista[0].cuerpo) 'Este es el cuerpo de nuestro primer tema del foro'
Os parecerá raro que muestre 1L o u'TEXTO'. Si os fijáis en los tipos vemos que el número es del tipo long y el texto es unicode. Podemos modificarlos utilizando las funciones int() y str().
Hasta aquí todo bien pero no siempre queremos extraer todos los datos. Queremos filtrar, excluir, ordenarlos,... pues para todo esto tenemos las siguientes funciones del administrador objects:
- filter : es el más usado. Equivale a WHERE y podemos filtrar por los atributos del modelo, por ejemplo: Temas.objects.filter(id=2)
Se puede filtrar por varios campos como si de un AND se tratase: Temas.objects.filter(id=1,titulo='Titulo primer tema').
Ahora extendemos un poco más los usos de filter. - exclude : acción inversa a filter. También equivale a WHERE y excluye de los resultados los que cumplan las condiciones que le pasemos por parámetro. Por ejemplo podríamos hacer: Temas.objects.all().exclude(id=2)
- order_by : para ordenar el resultado. Equivale a ORDER BY y tendremos que pasar por parámetro un string con el título del atributo por el que queramos ordenar. Podemos ordenar por varios y hacerlo tanto ascendente como descendentemente.
*ascendente: Temas.objects.order_by('titulo','id')
*descendente: Temas.objects.order_by('-titulo') - values : devuelve el resultado como diccionario de los valores y no como un QuerySet:
Código :
>>> lista = Temas.objects.filter(id=1).values() >>> lista [{'cuerpo': u'Este es el cuerpo de nuestro primer tema del foro', 'titulo': u'Titulo primer tema', 'id': 1L}]
- get : idéntico a filter pero solo sirve para "consultas" que devuelvan un único resultado. Esta función devolverá un objeto de la clase del modelo y no un QuerySet. Si la consulta devuelve más de un resultado se devolverá una excepción:
Código :
>>> lista = Temas.objects.all().get() Traceback (most recent call last): File "<console>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 351, in get % (self.model._meta.object_name, num, kwargs)) MultipleObjectsReturned: get() returned more than one Temas -- it returned 2! Lookup parameters were {} >>> lista = Temas.objects.filter(id=1).get() >>> lista <Temas: Temas object> >>> type(lista) <class 'tutorial.foro.models.Temas'>
- count : cuenta los registros devueltos.
Patrones de búsqueda
Estos patrones son diferentes argumentos que podemos pasar a filter(), exclude() y get() para obtener el resultado esperado.
Se utilizan de la siguiente manera: funcion(<campo>__<patron>=valor), por ejemplo, Temas.objects.filter(titulo__icontains='primer')
Si dentro del texto a buscar introducimos los caracteres ', %, _, ; y alguno más, estos serán escapados automáticamente. Si por ejemplo escribiéramos filter(titulo="Primer%''''") esto se corresponde con titulo='Primer\%\'\'\'\''. Esto evita problemas de seguridad como inyecciones de SQL.
- exact : es el que usa por defecto si no lo indicamos. [...].filter(id=1) == [...].filter(id__exact=1)
- iexact : busca el exacto pero sin distinguir mayúsculas de minúsculas
- contains : busca que el campo contenga el texto. Equivale a LIKE '%<texto>%'.
- icontains : igual que el anterior pero sin distinguir mayúsculas de minúsculas
- gt, gte, lt, lte : sirve para números y se corresponden con >, >=, < y <=. Cuidado al usarlo con texto: 10 es menor que 4
- in : para buscar los valores que coincidan con los de la lista. La forma de usarlo es pasando una lista o tupla después del igual: id__in=[1,5,7]
- startswith, istartswith, endswith, iendswith: no hace falta decir nada...
- range : para búsquedas dentro de un rango. Equivale a BETWEEN X AND Y. Ejemplo: id__range=(1,5)
- year, month, day : para búsquedas en campos date o datetime. Podemos filtrar por año, mes o dia. Ejemplo: date__year=2012,date__month=2
- isnull : debemos pasarle True si queremos que extraiga los nulos o None si queremos que extraiga los no nulos.
- search : igual que contains pero más rápido. Requiere MySQL y que el campo sea índice del tipo full_text.
- pk : sirve para buscar por la clave primaria sin tener que indicar el campo. pk=5 sería igual que id=5
Eliminando datos
Para eliminar únicamente basta con filtrar por lo que queramos borrar y llamar a la función delete():
Código :
prueba = Temas.objects.filter(id=1) prueba.delete()
Uff!!! cuantas cosas sobre los modelos de datos y todavía nos queda más. En el próximo capítulo aprenderemos los tipos de datos que podemos utilizar y un poquito más sobre cómo crear relaciones entre tablas, seleccionar, etc.
¿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 ach4m0 el 22 de Febrero de 2012
Temas.objects.filter(id__range=(1,5).filter(titulo__icontains="tema").exclude(id=1)
Por ejemplo....
Por Tifa^ el 29 de Febrero de 2012
Por Inyaka el 29 de Febrero de 2012
Por ach4m0 el 29 de Febrero de 2012
Por oswalgogra el 11 de Marzo de 2012
Me gustaría saber algo, ya que no consigo hacerlo:
Cómo se puede utilizar varias aplicaciones en un mismo proyecto? es que tengo muchos modelos y quiero dividir el proyecto en varias aplicaciones, para ordenar un poco el proyecto.
Agradezco la ayuda!
Por ach4m0 el 11 de Marzo de 2012
Anonymous :
Me gustaría saber algo, ya que no consigo hacerlo:
Cómo se puede utilizar varias aplicaciones en un mismo proyecto? es que tengo muchos modelos y quiero dividir el proyecto en varias aplicaciones, para ordenar un poco el proyecto.
Agradezco la ayuda!
Por ach4m0 el 11 de Marzo de 2012
ach4m0 :
Anonymous :
Me gustaría saber algo, ya que no consigo hacerlo:
Cómo se puede utilizar varias aplicaciones en un mismo proyecto? es que tengo muchos modelos y quiero dividir el proyecto en varias aplicaciones, para ordenar un poco el proyecto.
Agradezco la ayuda!
Por ach4m0 el 11 de Marzo de 2012
Se recomienda dividir el proyecto en aplicaciones. Si tienes un proyecto que se llama tutorial y quieres crear dos aplicaciones con nombres ap1 y ap2 tendrias que hacer:
alberto@a-AMILO-Si-3655:~/django/tutorial$ django-admin startapp ap1
alberto@a-AMILO-Si-3655:~/django/tutorial$ django-admin startapp ap2
Después te tocará agregar las siguientes lineas a la variable INSTALLED_APPS del fichero settings.py:
'tutorial.ap1',
'tutorial.ap2',
Si quisieras por ejemplo acceder a algun modelo de la ap2 en la ap1 podrias hacerlo importandolo asi:
from tutorial.ap2.models import *
Siento los mensajes en blanco de antes ;-O
Por ali el 08 de Junio de 2012
Por Andres el 07 de Febrero de 2013
Ahí va:
En mi caso tengo por ejemplo dos input tipo text y un botón (en el template.html) como hago para que ese botón me llame a una función y pasar por parámetro lo correspondiente a los dos input y de esta manera hacer cambios en la BD agradezco tu ayuda! no lo consigo por ningún lado
Por ach4m0 el 07 de Febrero de 2013
Andres-blog :
Ahí va:
En mi caso tengo por ejemplo dos input tipo text y un botón (en el template.html) como hago para que ese botón me llame a una función y pasar por parámetro lo correspondiente a los dos input y de esta manera hacer cambios en la BD agradezco tu ayuda! no lo consigo por ningún lado
Hola Andres! Lo que tendrias que hacer es con ese boton llamar a una url que tenga una vista asociada y desde esa vista recoges parametros e instancias modelos para hacer los cambios.
Por Andres el 10 de Febrero de 2013
(r'^juegos/(P<pagina>\d+)/(P<idgame>[\d -_])/(P<resa>[\d -_])/(P<resb>[\d -_])/$', 'JuegoApp.views.juegosguardar'),
(r'^juegos/(?P<pagina>\d+)', 'JuegoApp.views.juegosap'),
Juegos
models.py
from django.shortcuts import render_to_response
from scorecenter.JuegoApp.models import Juego
from scorecenter.PrediccionApp.models import Prediccion
def juegosguardar(request, pagina="1", idgame=1, resa=0, resb=1):
game = Juego.objects.filter(id=idgame).get
temporal = Prediccion(idusario=2, idjuego=idgame, equipoa=resa, equipob=resb, resultado=1)
temporal.save()
pag = int(pagina)
pag = pag-1
lista = Juego.objects.order_by('-fecha')[pag*4:pag*4+4]
template_name = 'juegos_semana.html'
return render_to_response(template_name,{'lista':lista})
def juegosap(request, pagina="1"):
pag = int(pagina)
pag = pag-1
lista = Juego.objects.order_by('-fecha', '-id')[pag*4:pag*4+4]
template_name = 'juegos_semana.html'
return render_to_response(template_name,{'lista':lista})
No tengo idea de que esta pasando pero cuando hago la llamada a
juegos/1/2/3/16
está entrando siempre por juegosap y no a juegosguardar. Tienes alguna idea de como manejar bien la nomenclatura URL pues creo que ahí esta mi error
Por ach4m0 el 10 de Febrero de 2013
Por Andres el 11 de Febrero de 2013
resul = TipoResultado.objects.get(id=1)
temporal = Prediccion(equipoa=int(resa), equipob=int(resb))
temporal.idjuego = game
temporal.resultado = resul
temporal.save()
Sin embargo con resul no me genera problemas pero con juego me dice
Cannot assign "<Juego: Juego object>": "Prediccion.idjuego" must be a "Juego" instance
Por Outlandermx el 13 de Agosto de 2013
Mira tengo esto en mi settings (desde luego la base de datos esta creada en mi servidor mysql)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django',
'USER': 'root',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': '', # Set to empty string for default.
}
}
Y quiero serciorarme de que esta funcionando mi conexion, haciendo lo que dices "python manage.py shell"
y me arroja este error:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf3 in position 38: ordinal not in range(128)
Lo misterioso biene aqui:
1.- a penas quito la configuracion de DB que tengo y funciona perfecto, entro al shell.
2.- vuelvo a poner la configuracion y guardo como utf-8 desde file->save with encoding (uso sublime text 2) y nada mismo error.
Estoy que me vuelvo loco.
Gracias de antemano y saludos.
Por KevinS el 28 de Abril de 2014
Por Rigoberto el 02 de Marzo de 2016