Tutorial de Django
Django es un web framework
para Python de alto nivel que fomenta el desarrollo rápido con diseño limpio y pragmático. Creado por desarrolladores experimentados, se ocupa de gran parte de la molestia del desarrollo web, por lo que se puede concentrarse en escribir su aplicación sin necesidad de reinventar la rueda. Es gratis y de código abierto.
Contenido:
- Instalación
- Crear proyecto
- ORM de Django
- Módulo de administración
- Despliegue en Pythonanywhere
Instalación
Para el pressente tutorial se usará Ubuntu 18.04.
- Crear un
Virtual Enviroment
para el manejo de los paquetes que usaremos en el proyecto.
$ virtualenv -p python3.6 .venv
- Activar
Venv
$ source ./venv/bin/activate
- Instalar Django via pip
$ pip install Django
- Verificar instalación
$ python -m django --version
$ 3.1
Crear proyecto
Iniciemos creando la el código base de nuestro proyecto. En el terminal ejecute lo siguiente:
$ django-admin startproject miEjemplo
Se han creado varios archivos:
miEjemplo/
manage.py
miEjemplo/
__init__.py
settings.py
asgi.py
settings.py
urls.py
wsgi.py
Los archivos son: Existen dos carpetas miEjemplo. La de más afuera, es la que contiene nuestro proyecto, su nombre puede ser cambiado en cualquier momento. La de adentro es la que define nuestro paquete de Python para nuestro proyecto. No debe de cambiar su nombre. Es importante porque podremos usarla para importar cualquier cosa dentro de nuestro proyecto.
- manage.py: Es un archivo utilitario para interactuar con la línea de comandos de varias maneras, aquí puedes ver más detalle.
- miEjemplo/init.py: Este archivo le dice a Python que esta carpeta debe de considerarse como un paquete de Python.
- settings.py: Aquí se aloja las configuraciones para nuestro proyecto. Aquí más detalle.
- urls.py: Aquí se alojan las URLs declaradas para nuestro proyecto de Django, algo así como una tabla de contenido. Aquí más detalle.
- mysite/asgi.py y mysite/wsgi.py: Son archivos que nos permiten servir nuestro proyecto en servidores web. Aquí más detalles sobre ASGI y WSGI.
Levantemos nuestro servicio localmente:
$ python manage.py runserver
Se mostrará el siguiente mensaje:
Watching for file changes with StatReloaderPerforming system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
August 08, 2020 - 03:03:24
Django version 3.1, using settings 'miEjemplo.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Las migraciones a las que hace referencia el mensaje son las migraciones de los modelos que vienen por defecto en Django que tratan sobre el manejo del modulo de adminsitrador, usuarios y otros. Lo veremos más adelante. Si abrimos nuestro navegador y consultamos la URL nos debe salir la siguiente pantalla:
ORM de Django
El ORM (Object-Relational Mapping) es un modelo que consiste en la transformación de las tablas de una base de datos, en una serie de entidades que simplifiquen las tareas básicas de acceso a los datos. Según la documentación oficial, Django ofrece un API para la abstracción de la base de datos que permite crear, recuperar, actualizar y eliminar (CRUD: Create, Retrieve, Update, Delete). A continuación usaremos como ejemplo el ejercicio de la documentación oficial.
Primero creemos nuestra app.
$ python manage.py startapp blog
Así como en el punto de creación de proyecto, aquí también se crearon varios archivos. Por lo pronto nos enfocaremos en models.py
donde colocaremos las entidades que serán parte de nuestra aplicación.
# blog/models.py
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
Como podemos observar en el código, los modelos son definidos mediante la clase Model
. Cada modelo representa una tabla en la base de datos y una instancia de cada clase representa un registro en particular de nuestra tabla. Dentro de cada modelo podremos definir las columnas
o campos
que deseamos en nuestros modelos. Django nos da muchos tipos de campos que podemos usar según lo que necesitemos. Para mayor detalle consulte la documentación oficial.
Ahora que tenemos nuestros modelos definidos, realicemos las migraciones necesarias para que se refleje en la base de datos. Para el presente tutorial trabajaremos con la instancia de sqlite3
que maneja Django por defecto.
Primero registraremos nuestra app en nuestro proyecto, para eso iremos a miEjemplo/settings.py
.
# miEjemplo/settings.py
...
INSTALLED_APPS = [
'blog', # agregamos nuestra app
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
...
Luego ejecutamos lo siguiente:
$ python manage.py makemigrations
Obtendremos la siguiente respuesta:
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Author
- Create model Blog
- Create model Entry
El comando makemigrations
crea en nuestra aplicación archivos de migraciones que definen los que se hará en la base de datos al realizar la migración, veamos el contenido de blog/migrations/0001_initial.py
# blog/migrations/0001_initial.py
# Generated by Django 3.1 on 2020-08-08 17:43
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Author',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254)),
],
),
migrations.CreateModel(
name='Blog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('tagline', models.TextField()),
],
),
migrations.CreateModel(
name='Entry',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('headline', models.CharField(max_length=255)),
('body_text', models.TextField()),
('pub_date', models.DateField()),
('mod_date', models.DateField()),
('number_of_comments', models.IntegerField()),
('number_of_pingbacks', models.IntegerField()),
('rating', models.IntegerField()),
('authors', models.ManyToManyField(to='blog.Author')),
('blog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.blog')),
],
),
]
Como se mencionó, define las accione a ejecutar en la migración. Vemos que también agrega un campo id
que no definimos en nuestro modelo. Esto ocurre porque no definimos ningún campo como primary key
, así que lo agrega automáticamente.
Corramos la migración:
$ python manage.py migrate
Tendremos lo siguiente:
Operations to perform: Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying blog.0001_initial... OK
Applying sessions.0001_initial... OK
Vemos más migraciones que la que creamos antes porque son las que vienen por defecto con Django, que hemos mencionado antes. A continuación vamos a interactuar con el shell
de Django y crear varios registros.
Interacción con Shell y creación de Queries
Para ingresar al shell
en el terminal ejecutamos:
$ python manage.py shell
Tendremos lo siguiente:
Python 3.6.8 (default, Jan 14 2019, 11:02:34)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
Y podremos comenzar a interactuar con la consola.
C: (Create) - Creando objetos
Crearemos nuestro primer blog
>>> from blog.models import Blog
>>> b = Blog(name='Mi primer Blog', tagline='Mi ejemplo de blog')
>>> b.save()
Django en la llamada del método save()
realiza la ejecución de un INSERT en la base de datos. Antes de save()
, no se realiza ninguna operación en la base de datos. Se puede también realizar esta operación con la función create
. Las operaciones que involucran manejo de datos en base, se ejecutan como transactions
, si no está familiazirado con el término, revise la documentación
A continuación usaremos los campos ForeignKey
y ManyToManyField
>>> entry = Entry()
>>> entry.pub_date = '2020-08-08'
>>> entry.mod_date = '2020-08-08'
>>> entry.number_of_comments = 0
>>> entry.number_of_pingbacks = 0
>>> entry.rating = 0
>>> entry.blog = b
>>> entry.save()
En el paso anterior, creamos nuestra entrada, a la que hemos asociado con un blog, usando de esta forma el ForeignKey
de ese modelo. Bastante sencillo, ¿cierto?. Agreguemos varios autores.
>>> from blog.models import Author
>>> au1 = Author.objects.create(name='au1')>>> au2 = Author.objects.create(name='au2')
>>> au3 = Author.objects.create(name='au3')
>>> entry.authors.add(au1, au2, au3)
Django mostrará un mensaje de error si se asigna un objeto de tipo inapropiado.
R: (Retrieve) - Recuperando objetos
Para obtener o recuperar
objetos de nuestra base de datos, usaremos los QuerySets
de nuestros modelos.
Un QuerySet
representa una colección de objetos de nuestra base de datos. Estos pueden ser cero, uno o varios objetos, y pueden ser accedidos aplicando filtros. En términos de SQL, un QuerySet
es una setencia SELECT a la que se le aplican filtros, como lo hariamos en WHERE y podemos definir la cantidad máxima de objetos, como lo hariamos con LIMIT. Veamos algunos ejemplos.
Primero vamos a obtener todos los autores que hemos creado.
>>> Author.objects.all()
<QuerySet [<Author: au1>, <Author: au2>, <Author: au3>]>
Como es lógico asumir, all()
nos retorna todos los autores que están en nuestra base de datos. A continuación usaremos filtros para ser más especificos. Existen dos formas de especificar los objetos que deseamos:
filter(**kwargs)
: Retorna un nuevoQuerySet
que contiene los objetos que corresponden a los parámetros establecidos.exclude(**kwargs)
: Retorna un nuevoQuerySet
que NO corresponden a los establecidos.
Veamos algunos ejemplos:
>>> Entry.objects.filter(pub_date__year=2020)
<QuerySet [<Entry: >]
>>>> Entry.objects.filter(pub_date__year=2019)
<QuerySet []>
En este caso, usando filter
, en el primero pude obtener solo los Entry
tienen como año en pub_date
el 2020. Como solo tengo un Entry
creado y es del 2020, el siguiente que buscaba los publicados en 2019, no tiene objetos.
También es posible encadenar filtros de la siguiente manera:
>>> Entry.objects.filter(
... headline__startswith='Encabezado'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime.date(2005, 1, 30)
... )
Como se puede observar en los dos ejemplos, se emplean atributos de los campos de nuestros modelos con la notación field__lookuptype=value
. Esto se denomina Field Lookups
, que es una forma de especificar con mejor detalle una condición para nuestro filtro.
Por ejemplo:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
El equivalente en SQL seria el siguiente:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
Para mejor detalle sobre los Field Lookups
revise la documentación oficial
U: (Update) - Actualizando objetos
Si uno desea colocar un valor para varios registros, podemos hacer uso de la función update
a un QuerySet
de la siguiente manera:
# Actualizar los headlines de los entry que sean del 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
Este caso es bastante sencillo, el query ciertamente es más complejo según lo hace el filtro.
D: (Delete) - Eliminando objetos
Eliminar también es bastante sencillo. Podemos eliminar mediante una instancia, de la siguiente manera:
# e es un Entry
>>> e.delete()
Podemos eliminar varios registros, usando QuerySet
:
>>> Entry.objects.filter(pub_date__year=2005).delete()
También, al eliminar objetos que tienen asociados por ForeignKey
, estos también eliminarán a los que esten asociados a este, de la siguiente manera:
b = Blog.objects.get(pk=1)
# Se eliminará el Blog y los Entrys asociados.
b.delete()
Módulo de administración
Uno de los aspectos más poderosos de Django es su interfaz de administrador que viene por defecto. Este módulo se basa en los modelos de nuestras aplicaciones y provee de una interfaz rápida y bien proporcionada para el manejo del contenido dentro de nuestro proyecto. Se recomienda que este sea de uso limitado como herramienta interna de la organización y no para usuarios finales. A continuación veremos como colocar nuestros modelos en este y la personalización.
Primero entremos a la interfaz de administración. Creemos un super usuario desde la consola
$ python manage.py createsuperuser
Username (leave blank to use 'gcmurillo'): admin
Email address: Password:
Password (again):
Superuser created successfully.
En localhost:8000/admin/
podremos acceder al administrador.
Al ingresar, no visualizamos los modelos de nuestra aplicación Blog
, sino solamente los que viene por defecto con Django para el manejo de usuarios.
Vamos a colocar nuestro modelo Author
en el nuestra interfaz de administración. Para eso modificaremos el archivo admin.py
de blog
# blog/admin.py
from django.contrib import admin
from blog.models import Author
class AuthorAdmin(admin.ModelAdmin):
pass
admin.site.register(Author, AuthorAdmin)
Revisemos nuestra interfaz.