Framework: Django#

Logo Django

Documentación básica para trabajar con Django 3

Configuraciones#

Creación del proyecto#

Crear entorno virtual y seguir estos pasos:

  • Instalar Django: pip install django

  • crear un nuevo proyecto: django-admin startproject nombre_proyecto

  • Arrancar servidor: python manage.py runserver

  • En el navegador: localhost:8000 para ver si arrancó la aplicación.

Configuración del proyecto (settings.py)#

Acceder desde la raiz del proyecto a la carpeta con el mismo nombre de este. Editar: settings.py:

  • debug: True por defecto, mostrará errores en el navegador. False para evitar mostrarlos en producción.

  • ALLOWED_HOSTS: Un listado en blanco en el cual podemos escribir los dominios permitidos con los que mostrar la app.

  • INSTALLED_APPS: Listado donde se añaden las apps que se irán creando.

  • DATABASES: Diccionario con las bases de datos que se van a usar, por defecto una SQLite.

  • LANGUAGE_CODE: cambiar valor por es para tener el proyecto en español.

  • TIME_ZONE: Para zona horaria España, cambiar a Europe/Madrid

  • STATIC_URL: Url donde se guardarán los archivos estáticos.

  • MEDIA_URL: Url donde se guardan los archivos multimedia (no existe por defecto).

Arrancar la base de datos#

Trabajar con SQLite#

  • Viene preparada por defecto:

1DATABASES = {
2    'default': {
3        'ENGINE': 'django.db.backends.sqlite3',
4        'NAME': BASE_DIR / 'db.sqlite3',
5    }
6}
  • Crear el CRUD inicial de Usuarios: python manage.py migrate

Trabajar con MySQL#

  • La configuración sería la siguiente:

 1DATABASES = {
 2    'default': {
 3        'ENGINE': 'django.db.backends.mysql',
 4        'NAME': 'nombre_database',
 5        'USER': 'usuario_database',
 6        'PASSWORD': 'clave_database',
 7        'HOST': 'localhost',
 8        'PORT': '3306',
 9    }
10}
  • Del mismo modo se ejecuta la primera migración: python manage.py migrate

Crear un Administrador#

  • Una vez ejecutada la primera migración se genera un usuario ejecutando en terminal: python manage.py createsuperuser

Crear una aplicación#

Django es un Framework modular, lo que quiere decir que iremos creando aplicaciones en el para gestionar distintas páginas y así poder reutilizar código. * Crear app: python manage.py startapp nombre_de_tu_app, esto nos genera una carpeta con los archivos esenciales para una app (views.py, models.py…) * Añadir a la lista de apps en settings.py:

1INSTALLED_APPS = [
2    'django.contrib.admin',
3    'django.contrib.auth',
4    'django.contrib.contenttypes',
5    'django.contrib.sessions',
6    'django.contrib.messages',
7    'django.contrib.staticfiles',
8    'nombre_de_tu_app' # Declaramos nuestra app en esta lista
9]

Rutas#

Rutas principales (prueba/urls.py)#

El archivo de rutas principal de Django se encuentra en la carpeta cuyo nombre es el del proyecto y se llama urls.py.

urls.py principal:

1# las dos primeras líneas importan el panel de administración y la librería path para agregar rutas
2from django.contrib import admin
3from django.urls import path
4from nombre_de_tu_app import views # Este es el archivo de vista que importamos de la app creada anteriormente
5
6urlpatterns = [
7    path('admin/', admin.site.urls),
8    path('home/', views.home, name = 'home'), # Definimos que '' (ruta raiz) apunte a la vista **home** y tenga el name 'home' para luego usar un template tag de rutas.
9]

Nota

Si en lugar de home/ definimos un string vacio esta vista se establecerá como la vista principal en cada aplicación

Nota

La primera ruta que se observa lleva hacia el panel de administración que viene ya creado de serie en Django

Atención

En los casos que se arranca el servidor y este da un fallo suele ser por dos razones, la primera que no se haya importado la vista correspondiente y la segunda que no se haga creado la función de vista en su archivo views.py

Rutas en App (nombre_app/urls.py)#

Es posible generar otros archivos de rutas urls.py dentro de cada aplicación para gestionar sus rutas internas.

  • En el archivo de rutas principal (prueba/urls.py):

1from django.contrib import admin
2from django.urls import path, include # Cargamos la librería include
3
4urlpatterns = [
5    path('admin/', admin.site.urls),
6    # importamos el archivo urls de nuestra app:
7    path('', include('nombre_de_tu_app.urls'))
8]
  • Se crea el archivo en la ruta de la app correspondiente: (nombre_de_tu_app/urls.py):

1from django.urls import path
2from . import views as nombre_de_tu_app
3
4urlpatterns = [
5    path('', nombre_de_tu_app.home, name='home'),
6    path('sobremi/', nombre_de_tu_app.about, name='sobremi'),
7]

En este archivo se gestionarán las rutas hacia las vistas de esta app en concreto.

rutas con parámetros#

  • Las rutas con parámetros:

1from django.urls import path
2from . import views as nombre_de_tu_app
3
4urlpatterns = [
5    path('', nombre_de_tu_app.home, name='home'),
6    # despues del slash pasamos entre símbolos menor y mayor que el tipo de variable y el parámetro. si no lleva nada lo reconoce como cadena
7    path('sobremi/<int:id_entrada>', nombre_de_tu_app.about, name='sobremi'),
8]
1from django.shortcuts import render
2from .models import Prueba
3
4# la función recibe por parámetros la id de la entrada:
5def about(request, id_entrada):
6    # este parámetro lo podemos usar por ejemplo para encontrar una entrada ya que django por defecto les asigna un id
7    entrada = Prueba.objects.find(id=id_entrada)
8    return render(request, 'nombre_de_tu_app/about.html', {'entrada':entrada})

Rutas de CBV#

Si trabajamos con Vistas Basadas en Clases (CBV) las rutas son distintas:

1from django.urls import path
2# Importamos las vistas:
3from .views import HomePageView
4
5urlpatterns = [
6    # Devolvemos las urls con el metodo as_view para que las muestre como tal:
7    path('', HomePageView.as_view(), name="home"),
8]

Vistas#

Existen dos formas de crear vistas en Django, las FBV (Function-based Views) y las CBV (Class-based Views).

FBV (Function-Based Views)#

Vistas basadas en funciones:

  • Devolver respuesta HTML con HttpResponse:

1from django.shortcuts import HttpResponse # el modulo HttpResponse carga una respuesta HTML directamente sin plantillas.
2
3# Creamos la función que gestionará la vista home definida como raiz en urls.py:
4def home(request):
5    return HttpResponse("<h1>Título de prueba</h1><h2>Subtítulo</h2>") # esta va a retornar una respuesta html

Si nos vamos al navegador y ejecutamos la raiz veremos que el mensaje de bienvenida cambió por este último.

  • Devolver una plantilla HTML con Render:

1# importamos render que suele venir importado por defecto:
2from django.shortcuts import render
3
4# creamos una función para gestionar los datos de vista:
5def home(request):
6    # dentro de esta vista retornamos render y le pasamos por el segundo parámetro la plantilla que vamos a usar:
7    return render(request, 'nombre_de_tu_app/home.html')

Atención

Es probable tener un error Template does not exist, se debe a que se ha creado aun el template, o que no se ha añadido la app a INSTALLED_APPS o simplemente requiere reiniciar el servidor para que funcione.

CBV (Class-Based Views)#

Vistas basadas en Clases, existen varias:

TemplateView#

Clase de vista estandar, se utiliza comunmente para renderizar templates:

1from django.shortcuts import render
2# Importamos la librería templateview:
3from django.views.generic.base import TemplateView
4
5# Utilizamos las de tipo templateview para devolver un template:
6class HomePageView(TemplateView):
7    template_name = 'nombre_de_tu_app/home.html'

ListView#

Con ListView podemos devolver una tabla de la base de datos de forma sencilla:

1from django.shortcuts import render
2# Importamos el listview y la base de datos:
3from django.views.generic.list import ListView
4from .models import Page
5
6# Ahora creamos la clase de tipo ListView:
7class PageListView(ListView):
8    model = Page # Gestionará el modelo page
9    paginate_by = 3 # así de sencillo se paginan resultados.

De esta forma tenemos un listado en el template listo para recorrer usando el bucle sobre el valor object_list {% for pagina in object_list %}

Atención

Para que funcione esta vista y encuentre su template por defecto sería page_list.html y la colocamos dentro de la carpeta templates/nombre_de_tu_app/

DetailView#

Con la vista detalle recuperamos un elemento de la base de datos para visualizarlo, veamos views.py:

1# Importamos el detailview:
2from django.views.generic.detail import DetailView
3from .models import Page
4
5
6# Ahora vamos a integrar la clase de pagina simple con el detailview:
7class PageDetailView(DetailView):
8    model = Page # cargamos el modelo Page

En la ruta deberemos asignar el parámetro <int:pk> para poder recibir el id del elemento.

Atención

Debemos crear el template dentro de templates/nombre_de_tu_app/ con el nombre page_detail.html, ahora solo falta imprimir los datos usando el template tag {{page}}

CreateView#

Como su nombre indica, es la vista para crear elementos, vamos a probarla en views.py:

 1# Importamos CreateView:
 2from django.views.generic.edit import CreateView
 3# e importamos la librería para hacer redirecciones:
 4from django.urls import reverse_lazy
 5
 6from .models import Page
 7
 8# Y creamos la vista con CreateView para crear registros:
 9class PageCreate(CreateView):
10    model = Page # Cargamos el modelo.
11    fields = ['title', 'content', 'order'] # Y ahora añadimos los campos que vamos a permitir que se puedan crear
12    # Opcionalmente hacemos un reverse_lazy que retorna a la página que le indicamos:
13    success_url = reverse_lazy('pages:pages')

Con esto solo nos falta el template llamado page_create.html y utilizar un formulario que suba dichos campos.

UpdateView#

Esta vista sirve para actualizar registros, hay que pasarle un pk para poder editar la página correcta.

  • Editamos views.py:

 1# Importamos el update:
 2from django.views.generic.edit import UpdateView
 3from django.urls import reverse_lazy
 4from .models import Page
 5
 6# Ahora creamos la vista update:
 7class PageUpdate(UpdateView):
 8    model = Page
 9    fields = ['title', 'content', 'order']
10    # Ahora le pasamos el sufijo que tendrá la página (page_update_form.html):
11    template_name_suffix = '_update_form'
12
13    # Ahora vamos a retornar al formulario una vez terminada la edición esta vez necesariamente con un método específico de django:
14    def get_success_url(self): # Le pasamos por argumenoto la id:
15    return reverse_lazy('pages:update', args = [self.object.id]) + '?ok' # Le pasamos por parámetros un valor ok para verificarlo en el template

De este modo solo nos falta el archivo page_update.html y en la ruta pasarle un parámetro con el nombre <int:pk>, en el template ponemos un formulario tal cual como en CreateView.

DeleteView#

Sirve para borrar entradas, funciona de un modo similar a UpdateView, veamos views.py:

 1from django.views.generic.edit import DeleteView
 2from django.urls import reverse_lazy
 3from django.shortcuts import render
 4
 5from .models import Page
 6
 7# Creamos la vista delete:
 8class PageDelete(DeleteView):
 9    model = Page
10    success_url = reverse_lazy('pages:pages')

Con esto le pasamos a la ruta un parámetro tipo <int:pk> y crear el template DeleteView.as_view()

Pasarle un formulario a una CBV#

Para pasarle un formulario a un CBV hacemos lo siguiente en views.py:

 1from django.views.generic.edit import CreateView
 2from django.urls import reverse_lazy
 3
 4from django.shortcuts import render
 5from .models import Page
 6# Importamos el formulario de forms:
 7from .forms import PageForm
 8
 9class PageCreate(CreateView):
10    model = Page
11    form_class = PageForm # Asignamos el formulario que vamos a utilizar
12    success_url = reverse_lazy('pages:pages')

Crear un nuevo contexto#

Este concepto se resume en la manera de exportar datos desde las vistas CBV al Template y este sería el modo:

 1from django.shortcuts import render
 2from django.views.generic.base import TemplateView
 3
 4class HomePageView(TemplateView):
 5    template_name = 'core/home.html'
 6    # Podemos pasarle valores a la vista a través de un diccionario de contexto con un método específico:
 7    def get_context_data(self, **kwargs):
 8        # Cargamos del padre la estructura del diccionario:
 9        context = super().get_context_data(**kwargs)
10        # Y ahora podemos grabar por ejemplo un título:
11        context['title'] = 'Título de mi web'
12        # La devolvemos al Template para que pueda usarlo:
13        return context

Imagina ahora que queremos usar ese contexto en un título del template, pues escribimos <h1>{{titulo}}</h1> y listo.

Recuperar datos de POST o GET#

Para recuperar datos desde GET o POST utilizamos la función con su nombre que viene ya preparada en la clase superior:

 1from .models import Prueba
 2from django.views.generic import TemplateView
 3from .forms import ContactoForm
 4
 5class RegistroView(TemplateView):
 6    template_name = 'nombre_de_tu_app/index.html'
 7
 8    # Se utiliza la función predefinida llamada post o get con los parámetros que vemos:
 9    def post(self, request, *args, **kwargs):
10        # guardamos el formulario en una variable con los datos rellenos:
11        form = self.form_class(request.POST)
12        # comprobamos que sea válido:
13        if form.is_valid():
14            # preparamos los datos para guardar:
15            registro = form.save(commit=False)
16            # podemos editar algun dato por el camino:
17            registro.fecha_creacion(datetime.now)
18            # y guardamos el registro en el modelo:
19            registro.save()
20
21            # regresamos a la página de vuelta:
22            return redirect(reverse('home'))
23        else:
24            form = ContactoForm()

De este modo una vez recibe datos los almacena en el modelo.

Decoradores#

Los decoradores sirven para hacer modificaciones en las vistas, como por ejemplo definir si una url la puede ver solo usuarios registrados o si es del staff:

  • Decoradores en CBV:

 1# Se le pasa el decorador a la clase directamente:
 2@method_decorator(login_required, name='dispatch')
 3class ProfileUpdate(TemplateView):
 4    template_name = 'registration/profile_form.html'
 5
 6# podemos definir si es un usuario registrado o si solo puede acceder el staff
 7@method_decorator(staff_member_required, name='dispatch') # Para que el decorador de metodos sepa cual es el que tiene que decorar lo asignamos con un parámetro name
 8class PageCreate(CreateView):
 9    model = Page
10    form_class = PageForm
11    success_url = reverse_lazy('pages:pages')
  • Añadimos lo siguiente al final de settings.py para definir hacia donde irá para inciar sesión:

1# Este es el path al que queremos que redireccione:
2LOGIN_REDIRECT_URL = 'pages:pages'
3LOGOUT_REDIRECT_URL = 'home'

Middleware en Django#

Los Middlewares se utilizan para controlar como se procesa la información, estos middlewares se ejecutan de forma global antes o despues de cada petición. Por defecto django cuenta con al menos cuatro middlewares: SecurityMiddleware, SessionMiddleware, CommonMiddleware y AuthenticationMiddleware. Los middlewares se declaran en settings.py en la constante MIDDLEWARE

  • SecurityMiddleware: Intenta garantizar la seguridad en la aplicación evitanto ataques XSS.

  • SessionMiddleware: Accede a la cookie de sesión y define donde se guardan las sesiones.

  • CommomMiddleware: Se encarga de normalizar las direcciones añadiendo www o / al final.

  • CsrfViewMiddleware: Sirve para garantizar que se cumple el CSRF_TOKEN.

  • AuthenticationMiddleware: Sirve para conseguir el usuario de la petición y añadirlo al request.

  • MessageMiddleware: Especialmente usado para mensajes flash, aunque ya se delegan sobre todo al Frontend.

  • XFrameOptionMiddleware: Evita errores de X-Content o Iframes y ataques click-jacking

Crear Custom Middleware#

  1. Dentro de una App se crea un archivo middleware.py (cada app tiene su propio archivo o también se puede sacar en la raiz):

  2. Este sería el comportamiento base de un middleware:

 1# Se importa el modulo que llama a las peticiones:
 2from typing import Callable
 3
 4# Se crea una clase para el middleware:
 5class NombreMiddleware:
 6    # en el constructor recibirá la respuesta con Callable:
 7    def __init__(self, get_response: Callable):
 8        # guardamos en el atributo de clase la petición:
 9        self.get_response = get_response
10
11
12    # se genera el metodo de llamada:
13    def __call__(self, request):
14        # guardamos la respuesta de la petición:
15        response = self.get_response(request)
16
17
18        return response
  1. Activar middleware en settings.py:

 1MIDDLEWARE = [
 2    'django.middleware.security.SecurityMiddleware',
 3    'django.contrib.sessions.middleware.SessionMiddleware',
 4    'django.middleware.common.CommonMiddleware',
 5    'django.middleware.csrf.CsrfViewMiddleware',
 6    'django.contrib.auth.middleware.AuthenticationMiddleware',
 7    'django.contrib.messages.middleware.MessageMiddleware',
 8    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 9    'core.middleware.NombreMiddleware' # añadir el nuevo middleware creado
10]
  1. Ahora se va a crear un custom middleware que filtre direcciones IP:

 1# instalamos para el ejemplo django-ipware:
 2from ipware import get_client_ip
 3from django.http import HttpResponse
 4
 5# se asigna el nombre del middleware:
 6class IpValidMiddleware:
 7    def __init__(self, get_response):
 8        self.get_response = get_response
 9        # Creamos la lista negra de ips:
10        self.black_list = ['127.0.0.1']
11
12
13    def __call__(self, request):
14        response = self.get_response(request)
15
16        # recuperamos la ip de la request en dos variables (obligatorio aunque se use solo una):
17        ip, is_routable = get_client_ip(request)
18
19        # comprobamos si esta en lista negra y cambiamos la response en caso de que sea así:
20        if ip in self.black_list:
21            # tiramos de error 404:
22            response = HttpResponse('Bad request', status=404)
23
24        return response

Templates#

Las plantillas son las que muestran el sitio web mediante etiquetas HTML y también imprimen resultados que gestiona el servidor con Template Tags.

  • Para comenzar a utilizar templates creamos una carpeta llamada templates en el interior de la carpeta de nuestra app y dentro de templates otro directorio con el nombre de la app. (nombre_de_tu_app/templates/nombre_de_tu_app)

  • Ahora creamos un archivo html por ejemplo home.html que cargará la página de inicio:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Página de prueba</title>
</head>
<body>
    <h1>Bienvenido a mi página de prueba</h1>
    <h2>Aquí haremos pruebas varias</h2>
</body>
</html>

Atención

Para que funcione debemos tener listo el render que devuelve este archivo html y al abrir el navegador se mostrará correctamente.

Template Tags#

Los Template Tags son un tipo de etiquetas especiales en Django que se utilizan en las plantillas para ejecutar respuestas backend.

Estas etiquetas suelen tener dos tipos de estructuras: {% instrucción %} o {{ datos }} según el tipo de tarea que vayamos a ejecutar.

Template tag base#

Una buena práctica para no repetir código en plantillas es coger todo el contenido común y almacenarlo en una plantilla base:

  • Entramos en la carpeta nombre_de_tu_app/templates/nombre_de_tu_app y creamos un archivo llamado base.html donde copiaremos el contenido común:

  • Ahora vamos a quitar el código de home.html y lo pegamos en base.html:

 1<!DOCTYPE html>
 2<html lang="es">
 3<head>
 4    <meta charset="UTF-8">
 5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6    <title>Página de prueba</title>
 7</head>
 8<body>
 9    <h1>Bienvenido a mi página de prueba</h1>
10    <h2>Aquí haremos pruebas varias</h2>
11
12    <!-- Justo aquí enmedio utilizaremos el template tag base para extender una parte de otra plantilla  -->
13    {% block cuerpo %}{% endblock %}
14
15    <footer>Piptocode, hecho con cariño y para amantes de la programación</footer>
16</body>
17</html>
  • Finalmente vamos a usar home.html como una plantilla de extensión con su propio código:

1<!-- llamamos el template tag con extends: -->
2{% extends 'nombre_de_tu_app/base.html' %}
3
4<!-- Utilizamos el block content para definir donde irá el contenido de la pagina home respecto a la plantilla base -->
5{% block cuerpo %}
6    <h2>Portada</h2>
7    <p>Esta es la página principal del sitio y utiliza una plantilla base para el contenido estático</p>
8{% endblock %}

Siguiendo este patrón podemos reutilizar el código base de la web en nuevas páginas o incluso nuevas apps de Django.

Nota

En relación a extends podemos cargar cualquier plantilla a modo de fragmento, por ejemplo para ordenar css inline, un sidebar u otros elementos dinámicos que hagan falta en el día a día. El objetivo es reducir el número de lineas HTML aumentando la cantidad de plantillas identificables y reutilizables.

Template tag url#

Con este template tag podemos establecer vínculos a otras páginas enlazando los names del archivo de rutas.

¿recuerdas las líneas que escribimos dentro de urls.py? path('', views.home, name = 'home'),, el path recibe tres valores, la ruta del navegador, la ubicación de la vista y por último el nombre de la ruta, este tercer valor es el que utilizamos con el template tag url

  • vamos a editar el archivo base.html para añadir un menú de navegación:

 1<!DOCTYPE html>
 2<html lang="es">
 3<head>
 4    <meta charset="UTF-8">
 5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6    <title>Página de prueba</title>
 7</head>
 8<body>
 9    <h1>Bienvenido a mi página de prueba</h1>
10    <h2>Aquí haremos pruebas varias</h2>
11
12    <nav>
13        <!-- el template tag url lo usamos dentro del atribut href de un hipervínculo: -->
14        <a href="{% url 'home' %}">Índice</a> <!-- lleva entre comillas simples el nombre de la ruta que vamos a vincular -->
15        <a href="">Sobre mí</a>
16        <a href="">Contacto</a>
17    </nav>
18
19    {% block cuerpo %}{% endblock %}
20
21    <footer>Piptocode, hecho con cariño y para amantes de la programación</footer>
22</body>
23</html>

Atención

Si añadimos un name que no existe en el archivo de rutas Django lanzará una pantalla de error en lugar de la plantilla.

Template tag static#

Con este template tag vamos a cargar archivos estáticos de nuestra web, entre ellos están las imágenes, videos, hojas de estilo y javascript.

  • Siguiendo una práctica convencional creamos una carpeta llamada static dentro del directorio de la app y dentro de static una carpeta con el nombre de la app: nombre_de_tu_app/static/nombre_de_tu_app.

  • Dentro de la última carpeta podemos ir añadiendo carpetas básica como css, js e img para ir añadiendo los archivos correspondientes.

  • Ahora podemos utilizar archivos estáticos dentro de dichas rutas:

 1<!-- cargamos el template tag static -->
 2{% load static %}
 3
 4<!DOCTYPE html>
 5<html lang="es">
 6<head>
 7    <meta charset="UTF-8">
 8    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 9    <title>Página de prueba</title>
10    <!-- ahora si queremos cargar un archivo estatico como una hoja de estilo lo hacemos así: -->
11    <link rel="stylesheet" href="{% static 'nombre_de_tu_app/css/estilos.css' %}">
12</head>
13<body>
14    <h1>Bienvenido a mi página de prueba</h1>
15    <h2>Aquí haremos pruebas varias</h2>
16
17    <nav>
18        <a href="{% url 'home' %}">Índice</a>
19        <a href="">Sobre mí</a>
20        <a href="">Contacto</a>
21    </nav>
22
23    {% block cuerpo %}{% endblock %}
24
25    <footer>Piptocode, hecho con cariño y para amantes de la programación</footer>
26</body>
27</html>

Template tag de datos#

Los template tags de datos muestran información que enviamos desde la vista al template.

  • Si nos vamos a views.py para añadir un dato:

1from django.shortcuts import render
2
3def home(request):
4    # creamos una variable:
5    nombre = "Guillermo Granados Gómez"
6    return render(request, 'nombre_de_tu_app/home.html', {'nombre':nombre}) # devolvemos la información en un diccionario
  • Ahora que tenemos un dato, podemos mostrarlo en cualquier template de nuestra app:

 1<!DOCTYPE html>
 2<html lang="es">
 3<head>
 4    <meta charset="UTF-8">
 5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6    <title>Página de prueba</title>
 7</head>
 8<body>     <!-- Ahora podemos mostrar el dato usando su clave -->
 9    <h1>Bienvenido a la web de {{ nombre }}</h1>
10    <h2>Aquí haremos pruebas varias</h2>
11
12    <nav>
13        <a href="{% url 'home' %}">Índice</a>
14        <a href="">Sobre mí</a>
15        <a href="">Contacto</a>
16    </nav>
17
18    {% block cuerpo %}{% endblock %}
19
20    <footer>Piptocode, hecho con cariño y para amantes de la programación</footer>
21</body>
22</html>

Template tag for#

En los templates de Django para hacer un bucle for lo hacemos del siguiente modo:

  • Para empezar necesitamos un diccionario al que acceder desde views.py:

 1from django.shortcuts import render
 2
 3def home(request):
 4    # creamos un diccionario:
 5    personas = [
 6        {'nombre': 'Pepe', 'edad': 26},
 7        {'nombre': 'Antonio', 'edad': 38},
 8        {'nombre': 'María', 'edad': 37}
 9    ]
10    return render(request, 'pruebauno/home.html', {'personas':personas}) # devolvemos la información en un diccionario
  • Y ahora podemos recorrer el diccionario en nuestro template con el template tag for:

1<h3>Listado de Clientes</h3>
2<ul>
3    {% for persona in personas %} <!-- Abrimos el bucle for en el template -->
4        <li>Nombre: {{ persona.nombre }}, Edad: {{ persona.edad }}</li> <!-- Creamos el elemento que va a iterar en la lista imprimiendo los valores -->
5    {% endfor %} <!-- Y lleva una llave de cierre -->
6</ul>

Template tag if#

Con el template tag if podemos establecer condiciones dentro de los templates, retomando el ejemplo de for vamos a pintar de verde uno de los registros:

1<h3>Listado de Clientes</h3>
2<ul>
3    {% for persona in personas %}
4        <!-- Si en nombre se encuentra Antonio lo pintaremos de verde: -->
5        <li {% if 'Antonio' in persona.nombre  %} style="color: green" {% endif %}>
6            Nombre: {{ persona.nombre }}, Edad: {{ persona.edad }}
7        </li>
8    {% endfor %}
9</ul>

Crear template tags customizados#

Son muy útiles para cargar etiquetas con contenido específico que se pueda usar en plantillas.

Como preparar el archivo de templatetags: 1. Dentro de una app como core crear carpeta llamada templatetags. 2. Dentro de templatetags se crea un archivo con una colección de tags components.py:

Simple tag#

Devuelve información pura como números, cadenas, listados, objetos, diccionarios.

 1# se importa la librería template:
 2from django import template
 3
 4# se registra la librería:
 5register = template.Library()
 6
 7# y se van creando cada una de las tags:
 8@register.simple_tag
 9def get_title():
10    return 'bienvenidos a mi página'
  • Cogemos un template y lo aplicamos:

 1<!-- Se carga el módulo con los template tags: -->
 2{% load components %}
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="UTF-8">
 7    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 8    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 9    <title>Document</title>
10</head>
11<body>
12    <!-- hay que crear una variable a partir del simple_tag: -->
13    {% get_title as title %}
14    <!-- Utilizar la variable: -->
15    <h1>{{ title }} </h1>
16    <form method="POST">
17        {{ form.as_p }}
18        {% csrf_token %}
19        <button type="submit">Iniciar sesión</button>
20    </form>
21    <hr />
22    <a href="{% url 'register' %}">Crear usuario</a>
23</body>
24</html>

Filter tags#

Los filter tags se utilizan para filtrar datos en otros tags, y pueden ser simples o compuestos:

  • Se reutiliza el archivo components.py (aunque se puede crear uno nuevo):

 1# se puede registrar un filtro sencillo:
 2@register.filter
 3def upper(value):
 4    return value.upper()
 5
 6# o un filtro con hasta dos parámetros:
 7@register.filter
 8def change_language(value, arg):
 9
10    if arg == 'english':
11        value = 'Welcome to my site'
12    if arg == 'français':
13        value = 'bienvenu à mon site'
14
15    return value
  • Estos filtros son utilizados en los templates así:

 1{% load components %}
 2<!DOCTYPE html>
 3<html lang="en">
 4<head>
 5    <meta charset="UTF-8">
 6    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 8    <title>Document</title>
 9</head>
10<body>
11    {% get_title as title %}
12    <!-- Utilizar un filtro simple: -->
13    <h1>{{ title|upper }} </h1>
14    <!-- Utilizar filtros compuestos: -->
15    <h2>{{ title|change_language:'english' }} </h2>
16    <h2>{{ title|change_language:'français' }} </h2>
17    <form method="POST">
18        {{ form.as_p }}
19        {% csrf_token %}
20        <button type="submit">Iniciar sesión</button>
21    </form>
22    <hr />
23    <a href="{% url 'register' %}">Crear usuario</a>
24</body>
25</html>

Modelos#

Los modelos en Django sirven para crear estructuras de bases de datos con las que podremos interactuar gracias a sus QuerySets.

En cada app que creamos tenemos un archivo models.py, vamos a editar uno para ver que campos tiene:

 1# Los modelos se crean usando una clase que hereda de la superclase Model:
 2class Prueba(models.Model):
 3    autor = models.ForeignKey(User, verbose_name = "Autor", on_delete = models.CASCADE) # El primero es una clave foranea para vincular otras tablas como la de usuarios que viene por defecto
 4    titulo = models.CharField(max_length=200, verbose_name="Título") # CharField es un campo de tipo texto, el primer parámetro que le pasamos define el tamaño máximo y es obligatorio, el segundo es opcional y sirve para todos los campos (verbose_name define como se mostrará la label del panel de administración)
 5    descripcion = models.TextField(verbose_name="Descripción") # Con TextField tenemos una caja de texto sin límite de rango.
 6    link = models.URLField(null=True, blank=True, verbose_name="Enlace") # URLField nos permite agregar una url válida.
 7    fecha_creacion = models.DateTimeField(auto_now_add = True) # crea un campo de fecha y hora, podemos pasarle la fecha de una publicación de forma automática con auto_now_add.
 8    fecha_edicion = models.DateField(auto_now = True) # aquí tenemos otra variante, en primer lugar DateField guarda solo la fecha y opcionalmente podemos decir que lo haga cuando editamos la entrada con auto_now.
 9
10
11    # opcionalmente podemos usar la clase Meta para editar valores que nos servirán para mostrar los datos en el panel de administración:
12    class Meta:
13        verbose_name = "prueba" # Nombre de la tabla en el panel.
14        verbose_name_plural = "pruebas" # nombre en plural.
15        ordering = ["-fecha_creacion"] # Orden prioritario, en este caso por fecha descenciente.
16
17    # Con esta función podemos retornar en el panel de administración un valor de referencia
18    def __str__(self):
19        return self.titulo

Atención

Tienes que tener registrada tu app en el apartado INSTALLED_APPS o sino dará error a la hora de migrar la base de datos.

Consejo

Los parámetros comunes para prácticamente todos los campos son verbose_name (nombre que muestra), blank (True o False para permitir el campo vacío), null (True o False para permitir campo nulo)

Cargar imágenes en modelos con Pillow#

Pillow es una librería de Python que se utiliza para el tratamiento de imágenes. En Django la podemos utilizar para gestionar la carga de estas.

  • Lo primero que tenemos que hacer es instalar Pillow en nuestro entorno: pip install Pillow

  • Ahora vamos a editar nuestra clase de models.py:

 1class Prueba(models.Model):
 2    autor = models.ForeignKey(User, verbose_name = "Autor", on_delete = models.CASCADE)
 3    titulo = models.CharField(max_length=200, verbose_name="Título")
 4    descripcion = models.TextField(verbose_name="Descripción")
 5    fecha_creacion = models.DateTimeField(auto_now_add = True)
 6    fecha_edicion = models.DateField(auto_now = True)
 7    # con ImageField podemos subir una imagen a un directorio que elijamos:
 8    imagen = models.ImageField(upload_to="imagenes/")
 9
10    class Meta:
11        verbose_name = "prueba"
12        verbose_name_plural = "pruebas"
13        ordering = ["-fecha_creacion"]
14
15    def __str__(self):
16        return self.titulo
  • Para poder subir las imágenes tenemos que añadir en settings.py la siguiente línea:

1MEDIA_URL = '/media/'
2MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • Finalmente nos vamos al archivo de rutas principal (el que se encuentra dentro de la carpeta con el nombre de tu proyecto) y añadimos la siguiente configuración para poder visualizar las imágenes desde el panel:

 1from django.contrib import admin
 2from django.urls import path
 3from nombre_de_tu_app import views
 4# Importamos la librería settings:
 5from django.conf import settings
 6
 7urlpatterns = [
 8    path('admin/', admin.site.urls),
 9    path('', views.home, name = 'home'),
10]
11
12# Cargamos la ruta siempre que este en modo debug:
13if settings.DEBUG:
14    from django.conf.urls.static import static
15    urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

De este modo y mientras no estemos en producción podremos visualizar las imágenes desde el panel de administrador para probar que funciona correctamente.

Modelo de muchos a muchos#

En base de datos un modelo de muchos a muchos nos sirve para establecer una relación entre múltiples componentes de ambas tablas, como por ejemplo crear una lista de categorías: * Sería algo así nuestro modelo:

 1# primero creamos un modelo para guardar las categorías:
 2class Category(models.Model):
 3    name = models.CharField(max_length = 100, verbose_name="Nombre")
 4    created = models.DateTimeField(auto_now_add=True, verbose_name="Fecha de creación")
 5    updated = models.DateTimeField(auto_now=True, verbose_name="Fecha de edición")
 6
 7class Meta:
 8    verbose_name = "categoria"
 9    verbose_name_plural = "categorias"
10    ordering = ["-created"]
11
12def __str__(self):
13    return self.name
14
15class Prueba(models.Model):
16    autor = models.ForeignKey(User, verbose_name = "Autor", on_delete = models.CASCADE)
17    titulo = models.CharField(max_length=200, verbose_name="Título")
18    descripcion = models.TextField(verbose_name="Descripción")
19    fecha_creacion = models.DateTimeField(auto_now_add = True)
20    fecha_edicion = models.DateField(auto_now = True)
21    imagen = models.ImageField(upload_to="imagenes/")
22    # Ahora vamos a recuperar todas las categorías en la tabla que queremos usar:
23    categorias = models.ManyToManyField(Category, verbose_name="Categorías")
24
25    class Meta:
26        verbose_name = "prueba"
27        verbose_name_plural = "pruebas"
28        ordering = ["-fecha_creacion"]
29
30    def __str__(self):
31        return self.titulo

Ahora podemos generar categorías incluso desde la tabla de pruebas cuando ingresamos o editamos un registro.

Generar modelo y migrar la base de datos#

Cuando creamos un modelo nuevo lo primero que tenemos que hacer es maquetar la estructura que vamos a migrar cada vez que generemos la base de datos:

  • Para crear el modelo de las tablas de una app ejecutamos python manage.py makemigrations.

  • Si todo va bien, migramos la base de datos con python manage.py migrate

  • Para comprobar el estado de las migraciones se ejecuta: python manage.py showmigrations

ORM de Django y sus QuerySets#

Los QuerySets son listas de objetos que se recuperan de la base de datos de forma similar a una consulta SQL. Existen una serie de sentencias trabajar con estos datos.

  • Lo primero que vamos a hacer es ejecutar python manage.py shell, esto abrirá la consola del ORM.

  • Una vez arrancada lo primero que tenemos que hacer para las pruebas es importar un modelo from nombre_de_tu_app import Prueba

Ahora vamos a conocer los distintos comandos para realizar QuerySets:

  • Prueba.objects.all(): devuelve todos los registros de la tabla Prueba

  • Prueba.objects.create(titulo="Ejemplo", descripcion="esto es una entrada"): Genera un nuevo registro en la tabla Prueba, ten en cuenta que esten todos los campos o sino que puedan estar en blanco (blank=True)

  • Prueba.objects.filter(titulo__contains = 'Ejemplo'): Permite filtrar las tablas para devolver solo aquellos que contienen la palabra clave, si quitamos __contains solo obtendrá los que tengan exactamente y únicamente esa palabra.

  • Prueba.objects.filter(titulo__contains = 'Ejemplo').first(): Añadiendo el metodo first trae solo el primer registro.

  • Prueba.objects.count(): Devuelve el número total de registros que hay en la tabla.

  • Prueba.objects.order_by('fecha_creacion'): Permite ordenar los registros de la tabla nuevamente cuando se cargan en la vista.

  • Prueba.objects.filter(pk=1).update(titulo="Novedad"): Edita un registro ya existente.

  • Prueba.objects.delete(titulo="Ejemplo"): Elimina un valor según el campo que hayamos elegido para buscarlo

Para salir de la consola ORM escribimos exit() y pulsamos intro

Consejo

Podemos encadenar algunos querysets por ejemplo recuperar todos los datos y ordenarlos por fecha: Prueba.objects.all().order_by('-fecha_creacion')

Consejo

Para filtrar por campos de una tabla relacionada se usa doble guión bajo en la clave foranea: Videojuego.objects.filter(Consola__marca:'Sega')

Realizar QuerySets desde vista y pasarlos a template#

Es algo muy común, y es que cuando trabajamos con vistas FBV es el método estandar, para trabajar datos del modelo en la vista lo hacemos del siguiente modo, editamos views.py:

1from django.shortcuts import render
2# Importamos el Modelo:
3from . import Prueba
4
5def home(request):
6    # creamos una variable:
7    cosas = Prueba.objects.all()
8    return render(request, 'nombre_de_tu_app/home.html', {'cosas':cosas}) # pasamos el queryset por una variable y este lo trata en el template como un diccionario.

Formularios#

En Django podemos crear formularios individuales y reutilizables.

Crear un formulario básico#

 1# importamos la librería forms:
 2from django import forms
 3# Esto se importa opcionalmente si usamos fechas:
 4import datetime
 5
 6# Creamos un formulario utilizando una clase que hereda de forms:
 7class ContactoForm(forms.Form):
 8    # Cada campo recibe un tipo de dato con un label que es la etiqueta html y si es requerido:
 9    nombre = forms.CharField(label="Nombre", required=True) # CharField es para campo de texto
10    email = forms.EmailField(label="Correo", required=True) # Email para correos
11    url = forms.URLField(initial='https://', label="Web") # Este sirve para insertar una url y le podemos pasar un valor inicial
12    fecha_nacimiento = forms.DateField(initial=datetime.date.today) # este sirve para añadir una fecha y podemos pasarle la de hoy si importamos 'datetime'
13    contenido = forms.CharField(label="contenido", required=True, widget=forms.Textarea) # con widget le cambiamos el aspecto directamente para que sea un textarea

Crear un formulario usando su modelo#

Este otro método es mas fácil de personalizar a mi parecer, y organiza mejor todo ademas de permitir elegir que campos se mostrarán del modelo de datos, así pues editamos forms.py:

 1from django import forms
 2from .models import Prueba
 3
 4class PruebaForm(forms.ModelForm):
 5    class Meta:
 6        # elegimos el modelo de datos:
 7        model = Prueba
 8        # Elegimos los campos que se mostrarán de dicho modelo:
 9        fields = ['titulo', 'email', 'contenido']
10        # añadimos widgets para configurar el diseño de los campos del formulario:
11        widgets = {                 # podemos pasarle el atributo al input que queramos.
12            'titulo': forms.TextInput(attrs={'class':'formulario'}), # Le asignamos la clase formulario
13            'contenido': forms.Textarea(attrs={'class':'formulario'}),
14            'email': forms.EmailInput(attrs={'class':'formulario'})
15        }
16        # Así se puede esconder opcionalmente las labels o cambiar su texto:
17        labels = {
18            'title':'', 'order':'', 'content':''
19        }
20
21De este modo tenemos otra forma de sacar los formularios, lo demás es todo igual.

Crear formulario y aceptar parámetros para su configuración#

Este tercer método permite recibir parámetros al formulario de modo que podamos configurar ciertas características del mismo:

 1class PublisherForm(forms.ModelForm):
 2
 3# tenemos un campo con su uso más avanzado:
 4name = forms.CharField(
 5    label='Nombre',
 6    max_length=255,
 7    required=False,
 8    widget=forms.TextInput(
 9        attrs={'class': 'form-control'}
10    )
11)
12
13# En el meta rescatamos el Modelo y los campos:
14class Meta:
15    model = Publisher
16    fields = ('name', )
17
18# Y ahora se define un init que recibirá los args y kwargs, estos segundos los usaremos para comprobar que se recibe:
19def __init__(self, *args, **kwargs):
20    # se crean las variables con los argumentos posibles:
21    self.required = kwargs.pop('required', None)
22    self.readonly = kwargs.pop('readonly', None)
23    self.disabled = kwargs.pop('disabled', None)
24    super().__init__(*args, **kwargs)
25
26    # se comprueba si se reciben y se genera el comportamiento de cada uno:
27    if self.required:
28        if self.required == 'all':
29            for x in self.fields:
30                self.fields[x].widget.attrs['required'] = True
31        else:
32            for field in self.required:
33                self.fields[field].widget.attrs['required'] = True
34
35    if self.readonly:
36        if self.readonly == 'all':
37            for x in self.fields:
38                self.fields[x].widget.attrs['readonly'] = True
39        else:
40            for field in self.readonly:
41                self.fields[field].widget.attrs['readonly'] = True
42
43    if self.disabled:
44        if self.disabled == 'all':
45            for x in self.fields:
46                self.fields[x].widget.attrs['disabled'] = True
47        else:
48            for field in self.disabled:
49                self.fields[field].widget.attrs['disabled'] = True
50
51De este modo se tiene un control universal de cada campo.
52
53Si queremos establecer un campo del formulario como requerido:
54* form = PublisherForm(required = ['Nombre'])

Utilizar un formulario#

Si queremos usar un formulario lo importamos a la vista del siguiente modo.

1from django.shortcuts import render
2# importamos el formulario:
3from .forms import ContactoForm
4
5def contacto(request):
6    form = ContactoForm() # cargamos el formulario en una variable
7    return render(request, 'nombre_de_tu_app/contacto.html', {'form': form}) # finalmente lo pasamos al template.
  • Para cargar el formulario en la vista editamos el archivo html y lo añadimos:

<form method="POST">
    {% csrf_token %} <!-- le pasamos el token -->
    {{formulario.as_p}} <!-- pasamos el formulario y lo formateamos en parrafos, si usamos as_table se formateará en tabla -->
    <button type="submit">Enviar mensaje</button><!-- no olvides el botón submit -->
</form>
<!-- por último podemos depurar lo que envía el formulario con el siguiente tag: -->
{{request.POST}}

Nota

Si queremos utilizar los campos sueltos para un mayor control del front se llaman como atributos de un objeto: {{ formulario.nombre }} Esto carga el input, los label y cualquier otra propiedad relacionada con el campo.

Validar un formulario#

El formulario se valida una vez enviado a la vista antes de ser guardado o gestionado por la base de datos, veamoslo en views.py:

 1from django.shortcuts import render
 2from .forms import ContactoForm
 3
 4def contacto(request):
 5    form = ContactoForm()
 6
 7    # comprobamos que hemos recibido una petición post:
 8    if request.method == "POST":
 9        # le pasamos los datos a la plantilla del formulario:
10        form = ContactoForm(data=request.POST)
11
12        # validamos el formulario y si es correcto guardamos los datos en cada campo:
13        if form.is_valid():
14            # preparamos los datos para guardar:
15            registro = form.save(commit=False)
16            # podemos editar algun dato por el camino:
17            registro.fecha_creacion(datetime.now)
18            # y guardamos el registro en el modelo:
19            registro.save()
20
21            # regresamos a la página de vuelta:
22            return redirect(reverse('home'))
23        else:
24            form = ContactoForm()
25
26    # si no se ha recibido ninguna petición post se carga como tal:
27    return render(request, 'nombre_de_tu_app/contacto.html', {'form': form})

Usuarios en Django#

Django cuenta con modelos integrados para gestionar usuarios y permisos

Users o usuarios#

Cargar librerías#

  • Los metodos de usuarios se importan desde django.contrib.auth los metodos authenticate, users, login, logout

  • El modelo de usuarios se importa desde django.contrib.auth.models en donde se encuentran User y Group

  • Los formularios de usuarios se importan desde django.contrib.auth.forms en donde se encuentran UserCreationForm y AuthenticationForm

Se pueden llamar desde las vistas o los templates con request.user para hacer validaciones o simplemente mostrarlo.

También se pueden consultar en la bases de datos con el ORM:

  • User.objects.create_user(username, password, email): Permite crear un nuevo usuario.

  • User.objects.get(username).set_password('password'): permite cambiar la contraseña.

Sistema de autenticación#

  • Preparando la ruta (urls.py):

1urlpatterns = [
2    path('admin/', admin.site.urls),
3    path('', views.panel, name='panel'),
4    path('register/', views.register, name='register'),
5    path('login/', views.start, name='login'),
6    path('logout/', views.close, name='logout'),
7]
  • Preparando la vista (views.py):

 1from django.shortcuts import render, redirect, HttpResponse
 2# importar los metodos del auth necesarios:
 3from django.contrib.auth import authenticate, login, logout
 4# se importa el formulario de autenticación:
 5from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
 6
 7
 8def panel(request):
 9    # preparamos la respuesta que por defecto redirige a login:
10    response = redirect('login')
11
12    # comprobamos que se ha iniciado sesión y cambiamos la respuesta de ser así:
13    if request.user.is_authenticated:
14        response = HttpResponse('<h1>Hola ' + request.user.username + '!</h1><a href="logout/">Cerrar sesión</a>')
15
16    return response
17
18
19def register(request):
20    # cargamos el formulario de creación de usuarios:
21    form = UserCreationForm()
22    # cargamos la respuesta:
23    response = render(request, 'core/register.html', {'form': form})
24
25    # Se valida y se genera el nuevo usuario:
26    if request.method == "POST":
27        form = UserCreationForm(data=request.POST)
28
29        if form.is_valid():
30            user = form.save()
31            # Se puede aprovechar para hacer login automáticamente:
32            if user is not None:
33                login(request, user)
34                response = redirect('panel')
35
36    return response
37
38
39def start(request):
40    # se carga el formulario de autenticación:
41    form = AuthenticationForm()
42    # preparamos la respuesta:
43    response = render(request, "core/login.html", {'form': form})
44
45    # si llega algo se intentará iniciar sesión:
46    if request.method == 'POST':
47        form = AuthenticationForm(data=request.POST)
48
49        if form.is_valid():
50            username = form.cleaned_data['username']
51            password = form.cleaned_data['password']
52
53            # se verifica que el usuario existe o no:
54            user = authenticate(username=username, password=password)
55
56            # si existe se inicia sesión:
57            if user:
58                login(request, user)
59                response = redirect('panel')
60
61    return response
62
63
64def close(request):
65    # realizar logout:
66    logout(request)
67    return redirect('panel')
  • Preparando plantilla para login (login.html):

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4</head>
 5<body>
 6    <form method="POST">
 7        {{ form.as_p }}
 8        {% csrf_token %}
 9        <button type="submit">Iniciar sesión</button>
10    </form>
11    <hr />
12    <a href="{% url 'register' %}">Crear usuario</a>
13</body>
14</html>
  • Preparamos plantilla para el registro (register.html):

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4</head>
 5<body>
 6    <form method="POST">
 7        {{ form.as_p }}
 8        {% csrf_token %}
 9        <button type="submit">Registrar usuario</button>
10    </form>
11    <hr />
12    <a href="{% url 'login' %}">Iniciar sesión</a>
13</body>
14</html>

Cambiar usuario por email#

Se puede reemplazar el campo para iniciar sesión por otro valor como un DNI o también el Email:

  • Abstaer el formulario para usar email (forms.py):

 1from django import forms
 2# se importan los formularios de creación y autenticación para abstraer:
 3from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
 4# se importa el modelo user:
 5from django.contrib.auth.models import User
 6
 7# Extender un nuevo formulario a partir del original:
 8class CreationWithEmail(UserCreationForm):
 9    # se recupera el campo username y se cambia su formato:
10    username = forms.EmailField(label='Correo electrónico')
11
12    class Meta:
13        model = User
14        fields = ['username', 'password1', 'password2']
15
16
17# hacemos lo mismo con authentication:
18class AuthenticationEmail(AuthenticationForm):
19    username = forms.EmailField(label='Correo electrónico')
20
21    class Meta:
22        model = User
23        fields = ['username', 'password1']
  • Implementar formularios en las vistas de login y register (views.py):

 1from django.shortcuts import render, redirect, HttpResponse
 2from django.contrib.auth import authenticate, login, logout
 3# se reemplazan los forms de auth por los recien creados:
 4from .forms import CreationWithEmail, AuthenticationEmail
 5
 6
 7def register(request):
 8    # Reemplazamos el formulario:
 9    form = CreationWithEmail()
10    response = render(request, 'core/register.html', {'form': form})
11
12    if request.method == "POST":
13        # Reemplazamos el formulario:
14        form = CreationWithEmail(data=request.POST)
15
16        if form.is_valid():
17            user = form.save()
18
19            if user is not None:
20                login(request, user)
21                response = redirect('panel')
22
23    return response
24
25
26def start(request):
27    # Reemplazamos el formulario:
28    form = AuthenticationEmail()
29    response = render(request, "core/login.html", {'form': form})
30
31    if request.method == 'POST':
32    # Reemplazamos el formulario:
33        form = AuthenticationEmail(data=request.POST)
34
35        if form.is_valid():
36            username = form.cleaned_data['username']
37            password = form.cleaned_data['password']
38
39            user = authenticate(username=username, password=password)
40
41            if user:
42                login(request, user)
43                response = redirect('panel')
44
45    return response

Permisos#

Los permisos al igual que los grupos se generan por defecto con Django. En el caso de los permisos estos se generan nuevos (lectura, escritura, edición y borrado) con cada aplicación que se crea nueva en el proyecto.

Existen dos formas de editar permisos:

  1. Desde el panel de administración: seleccionar un usuario y añadir los permisos que debe tener.

  2. Desde la base de datos: Se pueden añadir, editar o borrar en la tabla auth_user_user_permissions. Se puede contrastar el id del campo permission_id con el campo codename de la tabla permission para así usarlo con una función especial en el código:

 1def post(self, request):
 2    # Se consulta si el usuario tiene el permiso en base a la app (API) y el nombre del permiso (add_consola):
 3    if request.user.has_perm('API.add_consola'):
 4
 5        serializer = ConsolaSerializer(data=request.data)
 6        if serializer.is_valid():
 7            serializer.save()
 8            return Response(serializer.data, status=status.HTTP_201_CREATED)
 9        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
10    # sino se le revoca la acción:
11    else:
12        return Response({'error': 'Operación no AUTORIZADA'})

Panel de administración#

El panel de Administración de Django es un modelo CRUD ya definido por defecto con todo el Scaffold del sistema login preparado por defecto.

Puesta en marcha#

Para poner en marcha el panel tenemos que hacer un par de cosas en consola:

  • Primero tenemos que crear todo el Scaffold ejecutando python manage.py migrate

  • Después ejecutamos python manage.py createsuperuser para generar un nuevo superusuario.

Ahora ya podemos acceder al panel de administración desde la ruta localhost:8000/admin

Mostrar datos de nuestros modelos#

El panel de Administración solo dispone por defecto de las tablas de usuarios. Pero si hemos creado un modelo debemos implementarlo, para ello en la carpeta de nuestra app veremos un archivo admin.py el cual editamos:

1from django.contrib import admin
2
3# Importamos el modelo:
4from .models import Prueba
5
6# Registramos en el panel el modelo:
7admin.site.register(Prueba)

De este modo podremos leer, editar, borrar y añadir registros a esta tabla de nuestra app.

Personalizar titulos de las Apps#

En el panel de administración vemos que las tablas se irán dividiendo en apartados según su app, si tenemos varias apps veremos que cada tabla esta dentro de apartados. Podemos cambiar el título de estos apartados accediendo a nuestra app y editando el archivo app.py:

1from django.apps import AppConfig
2
3
4class nombre_de_tu_appConfig(AppConfig):
5    name = 'nombre_de_tu_app'
6
7    # Podemos asignarle un nombre que veremos en el panel:
8    verbose_name = 'App de Prueba'
  • Para que esto funcione tenemos que exportar dicha configuración a settings.py:

1INSTALLED_APPS = [
2    'django.contrib.admin',
3    'django.contrib.auth',
4    'django.contrib.contenttypes',
5    'django.contrib.sessions',
6    'django.contrib.messages',
7    'django.contrib.staticfiles',
8    'nombre_de_tu_app.apps.nombre_de_tu_appConfig' # cambiamos el nombre de la app por su clase configuradora.
9]

Personalizar tablas#

Cuando accedemos a una tabla podemos ver una lista con todos los títulos o el valor que hayamos devuelto en el modelo. Pero podemos modificar su comportamiento editando el archivo admin.py:

 1from django.contrib import admin
 2from .models import Prueba
 3
 4# Creamos una clase que se encargará de editar las configuraciones de nuestro panel:
 5class PruebaAdmin(admin.ModelAdmin):
 6    # con esta tupla definimos los campos que serán de solo lectura cuando abramos un registro.
 7    readonly_fields = ('fecha_creacion', 'fecha_edicion')
 8    # Con list_display definimos que campos se mostrarán en el listado:
 9    list_display = ('titulo', 'autor', 'descripcion', 'fecha_creacion')
10    # Aquí también podemos establecer el orden de lista:
11    ordering = ('fecha_creacion', 'titulo')
12
13    # Opcionalmente podemos cambiar la jerarquía de los breadcums para que se muestren por fecha de publicación:
14    date_hierarchy = 'fecha_publicacion'
15
16    # Filtrar también los datos que se muestran en la barra lateral derecha:
17    list_filter = ('autor__username', 'fecha_creacion')
18
19admin.site.register(Prueba, PruebaAdmin)

Cambiar título al panel#

Para cambiar el título que se muestra en el panel es tan sencillo como irnos a urls.py principal y al final del archivo añadir:

1# Cambiar el título:
2admin.site.site_header = 'Mi Sitio web'
3
4# Cambiar el subtítulo:
5admin.site.index_title = 'Panel de Administración'
6
7# cambiar texto de la pestaña de navegación:
8admin.site.site_title = 'Mi sitio web dedicado a Django!!!'

Enviar Emails#

libreria send_email() send_mass_email() en HTML también

https://docs.djangoproject.com/en/4.0/topics/email/