Django es el framework de Python más utilizado para el desarrollo web. Sus características integradas y su estructura robusta lo convierten en una excelente opción a la hora de crear aplicaciones web. Pero hay tantos recursos disponibles que a veces es abrumador aplicar ese conocimiento a proyectos del mundo real. En este tutorial, vamos a crear una aplicación web completa, usando Django en el back-end y Django Templates estilizadas con Bootstrap en el front-end.

Tabla de contenidos

Requisitos

Para sacar el máximo provecho de este tutorial, lo ideal es que conozcas lo siguiente:

  • los conceptos básicos de Python
  • Programación orientada a objetos en Python
  • los conceptos básicos del marco web de Django

Si no tienes experiencia previa con Django, no tengas miedo de continuar con este tutorial. Este será un proceso paso a paso, y se explicará cada paso.

Antes de empezar, quiero presentarte a tu nuevo mejor aliado, la documentación de Django. Haremos referencia a él a lo largo del artículo, así que asegúrate de familiarizarte con él.

Una aplicación para compartir fotos de Django

Todo el código fuente de este tutorial está disponible en este repositorio de GitHub.

La complejidad de un proyecto depende de todas las características que queramos incluir. Cuantas más funciones queramos ofrecer a los usuarios, más tiempo necesitaremos dedicar a construir e integrar todo en un proyecto único.

Teniendo esto en cuenta, vamos a ver una distinción rápida entre lo que vamos a construir y lo que no.

Lo que vamos a construir

En este tutorial, crearemos una aplicación para compartir fotos de pila completa (desarrollo back-end y front-end). Nuestra aplicación incluirá las siguientes características:

  • Funcionalidad de base de datos CRUD (Crear, Leer, Actualizar, Eliminar)
  • Un sistema de gestión de usuarios, para que los usuarios puedan crear una cuenta, subir fotos, ver las fotos de otras personas y editar o eliminar sus propias fotos
  • una interfaz web sencilla hecha con Bootstrap

Nota: aunque esta aplicación parece bastante similar a una red social, no lo es. Una aplicación como Instagram o Twitter tiene mucha complejidad que no se puede cubrir en un solo artículo.

Pila tecnológica

Definamos las tecnologías que vamos a utilizar. Cubriremos el proceso de instalación de cada uno cuando necesitemos usarlo.

En el back-end, Django será el marco central de la aplicación. Nos permite definir las URLs, definir la lógica, gestionar la autenticación de usuarios y controlar todas las operaciones de la base de datos a través del ORM (mapeador relacional de objetos) de Django.

Además, usaremos un par de paquetes de terceros para acelerar el desarrollo de algunas funciones.

Django-taggit nos proporciona la posibilidad de configurar un sistema de etiquetas simple en pocos pasos. Pillow es un paquete de Python que proporciona capacidades de manipulación de imágenes de Django. Por último, Django-crispy-forms nos ofrece una forma sencilla de mostrar los formularios de Bootstrap.

En el front-end, vamos a utilizar el lenguaje de plantillas Django, que consiste en archivos HTML que muestran datos de forma dinámica.

También usaremos Bootstrap 5 (la última versión en el momento de escribir este artículo) para el diseño del sitio.

Nota: siempre puede verificar las dependencias utilizadas en este proyecto en el archivo requirements.txt.

Crear un proyecto de Django

¡Comencemos con Django!

En primer lugar, asegúrese de tener instalado Python 3. La mayoría de los sistemas Linux y macOS ya tienen Python instalado, pero si usa Windows, puede consultar la guía de instalación de Python 3.

Nota: usaremos comandos de Unix (macOS y Linux) a lo largo del tutorial. Si no puede ejecutarlos por alguna razón, puede usar un administrador de archivos gráficos.

En algunas distribuciones de Linux, el comando python se refiere a Python 2. En otros, python no existe en absoluto.

Veamos qué comando de Python necesita usar para seguir. Abra su terminal (en Unix) o ventana de línea de comandos (en Windows) y escriba python --version:

python --version

# My result
Python 3.9.5

Si tienes una versión de Python superior a la 3.6, estás listo para comenzar. Si no tienes la versión correcta de Python, es posible que recibas un mensaje como uno de estos:

Command 'python' not found
Python 2.7.18

El comando de Python que debe ejecutar para seguir este tutorial será python3:

python3 --version

Python 3.9.5

Entornos virtuales

Un entorno virtual es un entorno aislado de Python, que incluye todos los archivos que necesita para ejecutar un programa de Python.

Los entornos virtuales son una parte crucial de cualquier proyecto de Python (y Django), ya que nos permiten administrar y compartir dependencias (paquetes externos de los que depende el proyecto) con otras personas.

Para crear un entorno virtual de forma nativa, usaremos el módulo integrado venv, disponible a partir de Python 3.6 o superior.

El siguiente comando creará un entorno virtual con el nombre .venv (puedes elegir otro nombre si lo prefieres):

python -m venv .venv

Si está usando Ubuntu Linux, o cualquier otra distribución basada en Debian, es posible que reciba el siguiente mensaje:

The virtual environment was not created successfully because pip is not available ... 

Para solucionar esto, puede ejecutar el siguiente comando:

sudo apt-get install python3-venv

Si el comando anterior no funciona, puede usar virtualenv, que es otra biblioteca para trabajar con entornos virtuales:

virtualenv .venv

Después de ejecutar este comando, aparecerá una carpeta llamada .venv (o el nombre que hayas elegido).

Todos los paquetes que instalemos se colocarán dentro de ese directorio.

Para activar un entorno virtual, deberá ejecutar un comando específico en función de su sistema operativo. Puede consultar la siguiente tabla (extraída de los documentos de Python).

PlataformaShellComando para activar el entorno virtual
POSIXbash/zsh$ source .venv/bin/activate
fish$ source .venv/bin/activate.fish
CSH/TCSH$ source .venv/bin/activate.csh
PowerShell Core$ .venv/bin/Activate.ps1
Windowscmd.exeC:> .venv\Scripts\activate.bat
PowerShellPS C:> .venv\Scripts\Activate.ps1

Dado que estoy usando un shell bash en un sistema operativo POSIX, usaré esto:

source .venv/bin/activate

Observe cómo se agrega un título .venv a mi shell una vez que he activado el virtualenv.

Instalación de Django

Django es un paquete externo, por lo que necesitaremos instalarlo con pip:

pip install django

# Use pip3 if the command above doesn't work

pip3 install django

Nota: siempre podemos echar un vistazo a los paquetes instalados en nuestro venv con pip freeze.

A continuación, comencemos un proyecto de Django con el nombre config con la utilidad de línea de comandos django-admin.

django-admin startproject config

Aquí, config es el nombre del proyecto y se usa como convención de nomenclatura para mantener todos los proyectos con la misma estructura. Por ejemplo, Django cookiecutter usa este nombre de convención para iniciar un proyecto.

Dicho esto, puedes crear el proyecto con cualquier otro nombre.

Después de ejecutar estos comandos, debería tener la estructura de archivos normal de un proyecto de Django. Puede comprobarlo con el árbol de utilidades de la línea de comandos o con cualquier administrador de archivos.

Nota: si no puede ejecutar tree, deberá instalarlo.

$ tree config/
└── config
    ├── config
    │   ├── asgi.py
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py

Ahora entremos en la carpeta del proyecto con cd, y ejecutemos el servidor para comprobar que todo está configurado correctamente:

cd config/

python manage.py runserver

Verá un mensaje de advertencia que indica que hay migraciones no aplicadas. Este es un mensaje totalmente normal, y aprenderemos cómo ejecutar migraciones en la sección «Creación del modelo de foto«.

Ahora, visite localhost:8000 en su navegador. Deberías ver la icónica página de felicitaciones de Django.

Iniciar la aplicación para compartir fotos

El archivo manage.py tiene exactamente las mismas capacidades que django-admin, por lo que lo usaremos muchas veces durante este tutorial.

Su ubicación está en la carpeta raíz del proyecto, y cada vez que queramos ejecutar un comando con él, debemos ingresar al directorio del proyecto.

Recuerda siempre listar los archivos del directorio en el que te encuentras con ls, para comprobar si estamos en el lugar correcto:

$ ls
Another-files.. manage.py

Con estos consejos en mente, es hora de iniciar la aplicación principal del proyecto. Para ello abrimos un nuevo shell (para que el servidor local siga funcionando), y usamos el manage.py con el comando startapp.

Nota: cada vez que abramos una nueva sesión de shell, tendremos que volver a activar el entorno virtual.

source .venv/bin/activate
cd config
python manage.py startapp photoapp

En este caso, el nombre de la aplicación es photoapp. Una vez más, puedes crearlo con el nombre que quieras.

Cada vez que creamos una app debemos instalarla. Podemos hacer esto en el archivo config/settings.py agregando photoapp a la variable INSTALLED_APPS:

# config/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    ...

    # Custom apps
    'photoapp',
]

A continuación, ingresaremos al directorio de la aplicación y crearemos un archivo urls.py vacío. Podemos hacer esto ejecutando touch, o creándolo con un administrador de archivos gráfico:

cd photoapp/

touch urls.py

Por último, incluyamos todos los patrones de URL de la aplicación para compartir fotos en el proyecto general. Para lograr esto, usaremos la función django.urls.include:

# config/urls.py

from django.urls import path, include # Import this function

urlpatterns = [
    path('admin/', admin.site.urls),
    # Main app
    path('', include('photoapp.urls')),
]

El código anterior incluirá todos los patrones de URL de la photoapp/urls.py al proyecto.

Si echas un vistazo al shell en el que se ejecuta el servidor, verás un error:

raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) ....

Esto se debe a que no hemos creado la lista urlpatterns dentro del archivo photopp/urls.py.

Para resolver esto, cree una lista vacía llamada urlpatterns. Vamos a poblar esa variable más adelante con las rutas de Django:

# photoapp/urls.py

# Empty patterns
urlpatterns = [

]

Nota: la ventaja de usar este enfoque es que podemos hacer que la aplicación fotográfica sea reutilizable, incluyendo todo el código necesario dentro de ella.

Creación del modelo fotográfico

En esta sección, vamos a crear el esquema de la base de datos de nuestra aplicación. Para ello, usaremos el ORM de Django.

El ORM de Django permite la creación y gestión de tablas de bases de datos sin necesidad de utilizar SQL manualmente.

Cuando escribimos un modelo, representa una tabla de base de datos, y cada atributo dentro de ella representa una columna.

Dado que usaremos el sistema de autenticación integrado de Django, podemos comenzar a enfocarnos en la funcionalidad principal de la aplicación. De esa manera, evitamos crear un sistema de gestión de usuarios personalizado.

Antes de empezar, vamos a instalar algunos paquetes de terceros, django-taggit y Pillow. Podemos hacerlo con el siguiente comando:

pip install django-taggit Pillow

django-taggit es una aplicación de Django, por lo que necesitamos instalarla como hicimos con la photoapp:

# config/settings.py
INSTALLED_APPS = [
    ...

    # 3rd party apps
    'taggit',

    # Custom apps
    'photoapp',
]

# Django taggit

TAGGIT_CASE_INSENSITIVE = True

La variable TAGGIT_CASE_INSENSITIVE configura las etiquetas para que no distingan entre mayúsculas y minúsculas. Eso significa que PYTHON y python serán lo mismo.

Definamos el modelo Photo, que será el modelo principal de la aplicación. Abra el archivo photoapp/models.py y use el siguiente código:

# photoapp/models.py
from django.db import models

from django.contrib.auth import get_user_model

from taggit.managers import TaggableManager

class Photo(models.Model):

    title = models.CharField(max_length=45)

    description = models.CharField(max_length=250) 

    created = models.DateTimeField(auto_now_add=True)

    image = models.ImageField(upload_to='photos/')

    submitter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)

    tags = TaggableManager() 

    def __str__(self):
        return self.title

En el bloque de código anterior, hemos definido el modelo Photo. Veamos qué hace cada campo.

  • El campo de title es un CharField y está limitado a 45 caracteres.
  • description es otro CharField pero con un límite de 250 caracteres.
  • created es un DateTimeField y, como su nombre indica, almacena la fecha y la hora en que se crea la foto.
  • image es un ImageField. Sube las imágenes a media/photos y almacena la URL en la que se encuentra el archivo. Más adelante veremos cómo configurar archivos multimedia.
  • submitter es una ForeignKey, lo que significa que es una relación con un usuario y la foto cargada. De esa manera podemos filtrar qué usuario subió una foto.
  • Por último, tags es un TaggableManager y nos permite clasificar los temas por etiquetas.

Por otro lado, el método __str__ indica cómo se mostrará cada objeto en el área de administración. Más tarde, configuraremos el administrador y crearemos nuestros primeros objetos.

Para crear una base de datos basada en el modelo que creamos, primero debemos realizar las migraciones y luego ejecutarlas.

Introduzca el directorio raíz del proyecto y utilice el script manage.py con los siguientes argumentos:

python manage.py makemigrations

python manage.py migrate

El comando makemigrations creará un archivo de migraciones basado en el modelo Photo.

Nota: las migraciones son scripts de Python que producen cambios en la base de datos en función de los modelos.

Podemos ver exactamente lo que está sucediendo con esa migración abriendo el archivo photoapp/migrations/0001_initial.py:

# photoapp/migrations/0001_initial.py
# imports ...
class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('taggit', '0003_taggeditem_add_unique_index'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Photo',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                .....

Consejo: nunca modifique el archivo de migraciones a mano. Todas las migraciones deben ser generadas automáticamente por Django.

El comando migrate crea tablas de base de datos ejecutando todas las migraciones.

Después de ejecutar estos dos comandos, debería ver una base de datos SQLite en la carpeta raíz del proyecto. Si lo inspeccionamos con DB Browser, veremos todos los campos relacionados con el modelo Photo.

Administración de archivos multimedia en desarrollo

La aplicación para compartir fotos depende en gran medida de los archivos multimedia. Se trata de compartir imágenes, ¿no?

Los archivos multimedia en Django son todos los archivos cargados por el usuario. Por ahora, vamos a configurar archivos multimedia en desarrollo, ya que solo interactuaremos con la aplicación a través del servidor local.

Para habilitar los archivos multimedia en desarrollo, creamos las variables MEDIA_URL y MEDIA_ROOT dentro del archivo de configuración. Además, necesitamos modificar los urlpatterns del proyecto general para servir archivos multimedia desde el servidor local.

Primero, necesitamos editar el archivo config/settings.py y agregar el siguiente código al final del archivo:

# config/settings.py

# Other settings ...

MEDIA_URL = '/media/'

MEDIA_ROOT = BASE_DIR / 'media/'

MEDIA_URL es la URL que maneja todos los medios cargados en la carpeta MEDIA_ROOT. En este caso, la URL de medios absoluta se vería así: http://localhost:8000/media/.

Por otro lado, MEDIA_ROOT es la ruta que apunta a la carpeta donde se colocarán todos los medios.

Recuerde que, dado que estamos usando la biblioteca pathlib, podemos concatenar rutas con /.

Podemos pensar en MEDIA_ROOT como el almacenamiento físico donde se cargarán las imágenes y, MEDIA_URL, como la URL que apunta a ese almacenamiento.

Si queremos que Django administre archivos multimedia, necesitaremos modificar las URL del proyecto:

# config/urls.py

# New imports
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    # Main app
    path('', include('photoapp.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Teniendo esto en cuenta, la URL absoluta de las fotos subidas será: http://localhost:8000/media/photos/. Esto se debe a que establecemos el atributo upload_to photos/.

Nota: puede ser peligroso aceptar archivos cargados por el usuario. Echa un vistazo a esta lista de consideraciones de seguridad.

Cuando trabajamos con una aplicación que está disponible públicamente, debemos tener cuidado con los archivos multimedia. Podríamos sufrir ataques DoS. Los usuarios también podrían cargar contenido malicioso, por lo que el enfoque recomendado es usar siempre una CDN para resolver este tipo de problemas.

Por ahora, puedes olvidarte de los problemas de seguridad, ya que estamos trabajando con un proyecto de desarrollo y el ImageField solo acepta un conjunto predeterminado de extensiones.

Puedes comprobar esas extensiones válidas ejecutando el siguiente código en el shell de Django (asegurándote de que tu venv está activado):

$ python manage.py shell

>>> from django.core.validators import get_available_image_extensions
>>> get_available_image_extensions()
['blp', 'bmp', 'dib', 'bufr', 'cur', 'pcx', 'dcx', 'dds', 'ps', 'eps', 'fit', 'fits', 'fli', 'flc', 'ftc', 'ftu', 'gbr', 'gif', 'grib', 'h5', 'hdf', 'png', 'apng', 'jp2', 'j2k', 'jpc', 'jpf', 'jpx', 'j2c', 'icns', 'ico', 'im', 'iim', 'tif', 'tiff', 'jfif', 'jpe', 'jpg', 'jpeg', 'mpg', 'mpeg', 'mpo', 'msp', 'palm', 'pcd', 'pdf', 'pxr', 'pbm', 'pgm', 'ppm', 'pnm', 'psd', 'bw', 'rgb', 'rgba', 'sgi', 'ras', 'tga', 'icb', 'vda', 'vst', 'webp', 'wmf', 'emf', 'xbm', 'xpm']

Probando modelos con Django Admin

Django admin es una interfaz incorporada donde los usuarios administrativos pueden realizar operaciones CRUD con los modelos registrados del proyecto.

Ahora que hemos creado el modelo de foto y configurado los archivos multimedia, es hora de crear nuestro primer objeto Photo a través de la página de administración.

Para hacer esto, tenemos que registrar el modelo Photo en la página de administración. Abramos la photoapp/admin.py, importemos el modelo de foto y pasémoslo como parámetro a la función admin.site.register:

# photoapp/admin.py

from django.contrib import admin
from .models import Photo # We import the photo model

# Register your models here.
admin.site.register(Photo)

A continuación, es el momento de crear un superusuario para poder acceder a la página de administración. Podemos hacer esto con el siguiente comando:

python manage.py createsuperuser

Username: daniel 
Email address: 
Password: 
Password (again): 
Superuser created successfully

Puede dejar al superusuario sin correo electrónico por ahora, ya que estamos usando el usuario de autenticación predeterminado.

Después de crear el superusuario, vaya al navegador y navegue hasta http://localhost:8000/admin.

Te redirigirá a la página de inicio de sesión, donde tendrás que rellenar tus credenciales (con las que creaste el usuario).

Tras introducir nuestras credenciales, tendremos acceso a un sencillo panel de control, donde podremos empezar a crear fotos. Simplemente haga clic en la sección Fotos y luego en el botón Agregar.

Así es como se ve rellenar los campos de creación.

Cargar una imagen se puede hacer simplemente arrastrando y soltando.

Después de presionar el botón Guardar, veremos un tablero con todas las fotos creadas.

Manejo de respuestas web con vistas

Hemos definido el esquema de la base de datos de una aplicación que funciona, e incluso hemos creado algunos objetos con el administrador de Django. Pero no hemos tocado la parte más importante de cualquier aplicación web: ¡la interacción con el usuario!

En esta sección, vamos a crear las vistas de la aplicación para compartir fotos.

En términos generales, una vista es una llamada de Python (clase o función) que toma una solicitud y devuelve una respuesta.

De acuerdo con la documentación de Django, debemos colocar todas nuestras vistas en un archivo llamado views.py dentro de cada aplicación. Este archivo ya se creó cuando iniciamos la aplicación.

Tenemos dos formas principales de crear vistas: usando vistas basadas en funciones (FBV) o vistas basadas en clases (CBV).

Los CBV son la mejor manera de reutilizar el código, aplicando el poder de la herencia de clases de Python a nuestras vistas.

En nuestra aplicación, usaremos vistas genéricas, que nos permiten crear operaciones CRUD simples heredando clases precompiladas de Django.

Antes de comenzar, importaremos todo lo que necesitamos para construir las vistas. Abra el archivo photoapp/views.py y pegue el siguiente código:

# photoapp/views.py
from django.shortcuts import get_object_or_404

from django.core.exceptions import PermissionDenied

from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView

from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin

from django.urls import reverse_lazy

from .models import Photo

Veamos lo que estamos importando aquí:

  • get_object_or_404 es un atajo que nos permite recuperar un objeto de la base de datos, evitando un error DoesNotExists y generando una excepción HTTP 404.
  • PermissionDenied genera una excepción HTTP 403 cuando se llama.
  • Las vistas generic prediseñadas nos ayudan a crear la funcionalidad CRUD con pocas líneas de código.
  • Usaremos LoginRequiredMixin y UserPassesTestMixin para afirmar que los usuarios tienen los permisos adecuados al acceder a una vista.
  • reverse_lazy se utiliza en los CBV para redirigir a los usuarios a una URL específica.
  • Necesitamos importar Photo para recuperar y actualizar las filas de la base de datos (objetos de foto).

Nota: puedes acceder al archivo views.py en GitHub.

Vistas de listas de fotos

La vista de lista genérica nos ayudará a mostrar muchos objetos de un modelo. Lo compararemos con DetailView más adelante.

En esta sección, vamos a construir dos vistas principales. PhotoListView pasa como contexto todas las fotos cargadas por cualquier usuario, y PhotoTagListView toma un slug de etiqueta como argumento para mostrar las fotos.

En el código siguiente se define la herencia de PhotoListView de ListView:

# photoapp/views.py

class PhotoListView(ListView):

    model = Photo     

    template_name = 'photoapp/list.html'

    context_object_name = 'photos'

En primer lugar, heredamos el ListView y, por lo tanto, recibimos todo el comportamiento de esa clase.

Recuerda, siempre puedes comprobar el código fuente de cualquier clase de Django en el repositorio oficial de GitHub.

Luego definimos el modelo desde el que estamos leyendo los datos, la plantilla que vamos a usar (construiremos el front-end más adelante) y el nombre del objeto de contexto que podemos usar para acceder a los datos en la plantilla.

Ahora, es el momento de declarar PhotoTagListView. Esta vista es un poco más compleja, ya que tenemos que jugar con los métodos get_queryset() y get_context_data()

# photoapp/views.py
class PhotoListView(ListView): ...

class PhotoTagListView(PhotoListView):

    template_name = 'photoapp/taglist.html'

    # Custom method
    def get_tag(self):
        return self.kwargs.get('tag')

    def get_queryset(self):
        return self.model.objects.filter(tags__slug=self.get_tag())

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["tag"] = self.get_tag()
        return context

Aquí, heredamos todos los atributos de PhotoListView. Eso significa que estamos usando el mismo model y context_object_name, pero estamos cambiando el template_name.

Esta vista puede parecer la misma que la anterior, excepto que se trata de métodos personalizados.

Estamos creando un método personalizado get_tag recibir el slug de la etiqueta de la respuesta que Django va a tomar y devolverlo. Lo hacemos de esta manera porque vamos a usar esa función en dos lugares.

El método get_queryset está configurado para devolver self.model.objects.all() de forma predeterminada. Lo hemos modificado para que devuelva solo los objetos de foto etiquetados con el slug pasado a la URL.

Por último, se modificó el get_context_data para devolver también la etiqueta pasada a la URL. Esto se debe a que lo mostraremos más adelante en una plantilla.

Vista de detalle de la foto

Esta vista es una DetailView que muestra todos los datos relacionados con una foto única. Esto incluye el título, la descripción y las etiquetas de la foto deseada:

# photoapp/views.py

class PhotoListView(ListView): ...
class PhotoTagListView(PhotoListView): ...

class PhotoDetailView(DetailView):

    model = Photo

    template_name = 'photoapp/detail.html'

    context_object_name = 'photo'

Hacemos más o menos el mismo proceso que hicimos con las vistas de lista. La única diferencia es que estamos devolviendo un solo objeto en lugar de muchos, y usando una plantilla diferente.

Crear vista de foto

Esta vista permite a los usuarios crear un objeto de foto solo si han iniciado sesión. No queremos que los usuarios anónimos puedan subir contenido a nuestra plataforma. ¡Eso sería aterrador!

La forma más sencilla de proteger esta funcionalidad con Django es crear una clase que herede de CreateView y LoginRequiredMixinLoginRequiredMixin comprueba si un usuario ha iniciado sesión. Si el usuario no ha iniciado sesión, se le redirige a la página de inicio de sesión (que crearemos más adelante):

# photoapp/views.py

class PhotoListView(ListView): ...
class PhotoTagListView(PhotoListView): ...
class PhotoDetailView(DetailView): ...

class PhotoCreateView(LoginRequiredMixin, CreateView):

    model = Photo

    fields = ['title', 'description', 'image', 'tags']

    template_name = 'photoapp/create.html'

    success_url = reverse_lazy('photo:list')

    def form_valid(self, form):

        form.instance.submitter = self.request.user

        return super().form_valid(form)

En esta vista, Django creará un formulario title título, descriptionimage y tags.

También estamos usando el atributo sucess_url. Los usuarios serán redirigidos al panel de fotos si la creación de la foto se realizó correctamente.

Si echamos un vistazo más de cerca al método form_valid, notaremos que está configurando al usuario que realiza la solicitud como remitente del formulario de foto.

Actualizar y eliminar vistas de fotos

Queremos que los usuarios puedan modificar o eliminar una foto solo si son ellos los que la envían.

El manejo de la autenticación condicional puede ser difícil si estamos usando CBV. Sin embargo, podemos hacer uso de TestMixins para realizar esta tarea.

Vamos a crear una mezcla de prueba UserIsSubmitter que compruebe si el usuario que está intentando actualizar o eliminar una foto realmente la envió:

# photoapp/views.py

class PhotoListView(ListView): ...
class PhotoTagListView(PhotoListView): ...
class PhotoDetailView(DetailView): ...
class PhotoCreateView(LoginRequiredMixin, CreateView): ...

class UserIsSubmitter(UserPassesTestMixin):

    # Custom method
    def get_photo(self):
        return get_object_or_404(Photo, pk=self.kwargs.get('pk'))

    def test_func(self):

        if self.request.user.is_authenticated:
            return self.request.user == self.get_photo().submitter
        else:
            raise PermissionDenied('Sorry you are not allowed here')

En primer lugar, hemos creado un método personalizado get_photo que devuelve un objeto Photo, con la clave principal especificada en la dirección URL. Si la foto no existe, genera un error HTTP 404.

A continuación, hemos definido la función de prueba. Solo devolverá true si el usuario ha iniciado sesión y es el remitente de la foto.

Si el usuario no ha iniciado sesión, generará una excepción PermissionDenied.

Por otro lado, PhotoUpdateView y PhotoDeleteView son elementos secundarios de la mezcla que creamos, pero también UpdateView y DeleteView respectivamente:

# photoapp/views.py

class PhotoListView(ListView): ...
class PhotoTagListView(PhotoListView): ...
class PhotoDetailView(DetailView): ...
class PhotoCreateView(LoginRequiredMixin, CreateView): ...
class UserIsSubmitter(UserPassesTestMixin): ...

class PhotoUpdateView(UserIsSubmitter, UpdateView):

    template_name = 'photoapp/update.html'

    model = Photo

    fields = ['title', 'description', 'tags']

    success_url = reverse_lazy('photo:list')

class PhotoDeleteView(UserIsSubmitter, DeleteView):

    template_name = 'photoapp/delete.html'

    model = Photo

    success_url = reverse_lazy('photo:list')         

PhotoUpdateView hereda la función de prueba de la mezcla UserIsSubmitter y la funcionalidad de actualización de UpdateView.

El atributo fields define los campos que el usuario podrá editar. No queremos que se cambie la imagen, ni la fecha de creación ni el remitente.

Por otro lado, PhotoDeleteView también hereda la función de prueba, pero elimina la foto en lugar de actualizarla.

Ambas vistas redirigen al usuario a la URL de la lista si todo ha ido bien.

Eso es todo por las vistas. Ahora, vamos a crear una aplicación de autenticación simple y completar el proyecto.

Patrones de URL

Ya casi llegamos. Ya hemos definido el esquema de la base de datos y cómo el usuario creará y actualizará las fotos. Veamos cómo manejar la configuración de URL de la aplicación para compartir fotos.

¿Recuerdas cuando creamos una variable urlpatterns vacía al inicio del proyecto? ¡Es hora de poblarlo!

Primero, importemos todas las vistas y funciones que necesitamos:

# photoapp/urls.py

from django.urls import path

from .views import (
    PhotoListView,
    PhotoTagListView,
    PhotoDetailView,
    PhotoCreateView,
    PhotoUpdateView,
    PhotoDeleteView
)

La función path recibe dos argumentos, route y view, y un argumento opcional, name, que se utiliza como parte del espacio de nombres:

# photoapp/urls.py
app_name = 'photo'

urlpatterns = [
    path('', PhotoListView.as_view(), name='list'),

    path('tag/<slug:tag>/', PhotoTagListView.as_view(), name='tag'),

    path('photo/<int:pk>/', PhotoDetailView.as_view(), name='detail'),

    path('photo/create/', PhotoCreateView.as_view(), name='create'),

    path('photo/<int:pk>/update/', PhotoUpdateView.as_view(), name='update'),

    path('photo/<int:pk>/delete/', PhotoDeleteView.as_view(), name='delete'),
]

Al explicar esta configuración, la variable app_name declara el espacio de nombres de la aplicación.

Eso significa que, ya sea que estemos usando la función reverse en las vistas o la etiqueta {% url %} en las plantillas, necesitaremos usar el siguiente espacio de nombres:

photo:<<url_name>>

Si quieres saber más sobre cómo funciona el despachador de URL de Django, no dudes en leer la documentación.

Sistema de autenticación

En este proyecto, vamos a usar el sistema de autenticación predeterminado Django.

Esto se debe a que nuestro principal objetivo es tener una aplicación funcional lo antes posible. Sin embargo, crearemos una aplicación personalizada, ya que queremos agregar la funcionalidad de registro al proyecto.

Al principio, creamos una aplicación users y hacemos el mismo proceso de instalación que hicimos con la photoapp:

python manage.py startapp users

# config/settings.py

INSTALLED_APPS = [
    ...

    # 3rd party apps
    'taggit',

    # Custom apps
    'photoapp',
    'users',
]

A continuación, creamos el archivo urls.py como lo hicimos con la aplicación de fotos:

cd users/
touch urls.py

A continuación, incluimos las URL del usuario en el proyecto general:

# config/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    # Main app
    path('', include('photoapp.urls')),
    # Auth app
    path('users/', include('users.urls')),

] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

A continuación, escribimos un SignUpView para permitir que el usuario se registre a través del sitio:

# users/views.py

from django.views.generic import CreateView

from django.contrib.auth import authenticate, login

from django.contrib.auth.forms import UserCreationForm

from django.urls import reverse_lazy

class SignUpView(CreateView):

    template_name = 'users/signup.html'

    form_class = UserCreationForm

    success_url = reverse_lazy('photo:list')

    def form_valid(self, form):
        to_return = super().form_valid(form)

        user = authenticate(
            username=form.cleaned_data["username"],
            password=form.cleaned_data["password1"],
        )

        login(self.request, user)

        return to_return

Esta vista es CreateView y funciona con el UserCreationForm integrado para crear un nuevo usuario.

Estamos usando el método form_valid para iniciar sesión en los usuarios antes de redirigirlos al panel de fotos.

Crearemos una vista de inicio de sesión porque queremos usar una plantilla personalizada para mostrar la página de inicio de sesión. Para ello, importaremos el LoginView integrado y lo heredaremos:

# Previous imports
from django.contrib.auth.views import LoginView

class SignUpView(CreateView): ...

class CustomLoginView(LoginView):

    template_name = 'users/login.html'

Por último, es el momento de crear el enrutamiento de URL:

# users/urls.py
from django.urls import path

from django.contrib.auth.views import LogoutView

from .views import SignUpView, CustomLoginView

app_name = 'user'

urlpatterns = [
    path('signup/', SignUpView.as_view(), name='signup'),
    path('login/', CustomLoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

Una vez más, estamos usando la variable app_name. Por lo tanto, el espacio de nombres de la aplicación de usuario sería el siguiente:

user:<<url_name>>

Estamos configurando tres URLs. El signup/ y login/ están usando las vistas personalizadas que creamos, pero la URL de logout/ está usando el LogoutView integrado de Django.

Antes de continuar, vamos a configurar las redirecciones de autenticación en el archivo config/settings.py:

# Other settings ...
USE_TZ = True

# Django Authentication
LOGIN_URL = 'user:login'
LOGIN_REDIRECT_URL = 'photo:list'

LOGOUT_REDIRECT_URL = 'photo:list'

Esto le dice a Django que la URL de inicio de sesión es la URL de inicio de sesión del usuario personalizado, y que cuando los usuarios inician sesión, deben ser redirigidos al panel de fotos.

La parte front-end

Después de construir el back-end (lo que el usuario no puede ver) con Django, es hora de construir el front-end (lo que el usuario sí ve).

Para ello, vamos a utilizar el lenguaje de plantillas Django y Bootstrap 5. Esto nos permite generar HTML de forma dinámica y producir una salida diferente en función del estado de nuestra base de datos. Podemos ahorrar mucho código trabajando con la herencia de plantillas. El uso de Bootstrap 5 significa que no usaremos archivos estáticos.

Escribir la plantilla base

En esta sección, vamos a crear el archivo base.html, que es la plantilla de la que heredarán todos los demás.

Para ello debemos cambiar la clave DIRS dentro de la variable TEMPLATES ubicada en el fichero de configuración:

# config/settings.py

TEMPLATES = [
    {
        # Options ..
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        # More options
    },
]

El comportamiento predeterminado de Django es buscar archivos de plantilla dentro de la carpeta templates/ de cada aplicación.

Por ejemplo, las plantillas de la aplicación para compartir fotos se pueden encontrar en photoapp/templates. Es la misma historia para la aplicación de usuarios (users/templates).

Al asignar la clave DIRS a [BASE_DIR / 'templates'] le estamos diciendo a Django que también busque plantillas dentro de una carpeta llamada templates.

Cree un directorio de templates en la raíz del proyecto (donde se encuentra el archivo manage.py) y toque las plantillas base.html y navbar.html:

ls
# manage.py

mkdir templates && cd templates
touch base.html navbar.html

Concluyendo las plantillas de nuestro proyecto se pueden encontrar en cualquiera de estos tres directorios:

.
├── photoapp
│   └── templates
│       └── photoapp
├── templates
└── users
    └── templates
        └── users

Recuerda que siempre puedes consultar la estructura del proyecto en el repositorio de GitHub.

Dentro de la plantilla base.html, vamos a configurar la estructura HTML básica, algunas metaetiquetas, enlaces a la CDN de arranque y bloques que usarán otras plantillas:

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Django Photo Sharing app</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0"
      crossorigin="anonymous"
    />

    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
      integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
      crossorigin="anonymous"
    />
  </head>
  <body>
    {% include 'navbar.html' %}

    <div class="container mt-4">
    {% block body %} 

    {% endblock body %}

    </div>

    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8"
      crossorigin="anonymous"
    ></script>
  </body>
</html>

La etiqueta {% include %} (como su nombre indica) incluye todo el código de la plantilla seleccionada dentro de base.html archivo.

Por lo tanto, todo el código presente dentro del navbar.html se colocará al inicio del cuerpo.

Nota: hay mucho HTML y Bootstrap aquí. Siéntete libre de copiarlo todo, ya que no es el enfoque principal del tutorial.

A continuación se muestra el código de plantilla HTML para la barra de navegación. Esta barra de navegación contendrá algo de lógica para mostrar un enlace a la página de inicio de sesión, en caso de que el usuario no haya iniciado sesión:

<!-- templates/navbar.html -->
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="{% url 'photo:list' %}">Photo App</a>
    <button
      class="navbar-toggler"
      type="button"
      data-bs-toggle="collapse"
      data-bs-target="#navbarTogglerDemo02"
      aria-controls="navbarTogglerDemo02"
      aria-expanded="false"
      aria-label="Toggle navigation"
    >
      <span class="navbar-toggler-icon"></span>
    </button>
    <div
      class="collapse navbar-collapse flex-row-reverse"
      id="navbarTogglerDemo02"
    >
      <ul class="navbar-nav">
        {% if user.is_authenticated %}

        <li class="nav-item">
          <a class="nav-link active" href="{% url 'photo:create' %}">Add a photo</a>
        </li>
        <li class="nav-item">
          <a class="nav-link active" href="#">Hi {{user.username}}</a>
        </li>
        {% else %}

        <li class="nav-item">
          <a href="{% url 'user:login' %}" class="btn btn-sm btn-danger"
            >Sign In</a
          >
        </li>
        {% endif %}
      </ul>
    </div>
  </div>
</nav>

Así es como se mostrará la plantilla cuando el usuario inicie sesión.

A continuación se muestra lo que se presenta cuando el usuario no ha iniciado sesión.

No te preocupes si recibes un error en tu navegador. Todavía no hemos creado las plantillas para compartir fotos.

Plantillas para compartir fotos

Vamos a escribir todos los archivos necesarios en la aplicación para compartir fotos. Esto incluye las plantillas utilizadas para realizar las operaciones CRUD.

Todas estas plantillas ampliarán la plantilla base.html y se ubicarán en el directorio photoapp/templates/photoapp.

Pero antes de trabajar con formularios en plantillas, usaremos crispy_forms de Django para estilizar nuestra aplicación:

pip install django-crispy-forms

Una vez más, crispy_forms es una aplicación de Django, y debemos incluirla en la lista INSTALLED_APPS:

# config/settings.py

INSTALLED_APPS = [
    ...

    # 3rd party apps
    'taggit',
    'crispy_forms',

    # Custom apps
    'photoapp',
    'users',
]

# Indicates the frontend framework django crispy forms will use
CRISPY_TEMPLATE_PACK = 'bootstrap4'

Usamos el paquete de plantillas de Bootstrap 4, porque las clases de formulario de Bootstrap son compatibles entre la 4ª y la 5ª versión (en el momento de escribir este artículo).

Es posible que recuerde que usamos los siguientes nombres de plantilla en la photoapp/views.py:

'photoapp/list.html' 
'photoapp/taglist.html' 
'photoapp/detail.html' 
'photoapp/create.html' 
'photoapp/update.html' 
'photoapp/delete.html'

Eso significa que todas estas plantillas se ubicarán en photoapp/templates/photoapp.

Para crear esta carpeta, vaya a la aplicación para compartir fotos y cree un directorio templates/, y dentro de él cree otra carpeta llamada photoapp/:

cd photoapp/
mkdir -p templates/photoapp/
cd templates/photoapp/

Ahora crea todas las plantillas que declaramos en las vistas:

touch list.html taglist.html detail.html create.html update.html delete.html

Plantillas de lista

El list.html heredará de la plantilla base.html y, por lo tanto, toda la estructura HTML aparecerá en el código fuente:

<!-- photoapp/templates/photoapp/list.html -->
{% extends 'base.html' %} 

{% block body %}

<div class="row">
  {% for photo in photos %}
  <div class="col-lg-3 col-md-4 col-xs-6">
    <a href="{% url 'photo:detail' photo.id %}" class="d-block mb-4 h-100">
      <img src="{{photo.image.url}}" class="img-fluid rounded" alt="{{photo.title}}" />
    </a>
  </div>
  {% endfor %}
</div>

{% endblock body %}

Estamos usando el bucle for de la etiqueta de plantilla, que itera sobre las fotos y las muestra con filas y columnas de Bootstrap.

No olvide crear varios objetos de foto en el administrador de Django.

Visite localhost:8000/ para ver cómo se ve la plantilla.

La plantilla taglist.html heredará de la list.html que acabamos de crear:

<!-- photoapp/templates/photoapp/taglist.html -->
{% extends 'photoapp/list.html' %}

{% block body %}

<div class="alert alert-primary">
    <h2 class="text-center">Photos with the tag {{tag}}</h2>
</div>

{{ block.super }}

{% endblock body %}

Solo estamos modificando un poco esta plantilla. Es por eso que estamos llamando a {{ block.super }} que contiene todo el código dentro del bloque body de la plantilla list.html.

Cree un par de objetos con el code de etiqueta antes de continuar.

Ve a localhost:8000/tag/code/, donde el código es el slug de la etiqueta.

Recuerde que la URL de la taglist tiene la siguiente forma:

'localhost://8000/tag/<slug:tag>/'

Aquí, <slug:tag> hace referencia al nombre de la etiqueta.

Plantilla de foto de detalle

Editemos la plantilla detail.html para poder ver nuestras fotos en detalle:

<!-- photoapp/templates/photoapp/detail.html -->
{% extends 'base.html' %} 

{% block body %}
<div class="mx-auto">
  <h1 class="text-center">{{ photo.title }}</h1>
  <p class="text-center fw-light">Uploaded on: {{photo.created}} <br> By {{photo.submitter.username}}</p>
  {% if user == photo.submitter %}
    <p class="text-center">
      <span><a href="{% url 'photo:update' photo.id %}" class="text-primary px-2">Update</a></span>
      <span><a href="{% url 'photo:delete' photo.id %}" class="text-danger px-2">Delete</a></span>
    </p>
  {% endif %}
</div>
<div class="row pb-5">
  <div class="col-md-8">
    <img src="{{photo.image.url}}" alt="" width="100%" />
  </div>
  <div class="col-md-4">
    <h4>More about this photo:</h4>
    <ul class="list-group list-group-horizontal-lg list-unstyled py-4">
      {% for tag in photo.tags.all %}
        <li><a href="{% url 'photo:tag' tag.slug %}" class="btn btn-sm list-group-item list-group-item-primary">{{tag.name}}</a></li>
      {% endfor %}
    </ul>
    <p>{{ photo.description }}</p>
  </div>
</div>

{% endblock body %}

Veamos cómo se ve la plantilla antes de profundizar en la funcionalidad. Sigue a localhost:8000/photo/1.

Aquí, estamos accediendo a las propiedades de la foto desde las plantillas a través de la notación de puntos. Esto se debe a que photo.submitter.username es igual a daniel.

Implementamos un poco de lógica para mostrar los enlaces para actualizar o eliminar la foto en caso de que el usuario también sea el remitente.

Finalmente, mostramos todas las etiquetas de la foto iterando sobre photo.tags.all.

Crear la plantilla de foto

La siguiente plantilla incluirá un formulario crujiente, para que no tengamos que mostrar los formularios manualmente. Django lo hará por nosotros:

<!-- photoapp/templates/photoapp/create.html -->
{% extends 'base.html' %}
{% load crispy_forms_tags %}

{% block body %}
<div class="mx-auto">
  <h1 class="mt-3 text-center">Add photo</h1>
</div>
<div class="form-group">
  <form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form|crispy }}
    <button type="submit" class="btn btn-success mb-3">Add Photo</button>
  </form>
</div>
{% endblock body %}

Cada vez que usamos formularios crujientes, necesitamos cargar las etiquetas con {% load crispy_forms_tags %}.

Es extremadamente importante incluir enctype="multipart/form-data" porque si no lo hacemos, los archivos no se subirán. Aquí hay una muy buena respuesta a las implicaciones de usarlo en formularios.

Cada formulario de Django debe incluir un {% csrf_token %} dentro. Puede obtener más información sobre esta etiqueta en la página «Protección contra la falsificación de solicitudes entre sitios«.

Fíjate en cómo simplemente mostramos el formulario con {{form|crispy}}. Si sabe qué son las tuberías en Linux, estamos haciendo exactamente eso redirigiendo el formulario proporcionado por la vista al filtro crispy.

Vaya a la URL de agregar foto para verificar si la foto se ha cargado.

Si todo ha ido bien, deberíamos ver la foto añadida en el salpicadero.

Actualizar y eliminar plantillas

Terminemos la aplicación para compartir fotos antes de dirigirnos a las plantillas de autenticación.

La siguiente plantilla de update es un formulario sencillo en el que el usuario puede actualizar el title, la description y las tags de la foto:

<!-- photoapp/templates/photoapp/update.html -->
{% extends 'base.html' %}
{% load crispy_forms_tags %}

{% block body %}
<div class="mx-auto">
  <h1 class="mt-3 text-center">Edit photo {{photo}}</h1>
</div>
<div class="form-group">
  <form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form|crispy }}
    <button type="submit" class="btn btn-success mb-3">Edit Photo</button>
  </form>
</div>
{% endblock body %}

Podemos ver cómo se ve en localhost:8000/photo/1/update.

También queremos dar a los usuarios la opción de eliminar una foto. Con la siguiente plantilla, pueden decidir eliminar la foto o no:

<!-- photoapp/templates/photoapp/delete.html -->
{% extends 'base.html' %} 

{% block body %}
<div class="form-group mx-auto">
  <h2 class="text-center">
    You are going to <span class="text-danger">delete</span>: "<i
      >{{ photo }}</i
    >
    "
  </h2>
  <p class="text-center">Are you sure, you want to delete the photo ?</p>
  <div class="form-group">
    <form
      action=""
      method="post"
      class="d-flex flex-column align-items-center justify-content-center"
    >
      {% csrf_token %}
      <div class="row">
        <div class="col">
          <a href="{% url 'photo:detail' photo.id %}" class="btn btn-primary"
            >Cancel</a
          >
        </div>
        <div class="col">
          <button type="submit" class="btn btn-danger">Delete</button>
        </div>
      </div>
      <p>This action is irreversible</p>
    </form>
  </div>
</div>

{% endblock body %}

La página de eliminación se vería así.

Si el usuario decide cancelar, se le redirigirá a la página de detalles de esa foto.

Plantillas de autenticación de usuario

El propósito de esta sección es escribir todas las plantillas relacionadas con la autenticación. Escribiremos las plantillas de signup.html y login.html.

Al igual que la aplicación para compartir fotos, todas las plantillas siguientes se ubicarán en una estructura de carpetas users/templates/users/.

Entra en la app de usuarios y crea las carpetas en las que se ubicarán las plantillas:

# Enter to the project root directory
cd ../../../

cd users/
mkdir -p templates/users/

Cree los archivos de plantilla de registro e inicio de sesión dentro de esa carpeta:

cd templates/users/
touch signup.html login.html

A continuación se muestra el código de plantilla para la plantilla signup.html:

<!-- users/templates/users/signup.html -->
{% extends 'base.html' %} 
{% load crispy_forms_tags %}
{% block body %}
<div class="mx-auto">
  <div class="form-group">
    <form action="" method="post">
      {% csrf_token %} 
      {{ form|crispy }}
      <button type="submit" class="btn btn-danger w-100 my-3">Create account</button>
    </form>
  </div>
  {% comment %}  Already Registered {% endcomment %}
  <div class="text-center w-100">
    <p class="text-muted font-weight-bold">
      Already Registered?
      <a href="{% url 'user:login' %}" class="text-primary ml-2"> Login </a>
    </p>
  </div>
</div>
{% endblock body %}

Podemos comprobarlo en el navegador en localhost:8000/users/signup.

Por último, pero no menos importante, escribe la plantilla de inicio de sesión:

<!-- users/templates/users/login.html -->
{% extends 'base.html' %} 
{% load crispy_forms_tags %}

{% block body %}

<div class="mx-auto">
  <div class="form-group">
    <form action="" method="post">
      {% csrf_token %} 
      {{ form|crispy }}
      <button type="submit" class="btn btn-danger w-100 my-3">Sign in</button>
    </form>
  </div>
  {% comment %}  Already Registered {% endcomment %}
  <div class="text-center w-100">
    <p class="text-muted font-weight-bold">
      Don't have an account?
      <a href="{% url 'user:signup' %}" class="text-primary ml-2">Create account</a>
    </p>
  </div>
</div>
{% endblock body %}

Las plantillas de Django nos permiten ahorrar mucho tiempo al reutilizar el mismo HTML varias veces. Imagínate cuánto tiempo pasarías copiando y pegando el mismo HTML una y otra vez.

¡Perfecto! Ahora tienes una aplicación completamente funcional. Intenta usarlo, modificarlo o incluso ampliar su funcionalidad.

Resumiendo

¡Felicidades! Has creado un proyecto full-stack desde cero.

Django es el framework web de Python más utilizado. Le permite crear rápidamente aplicaciones web complejas.

Tiene muchas características integradas que aceleran el proceso de desarrollo, como la representación de plantillas del lado del servidor, las vistas basadas en clases y los formularios de modelo.

Django también ofrece varios paquetes de terceros que te dan la opción de usar la aplicación de otra persona. A modo de ejemplo, el proyecto trabaja con Django taggit y Django crispy forms.

En este tutorial, cubrimos lo siguiente:

  • Operaciones CRUD de Django
  • el sistema de autenticación integrado de Django
  • cómo administrar archivos multimedia en Django
  • usando Django taggit para clasificar el contenido
  • implementación de formularios de Django con crispy forms
  • escribir plantillas de Django con Bootstrap 5

La mejor manera de seguir aprendiendo y avanzando es aplicar los conocimientos adquiridos a proyectos nuevos y desafiantes. ¡Buena suerte!

Preguntas frecuentes sobre la aplicación para compartir fotos Django

¿Cómo puedo personalizar la aplicación para compartir fotos de Django para que se adapte a mis necesidades?

Django es un framework altamente personalizable. Puede modificar el archivo settings.py de la aplicación para adaptarlo a sus necesidades. Por ejemplo, puede cambiar la MEDIA_URL y la MEDIA_ROOT para especificar dónde debe almacenar Django los archivos cargados. También puede personalizar los modelos, las vistas y las plantillas de la aplicación. Por ejemplo, puede modificar el modelo de foto para agregar nuevos campos, cambiar la vista de lista de fotos para modificar la forma en que se muestran las fotos o editar la plantilla upload.html para cambiar la apariencia del formulario de carga.

¿Cómo puedo compartir fotos con la aplicación para compartir fotos Django?

Para compartir fotos con la aplicación para compartir fotos Django, primero debe cargar las fotos. Puede hacerlo haciendo clic en el botón ‘Cargar’ en la página de inicio de la aplicación. Una vez que se carguen las fotos, se mostrarán en la página de inicio. A continuación, puede compartir las fotos copiando la URL de la foto y enviándola a otras personas.

¿Puedo usar la aplicación para compartir fotos Django para compartir videos?

La aplicación para compartir fotos Django está diseñada principalmente para compartir fotos. Sin embargo, con algunas modificaciones, también puede usarlo para compartir videos. Deberá modificar el modelo de foto para aceptar archivos de vídeo y cambiar las vistas de carga y visualización para controlar los vídeos.

¿Cómo puedo instalar la aplicación para compartir fotos Django?

Para instalar la aplicación para compartir fotos Django, debe tener Python y Django instalados en su sistema. A continuación, puede clonar el repositorio de la aplicación desde GitHub e instalar los paquetes necesarios mediante pip. Después de eso, puede ejecutar la aplicación usando el comando runserver de Django.

¿Cómo puedo mejorar el rendimiento de la aplicación para compartir fotos Django?

Hay varias formas de mejorar el rendimiento de la aplicación para compartir fotos Django. Una forma es usar el marco de almacenamiento en caché de Django para almacenar en caché los resultados de las consultas a la base de datos. Otra forma es usar una CDN para servir archivos estáticos. También puede optimizar las consultas de su base de datos y utilizar la función de paginación de Django para limitar el número de fotos que se muestran en una página.

¿Puedo usar la aplicación para compartir fotos Django en dispositivos móviles?

Sí, la aplicación para compartir fotos Django es responsiva y se puede usar en dispositivos móviles. Sin embargo, es posible que la interfaz de la aplicación no esté optimizada para pantallas pequeñas. Puede modificar las plantillas de la aplicación para mejorar la experiencia del usuario móvil.

¿Cómo puedo agregar botones para compartir en redes sociales a la aplicación para compartir fotos de Django?

Para agregar botones para compartir en redes sociales a la aplicación para compartir fotos de Django, puede usar el lenguaje de plantillas de Django para incluir el código HTML y JavaScript necesario. También puedes usar un paquete de Django como django-social-share para simplificar el proceso.

¿Cómo puedo proteger la aplicación para compartir fotos Django?

Django viene con muchas funciones de seguridad integradas. Por ejemplo, escapa automáticamente el HTML de las plantillas para evitar ataques de secuencias de comandos entre sitios. También puede usar el sistema de autenticación de Django para restringir el acceso a ciertas partes de la aplicación.

¿Puedo usar la aplicación para compartir fotos Django para vender fotos?

La aplicación para compartir fotos Django no está diseñada para vender fotos. Sin embargo, con algunas modificaciones, puede agregar la funcionalidad de comercio electrónico a la aplicación. Tendrías que añadir un carrito de la compra, una pasarela de pago y una forma de entregar las fotos a los compradores.

¿Cómo puedo contribuir a la aplicación para compartir fotos de Django?

La aplicación para compartir fotos Django es un proyecto de código abierto y las contribuciones son bienvenidas. Puedes contribuir informando de errores, sugiriendo nuevas características, mejorando la documentación o enviando solicitudes de incorporación de cambios con cambios en el código.

Laravel vs CodeIgniter: una comparación completa
Cómo crear entornos virtuales en Python