Ejemplo de un proyecto básico con Python, Django y PyCharm – II

Django es un Framework para el desarrollo de Aplicaciones Web en Python, que facilita muchas tareas, como la generación de páginas dinámicas desde el modelo (base de datos), uso de estilos y estáticos para mejorar la presentación (ej: css, js, imágenes, etc), auto-generación de formularios desde el modelo (minimizando el esfuerzo de codificación), y proporciona autenticación de usuarios mediante la built-in App de Auth, que a su vez permite crear formulario de login, logout, o signup de forma muy sencilla. Todo eso, y algún detalle más, es lo que vamos a ver en este Post.

Continuando con la serie de Posts sobre Python y Django, en esta ocasión vamos a partir del anterior ejemplo que vimos en el Post Ejemplo de un proyecto básico con Python, Django y PyCharm, para extenderlo y aprender cosas nuevas partiendo de lo que ya hicimos. De nuevo, también disponible en el mismo repo público de GitHub (sepando por tags): GitHub – ElWillieES – django-basic-example

En esta ocasión vamos a ver a través de ejemplos paso a paso los siguientes puntos:

  • Cómo generar páginas dinámicas desde el modelo de base de datos.
  • Cómo entregar las páginas con estilos (ej: css, fuentes, imágenes, etc) y cómo realizar la gestión de dichos archivos estáticos.
  • Cómo crear páginas de formularios, que permitan a los usuarios no admins realizar operaciones CRUD (Create, Read, Update, Delete), mediante la auto-generación de formularios Django (ej: form.as_p, form.as_table, form.as_ul) y la extensión de vistas genéricas (ej: CreateView, UpdateView, DeleteView).
  • Cómo configurar la autenticación de usuarios (ej: login, logout, signup), utilizando la gestión de usuarios por defecto de Django, la auto-generación de formularios Django (ej: form.as_p, form.as_table, form.as_ul) y vistas genéricas (ej: LoginView, SignUpView), así como proteger los enlaces a los formularios para que sean accedidos sólo por usuarios autenticados, gracias a la Aplicación predefinida (built-in App) de Auth.
  • Cómo utilizar Bootstrap y Django Crispy Forms para mejorar la presentación de nuestra Web.

Dicho esto, comenzamos.

Generar páginas dinámicas desde el modelo de base de datos

La idea es generar páginas dinámicas desde el contenido de la base de datos, es decir, siguiendo con nuestro ejemplo de libros y autores, que para cada libro podamos tener una página (con su propia URL) en la que se muestre el detalle del mismo.

Para empezar, vamos a modificar nuestro modelo, el fichero models.py, para añadir el método get_absolute_url, que le permite a Django definir la URL para cada instancia del modelo, en este caso, en base a la Primary Key (PK). Hay que tener en cuenta, que Django automáticamente añade en la base de datos de cada Modelo una Primary Key (id) en forma de un campo entero auto-numérico, que podemos utilizar en casos como este, y a la que podremos referenciar como id o pk.

Para continuar, vamos a crear una Vista basada en una de las clases genéricas, DetailView, que tiene de particular que espera como parámetro una Primary Key (PK) o un Slug, que utiliza para poder proporcionar como contexto un objeto con la instancia del modelo que deseamos.

Tenemos el Modelo y la Vista. Ahora vamos a por la Plantilla (Template), para lo que crearemos una nueva plantilla book_detail.html, con un contenido como el siguiente. Como vemos, también hereda de la plantilla base.

{% extends "base.html" %}

{% block content %}
<div class="book-details">
    <h2>{{ book.title }}</h2>
    <p>Author: {{ book.author }}</p>
    <p>Genre: {{ book.genre }}</p>
    <p>Publish date: {{ book.publish_date }}</p>
</div>
{% endblock %}

Un detalle a tener en cuenta, es que desde la Plantilla podemos acceder al modelo también a través del contexto «object», además de cómo lo hemos hecho antes utilizando el nombre del modelo en minúsculas (book). Esto está muy bien, ya que facilita poder generalizar nuestras Plantillas, indiferentemente del modelo subyacente. A continuación se muestra un ejemplo de como quedaría así.

Ya tenemos el Modelo, la Vista, y la Plantilla, así que, sólo queda la URL. Modificamos el fichero urls.py de la Aplicación Django, para añadir el enrutamiento para la página de detalle que estamos creando. Importante que el nombre de la URL coincida con el que especificamos en la función get_absolute_url del Modelo, y utilizaremos la Vista que acabamos de definir.

Con esto ya estaría, pero para poder verlo en acción, vamos a editar la página principal (home), que muestra todos los libros que tenemos en base de datos, para incluir para cada libro, un enlace a la página que muestra su detalle, utilizando la Primary Key.

Hecho esto, tan sólo queda ejecutarlo y probarlo. Efectivamente, funciona como esperábamos.

Entrega de ficheros estáticos (css, js, imágenes, etc)

La utilización de ficheros estáticos es fundamental para cambiar y mejorar la apariencia de cualquier aplicación web.

Por defecto, Django busca los ficheros estáticos de cada Aplicación Django, en una carpeta /static de la propia Aplicación (ej: basic_example_pages/static), algo similar a como ocurre con las Plantillas (Templates), quedando los estáticos distribuidos a través de las diferentes Aplicaciones Django del Proyecto (en caso de tener varias).

Sin embargo, es muy habitual utilizar una carpeta de estáticos (ej: /static) global para todo el Proyecto Django y todas sus Aplicaciones, que es lo que vamos a configurar a continuación. Para ello:

  • Crearemos una carpeta /static, donde podremos almacenar nuestros estáticos.
  • Necesitamos defirnir dos configuraciones en el fichero settings.py, una es el valor de STATIC_URL que ya definimos en el Post anterior, la otra es STATICFILES_DIR, que le indica a Django en qué directorio buscar los ficheros estáticos.

En la siguiente imagen se representan estos cambios realizados en nuestro proyecto.

Ahora, vamos a modificar la Plantilla base.html para añadir una hoja de estilos (css) y una fuente personalizada de Google Fonts. Como el resto de páginas heredan de esta, es suficiente con hacer esta modificación un única vez, aquí. Tan sólo será necesario realizar los siguientes cambios.

Ya sólo nos queda crear la hoja de estilos (css/base.css), para que que la descargue el navegador y visualice nuestras páginas aplicándola, y así consigamos personalizar y mejorar su aspecto.

body {
    font-family: 'Lobster', sans-serif;
    font-size: 18px;
}

header {
    margin-bottom: 2rem;
}

a {
    color: blue;
    text-decoration: none;
}

a:hover {
    color: red;
    text-decoration: underline;
}

header nav ul {
  list-style-type: none;
  display: flex;
}

header nav ul li {
  margin-right: 1rem;
}

header nav ul li:not(:last-child)::after {
  content: "|";
  margin-left: 0.5rem;
}

.book-details {
    margin-top: 2rem;
    margin-bottom: 2rem;
}

.book-details p {
    margin: 0;
}

Hecho esto, tan sólo queda ejecutarlo y probarlo. Efectivamente, funciona como esperábamos, la hoja de estilos base.css y la fuente se cargan y se aplican a las páginas Web de nuestro Proyecto Django.

Con este pequeño ejemplo, hemos visto como utilizar estáticos en una aplicación Django para mejorar su apariencia. Si bien esta es una práctica necesaria, hay más técnicas para este fin, como es Bootstrap (lo veremos más abajo).

Crear Formularios para usuarios no Admins

Ha llegado el momento de hacer Formularios que puedan utilizar los usuarios que no son Admins, algo delicado, por las implicaciones de seguridad que tiene cuando se trata de aplicaciones web que están disponibles de forma masiva en Internet. Por suerte, Django proporciona la auto-generación de sus propios formularios (built-in Forms) de forma sencila (ej: form.as_p, form.as_table, form.as_ul), así como vistas genéricas (ej: CreateView, UpdateView, DeleteView), que facilitan esta tarea, y que son lo que vamos a usar.

Vamos a empezar con la creación de nuevos elementos, en nuestro caso, de nuevos libros.

Lo primero, añadir un enlace en la Plantilla base.html que vaya a la página que permite añadir nuevos libros. Tan sólo tenemos que añadir la siguiente línea.

Lo siguiente es configurar el enrutamiento, en el fichero urls.py de la Aplicación Django. Básicamente, añadir una línea, así como la importación de la Vista que vamos a usar (BookCreateView).

Continuamos con la creación de la Vista, para ello vamos a importar la clase CreateView, y vamos a crear nuestra propia clase extendiéndola. Importante el detalle de que tenemos que especificar los campos del modelo que deseamos mostrar en el formulario (title, genre, author, publish_date). En cualquier caso, tan sólo hay que añadir el siguiente código al fichero views.py de la Aplicación Django.

El modelo es el que ya creamos en su momento (Book), por lo que ya sólo nos queda crear la Plantilla (Template), extendiendo de la base. Los dos detalles más importantes:

  • La etiqueta csrf_token añade de forma automática la lógica necesaria para protegernos de ataques CSRF (Cross-Site Request Forgery)
  • La etiqueta form.as_p generará el formulario de forma automática como un conjunto de elementos <p>, para los campos que indicamos en la Vista, lo cual es una gran ayuda. También podríamos utilizar form.as_table ó form.as_ul.

Ahora vamos a continuar con la modificación de elementos existentes, es decir, poder editar un libro.

En la Plantilla book_detail.html que usamos para ver el detalle de un libro, vamos a añadir un enlace que permita acceder a un formulario para su edición, pasando como parámetro la Primary Key (PK) del libro a editar. Básicamente, es añadir sólo una línea.

Para continuar, vamos a crear la Plantilla book_update.html para la edición de los libros. Es prácticamente igual al que acabamos de crear para añadir nuevos libros.

Seguimos con la creación de la Vista, para ello vamos a importar la clase UpdateView, y vamos a crear nuestra propia clase extendiéndola, de nuevo especificando los campos del modelo que deseamos mostrar en el formulario (si queremos todos los campos, podemos utilizar __all__). En cualquier caso, tan sólo hay que añadir el siguiente código al fichero views.py de la Aplicación Django.

Ya sólo queda configurar el enrutamiento de URLs en el fichero urls.py de la Aplicación Django. Básicamente, añadir una línea, así como la importación de la Vista que vamos a usar (BookUpdateView).

Ya tenemos los formularios para que los usuarios puedan crear nuevos libros así como actualizarlos. Ahora vamos a continuar con el borrado de elementos existentes, es decir, poder eliminar o borrar un libro.

En la Plantilla book_detail.html que usamos para ver el detalle de un libro, vamos a añadir un enlace que permita acceder a un formulario para su borrado, pasando como parámetro la Primary Key (PK) del libro a eliminar. Básicamente, es añadir sólo una línea.

Para continuar, vamos a crear la Plantilla book_delete.html para el borrado de los libros. Es parecida a las que acabamos de crear para añadir o editar libros.

Seguimos con la creación de la Vista, para ello vamos a importar la clase DeleteView, y vamos a crear nuestra propia clase extendiéndola, e indicando que después de borrar el libro haga una redirección a la home. En cualquier caso, tan sólo hay que añadir el siguiente código al fichero views.py de la Aplicación Django.

Ya sólo queda configurar el enrutamiento de URLs en el fichero urls.py de la Aplicación Django. Básicamente, añadir una línea, así como la importación de la Vista que vamos a usar (BookDeleteView).

Y con esto ya habríamos acabado la creación de los formularios para que los usuarios puedan añadir, modificar, o eliminar los libros, sin tener que acceder al Panel de Administración de Django. Ya sólo queda probarlo, y efectivamente, funciona.

Autenticación de Usuarios

Si bien ya tenemos creados los Formularios para que los usuarios puedan crear, modificar, y eliminar libros (operaciones CRUD), la realidad es que son tareas que deberían estar restringidas a usuarios autenticados, aunque no sean Administradores, pero en ningún caso estar abierto para que cualquiera pueda acceder.

Para corregirlo, vamos a implementar la Autenticación de Usuarios que nos proporciona Django añadiendo páginas de login, logout, y signup, y vamos a restringir los enlaces a los formularios que realizan las operaciones CRUD para que estén disponibles sólo para usuarios autenticados. Para ellos vamos a utilizar la App Auth y el modelo de usuarios por defecto que proporciona Django.

Vamos a empezar con la implementación del Formulario de Login, mediante la vista LoginView, así como proporcionar un enlace para el Logout.

Para poder utilizar la vista LoginView y el enlace de Logout, tenemos que configurar el enrutamiento de URLs para utilizar la aplicación por defecto (built-in App) de Auth, en esta ocasión, modificando el fichero urls.py del Proyecto Django (en lugar del urls.py de la Aplicación Django). Básicamente, tan sólo hay que añadir una línea como la siguiente, que indica que utilizaremos el path accounts/ para las URLs de la aplicación Auth.

La vista LoginView buscará por defecto una plantilla login.html dentro de un directorio registration. Creamos la plantilla, que contendrá un formulario que auto-generará Django, de forma similar a como se muestra a continuación.

{% extends "base.html" %}

{% block content %}
<h1>Login</h1>
<form action="" method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Login">
</form>
{% endblock %}

Ahora tenemos que hacer una pequeña modificación en el fichero settings.py del Proyecto Django, para indicar a qué página deseamos que se realice la redirección después de que un usuario inicie sesión con éxito, algo que podemos configurar con la opción LOGIN_REDIRECT_URL. Y lo mismo para cuando un usuario haga Logout, en este caso, mediante la opción LOGOUT_REDIRECT_URL.

Hecho esto, podemos modificar nuestra Plantilla base.html, para que muestre un enlace para iniciar sesión (Login), o bien, si ya se había iniciado sesión que muestre un saludo al usuario junto con un enlace para cerrar sesión (Logout). Sólo hay que añadir las siguientes líneas de código.

Con esto, ya hemos implementado la posibilidad de hacer Login y Logout. Ahora vamos a implementar la posibilidad de auto-registro de usuarios (Sing up). Para ello, lo primero que necesitamos hacer es crear una nueva Aplicación Django llamada accounts.

python manage.py startapp accounts

Como siempre que creamos una Aplicación Django, tenemos que hacer saber al Proyecto Django de su existencia, algo que haremos en el fichero settings.py del proyecto, en este caso, añadiendo la siguiente línea.

En el fichero urls.py del Proyecto Django configuraremos el enrutamiento de URLs para la nueva Aplicación, en este caso diremos que cualquier petición a accounts/ se resuelva conforme se defina en el fichero urls.py de la nueva Aplicación Django que acabamo de crear. Esto implica simplemente añadir la siguiente línea al fichero urls.py del Proyecto Django (ojo, el orden es importante, tengamos en cuenta que hay dos entradas para accounts/).

Por otro lado, crearemos un fichero urls.py en la nueva Aplicación Django que acabamos de crear, que será el referenciado por el include que acabamos de configurar en el paso anterior. Añadiremos una ruta para signup/, con lo que quedará algo así.

from django.urls import path
from .views import SignupView

urlpatterns = [
    path("signup/", SignupView.as_view(), name="signup"),
]

Seguimos con la creación de la Vista, en este caso vamos a importar la clase CreateView para poder extenderla, y también el formulario UserCreationForm para utilizarlo en esta Vista y en la Plantilla asociada. Para esto, tan sólo hay que añadir el siguiente código al fichero views.py de la Aplicación Django de accounts.

La vista CreateView buscará la plantilla registration/signup.html que aún no existe. Creamos la plantilla, que contendrá un formulario que auto-generará Django, de forma similar a como se muestra a continuación.

Ya sólo nos queda añadir a la Plantilla base.html, un enlace a la Signup que sólo deberá mostrase cuando no hemos iniciado sesión. También vamos a aprovechar a que la opción de crear un nuevo libro sólo aparezca si se ha iniciado sesión.

Igualmente, aprovechamos para modificar la Plantilla book_detail.html y que los enlaces de editar o eliminar un libro sólo aparezcan si se ha iniciado sesión.

Con esto, ya estaría todo, listo para ejecutar y comprobar el resultado.

Autorización de Usuario

Si bien ya hemos implementación la Autenticación, es decir, permitir identificarnos como un usuario dentro nuestra aplicación, también debemos implementar la Autorización, es decir, permitir que un usuario sólo pueda ejecutar las acciones a las que tiene derecho.

Django también nos ayuda con esto, haciéndolo más sencillo. En nuestro caso, que estamos utilizando Vistas basadas en Clases, podemos implementarlo principalmente de dos formas:

  • Utilizando LoginRequiredMixin, para proteger determinadas páginas (ej: Create Book, Edit Book y Delete Book) de tal modo que sólo se pueda acceder a ellas si has iniciado sesión en la aplicación.
  • Utilizando UserPassesTestMixin, para proteger determinadas páginas (ej: Edit Book y Delete Book) de tal modo que sólo se pueda acceder a ellas si se cumple una condición personalizada que implementaremos sobrescribiendo un método.

Cuando hablamos de Mixin básicamente estamos hablando de herencia múltiple. Lo que vamos a hacer, es que las Clases de nuestras Vistas, además de heredar de una clase base en función del tipo de vista (ej: TemplateView, ListView, etc.), también hereden de otra clase (Mixin), y de esta forma, podamos alterar o personalizar su comportamiento. Vamos a verlo a través de un ejemplo.

Para implementar LoginRequiredMixin en algunas páginas (Create Book, Edit Book y Delete Book), los único cambios que tenemos que hacer son los siguientes.

Hecho esto, si intentamos acceder directamete sin autenticarnos previamente a cualquiera de las URLs de las páginas que hemos protegido, nos redireccionará a la página de Login.

Para implementar UserPassesTestMixin, además necesitaremos sobrescribir un método. En el siguiente ejemplo, se muestra a modo ilustrativo como podríamos hacer, suponiendo que tuviéramos un campo Owner en cada libro que almace el usuario que lo ha creado, y queramos limitar la edición y borrado sólamente al propietario. Los cambios que habría que realizar son los siguientes:

En nuestro caso de ejemplo, como nuestro modelo Book no incluye el campo owner, no implementaremos finalmente este último cambio (UserPassesTestMixin), pero en cualquier caso, de hacerlo sería de esta forma, pudiendo realizar compromaciones mucho más complejas.

Uso de Bootstrap

Bootstrap es un framework de desarrollo web de código abierto orientado al diseño y presentación de páginas Web, que proporciona una colección de herramientas y estilos CSS predefinidos para crear sitios web y aplicaciones responsive (su visualización y comportamiento es correcto en diferentes dispositivos y tamaños de pantalla). Creado inicialmente por Twitter, ahora es mantenido por la comunidad de desarrolladores.

Al utilizar Bootstrap, los desarrolladores ahorran esfuerzo (aumentando la productividad) al no tener que crear estilos y componentes desde cero, y utilizar los proporcionados por Bootstrap: botones, barras de navegación, formularios, tablas, alertas, carrouseles, etc. Vamos a verlo a través de un ejemplo, utilizando Bootstrap 5.

Para añadir Bootstrap, editaremos la Plantilla base.html para añadir el siguiente código (básicamente, añadir tres líneas), siguiendo las instrucciones de la Web de Bootstrap, aunque también vamos a añadir un contenedor de Bootstrap a nuestra página para envolver el contenido.

...
<head>
    ...
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    ...
</head>
<body>
...
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
...

Hecho esto, lo único que notaremos de cambio, es que el contenido de las páginas queda un poco centrado, tal y como se aprecia a continuación. Eso, y que ya tenemos disponibles para usar todas las clases CSS que proporciona Bootstrap.

Ahora podemos, por ejemplo, añadir las clases CSS pre-definicidas por Bootstrap que deseemos para formatear un botón, como se muestra en el siguiente ejemplo realizado sobre la Plantilla signup.html.

Y en un instante, hemos cambiado su aspecto, sin necesidad de desarrollar desde cero nosotros mismos los estilos (hay un montón de opciones).

Empleando unos minutos más, tomando los ejemplos del propio Bootstrap, podríamos adaptar la barra navegación de forma fácil y rápida, modificando la Plantilla base.html, algo como esto.

Con lo que conseguimos algo como esto.

Uso de Django Crispy Forms

Crispy Forms es una popular biblioteca de Python para Django que permite mejorar la apariencia de los formularios HTML proporcionados por Django, y se integra con el sistema de Plantillas (Templastes) de Django. Crispy Forms incluye sus propios CSS pre-definidos, que permiten crear fácilmente formularios con diseños complejos, campos en varias columnas, campos en línea, diseño de botones, campos de fecha, campos de selección, etc. Además se puede integrar Bootstrap 5, como vas a realizar a continuación.

Para usar Django Crispy Forms, vamos a añadir a nuestro requirements.txt las librerías django-crispy-forms y crispy-bootstrap5, e instalarlas en el Virtual Environment de PyCharm.

Además tendremos que modificar el fichero settings.py del Proyecto Django, por un lado para añadir como Aplicación Django al Proyecto las dos librerías que acabamos de incluir en el requirements.txt, y por otro lado añadiremos las opciones CRISPY_ALLOWED_TEMPLATE_PACKS y CRISPY_TEMPLATE_PACK al final del fichero. Hay que tener en cuenta que hay más valores posibles para CRISPY_TEMPLATE_PACK, como «bootstrap», «bootstrap3», «bootstrap4», ó «unif-form» (esta última opción es apropiada cuando no usamos Bootstrap).

...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'crispy_forms',         # Third Party
    'crispy_bootstrap5',    # Third Party
    'basic_example_pages.apps.BasicExamplePagesConfig',
    'accounts.apps.AccountsConfig',
...
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
CRISPY_TEMPLATE_PACK = "bootstrap5"

Hecho esto, aplicar Crispy Forms en un formulario pre-definido de Django es bastante sencillo, basta con añadir una línea para cargar Crispy, y cambiar la auto-generación del formulario de {{ form.as_p }} for {{ form|crispy }}, tal y como se ve en el siguiente ejemplo.

El resultado de este pequeño cambio, es que el formulario de Signup ahora se muestre así:

Incluso, si quisiéramos podríamos especificar cada campo, uno a uno y en el orden que deseemos, para construir nuestro formulario de una forma más personalizada, quizás incluso envolviéndoles sobre etiquetas div con clases CSS personalizadas, etc.

El resultado de este último ejemplo, con el orden de los campos cambiados, sería el siguiente.

Ejecución de Pruebas Unitarias (Unit Testing)

Tenemos que añadir a las Pruebas Unitarias que ya teníamos, las necesarias para probar la nueva página que se genera dinámicamente para mostrar los datos de un libro en base a su Primary Key, así como probar el Modelo. También vamos a comprobar que en la Home se muestran datos de la base de datos. Para ello, añadiremos lo siguiente a la clase BookTests del fichero tests.py.

También vamos a añadir Tests para probar las Vistas para crear, editar, y eliminar libros. Algún detalle importante, en estos tests:

  • Al realizar las llamadas con client.post, debemos especificar todos los campos que son usando en el formulario.
  • Después de realizarse la acción, se realiza una redirección, por lo que comprobamos que se devuelve un código HTTP 302.
  • test_book_updateview intenta actualizar el libro creado en el método setUpTestData (con pk=1).
  • test_book_deleteview intenta elimiar el libro creado en el método setUpTestData (con pk=1).

Hecho esto, estamos en situación de ejecutar las pruebas unitarias y el sonar-scanner para subir los resultados del análisis de código y cobertura a Sonarqube, y ver cómo ha quedado.

Despedida y Cierre

Hasta aquí llega este nuevo Post de Python y Django, en el que hemos visto varias cosas cómo por ejemplo:

  • Cómo generar páginas dinámicas desde el modelo de base de datos
  • Cómo entregar las páginas con estilos (ej: css, fuentes, imágenes, etc) y cómo realizar la gestión de dichos archivos estáticos.
  • Cómo crear páginas de formularios, que permitan a los usuarios no admins realizar operaciones CRUD, mediante la auto-generación de formularios Django (ej: form.as_p, form.as_table, form.as_ul) y la extensión de vistas genéricas (ej: CreateView, UpdateView, DeleteView).
  • Cómo configurar la autenticación de usuarios (ej: login, logout, signup), mediante la auto-generación de formularios Django (ej: form.as_p, form.as_table, form.as_ul) y vistas genéricas (ej: LoginView, SignUpView).
  • Cómo utilizar Bootstrap y Django Crispy Forms.

Además, tienes disponible este ejemplo en el siguiente repo público de GitHub: GitHub – ElWillieES – django-basic-example

Poco más por hoy. Como siempre, confío que la lectura resulte de interés.