miércoles, 22 de marzo de 2023

14.- Creación del primer servicio. Subir y Visualizar una imagen en la base de datos.

La aplicación que vamos a crear, la vamos a registrar también en el panel de administración para poder actualizar los datos o servicios desde el propio panel de administración. ¿Para que sirve esto?, pues por ejemplo si tenemos un restaurante, el propietario podrá subir el menú de cada día a través del panel de administración sin tener que modificar el código de las páginas web o mandárselo hacer al programador. 

Empezaremos creando una nueva aplicación para la zona de servicios de nuestro proyecto. En vez de elaborar una plantilla para la URL de servicios vamos a crear una nueva aplicación. Recordar que tenemos un Proyecto llamado "PracticaDjango" donde ya tenemos una aplicación llamada "Proyecto_web_app" y donde crearemos una segunda app para la zona de servicios llamada "Servicios".

Creemos, pues, la nueva aplicación:

$ python manage.py startapp Servicios
esquema directorios de las aplicaciones


Lo siguiente que tenemos que hacer es registrar esta nueva aplicación dentro del archivo settings.py de nuestro proyecto, dentro de nuestras aplicaciones.

PracticaDjango/PracticaDjango/settings.py

...
INSTALLED_APPS = [
    # Mis aplicaciones.
    'Proyecto_web_app',
    'Servicios',
    # Aplicaciones de terceros.
    'bootstrap5',
    # Aplicaciones por defecto de Django
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',      
]
...
Tenemos ahora que registrar en la base de datos de Django todas las características que tendrán esos servicios (titulo, contenido, imagen etc). Para esto tenemos que crear un modelo. Iremos al directorio de nuestra nueva aplicación 'Servicios' y entramos en el archivo models.py. Crearemos una clase que recoja los datos de esos servicios de la siguiente forma:

PracticaDjango/Servicios/models.py

from django.db import models

# Create your models here.
class Servicio(models.Model):
    titulo = models.CharField(max_length=50)
    # Django permite especificar el tamaño máximo del campo - max_length
    contenido = models.CharField(max_length=50)
    imagen = models.ImageField()
    # Para que automaticamente actualice las fechas usamos el argumento:
    # auto_now_add = True para guardar la fecha cuando se cree el registro.
    created = models.DateTimeField(auto_now_add=True)
    # auto_now = True para cuando se guarde o actualice el registro.
    updated = models.DateTimeField(auto_now=True)
    # Model meta Options - https://docs.djangoproject.com/en/4.1/topics/db/models/#meta-options
    class Meta:
        verbose_name = 'servicio ofrecido'
        verbose_name_plural = 'servicios ofrecidos'
        # Se utilizan para personalizar el nombre del modelo que se muestra
        # en el panel de administración.
    
    def __str__(self):
        # Para que nos devuelva el título del servicio.
        return self.titulo
A parte de los campos titulo, contenido e imagen puedes ver que hay otros dos campos que se suelen incluir y que se usan mucho en Django porque después son muy útiles para ordenar por fecha de creación y actualización. (created y updated)

También vamos a usar la clase Meta para poder usar verbose_name y verbose_name_plural. Los nombres que hemos especificado se usarán o serán los que aparezcan en el panel de administración de Django. En vez de usar el nombre que especificamos para la clase "Servicio", se usará el verbose_name de ese modelo para crear mensajes que sean menos "mecánicos". Y lo mismo para el plural de ese nombre. 

Antes de ejecutar las instrucciones que crearán la base de datos, como vamos a usar un campo que contendrá imágenes, es necesario instalar la librería Pillow que nos sirve para manipular imágenes.
$ pip install Pillow

Y como hemos visto ya, siempre que se crea o modifica un modelo hay que ejecutar las siguientes instrucciones:
$ python manage.py makemigrations
$ python manage.py migrate
Y con estos ya esta creada la tabla en la base de datos de nuestro proyecto. 

Para poder acceder al panel de administración si no lo hemos hecho ya antes tenemos que crear un superusuario. Esto se realiza con la instrucción:
$ python manage.py createsuperuser
A continuación tenemos que ir al archivo admin.py de la aplicación y registrarla. De la clase que acabamos de crear tenemos que importar su clase. Como esta en el mismo directorio usaremos el punto para importar la clase:

PracticaDjango/Servicios/admin.py

from django.contrib import admin
from .models import Servicio

# Register your models here.
# antes de registrar el servicio ponemos lo siguiente
# para que aparezcan los campos created y updated que por defecto
# no son visibles.
class ServicioAdmin(admin.ModelAdmin):
    readonly_fields = ('created', 'updated')

admin.site.register(Servicio, ServicioAdmin)

Si ejecutamos el servidor y accedemos a la URL admin  y dentro de servicios ofrecidos pulsamos en +Add veremos la siguiente imagen:


panel de administrador para crear servicios



Y ya dentro del panel de administración podemos crear el primer servicio que ofrezca nuestra empresa, con su titulo, contenido y una imagen. Los campos created y updated se pondrán automáticamente al pulsar el botón SAVE. 

Antes de continuar si queremos cambiar el idioma del panel de administración, al español por ejemplo, tenemos que ir al archivo settings.py del proyecto y buscar el apartado LANGUAGE_CODE y cambiarlo.

PracticaDjango/PracticaDjango/settings.py

...
# Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/

#LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'es-eu'
...

Hecho este inciso, volvemos a donde lo dejamos y vamos a agregar un primer servicio desde el panel de administración. Como es nuestro primer servicio vamos a crear uno de ejemplo:

creando un primer servicio


Si todo ha ido bien en la parte superior saldrá un mensaje de que el "servicio fue creado correctamente".

Y si entramos en el servicio creado podemos ver la siguiente pantalla.

servicio creado pero que no carga la imagen

El PROBLEMA radica en que si pulsas sobre el archivo de la imagen nos mostrará un error que básicamente lo que nos dice es que no se encuentra la imagen. Lo que esta ocurriendo es que Django por defecto no está preparado para mostrar contenido multimedia en modo de desarrollo. Tendremos que modificar su configuración para conseguirlo.

Además si miras el directorio del proyecto verás que la imagen subida Django la pone en la raiz del directorio lo cual no es muy práctico para tener el proyecto ordenado.

Vamos a ordenarlo todo un poco. En la raíz del proyecto crearemos una carpeta llamada 'media'. Y ahora tenemos que decirle a Django que busque los archivos multimedia en esta carpeta en concreto. Esto lo hacemos desde el archivo settings.py. Iremos al final del archivo e incluiremos las siguientes instrucciones:

PracticaDjango/PracticaDjango/settings.py

...
# Para poder ver y organizar el contenido multimedia que no es propiamente de la página
# Url pública de las imágenes, video, etc. que aparecerá en la barra del navegador
MEDIA_URL = 'media/'
# Donde debe buscar las imágenes, sonidos, videos etc.
MEDIA_ROOT = BASE_DIR/'media'

Esto permitirá a Django administrar la carga de archivos y servir archivos multimedia. MEDIA_URL es la URL base utilizada para servir los archivos multimedia cargados por los usuarios. MEDIA_ROOT es la ruta local donde residen. Los directorios y Las URL de los archivos se crean dinámicamente anteponiendo la ruta del proyecto o la URL del medio.

Con MEDIA_URL ='media/' queremos decir que la URL pública para nuestros archivos media será la que hemos definido 'media/'. Y una vez que tenemos definida esa URL pública, con MEDIA_ROOT = BASE_DIR/'media' le decimos donde tiene que buscarlo localmente.

Dentro de la carpeta media, para tenerlo todo más ordenado, deberíamos tener una jerarquía de subcarpetas para guardar los archivos media de cada una de las aplicaciones que vamos creando. Recuerda que hasta ahora tenemos dos creadas "Proyecto_web_app" y "Servicios".

 Lo ideal es que al registrar en la base de datos cada nuevo servicio, estos archivos se subieran automáticamente a la subcarpeta correspondiente. Y esto ¿Cómo lo conseguimos? Pues nos tenemos que ir al archivo models.py de la aplicación que queramos, en este caso de la aplicación 'Servicios' y simplemente en el campo de la base de datos donde configuramos el campo "ImageField" añadimos la opción "upload_to" y el nombre del subdirectorio correspondiente que queramos, que creará automáticamente Django dentro del directorio 'media' que se encuentra en la raiz del proyecto,  y hecho esto cuando registremos la imagen se guardará en este subdirectorio dentro de 'media' como ya he dicho, sin que nosotros tengamos que crear el subdirectorio.


PracticaDjango/Servicios/models.py

class Servicio(models.Model):
    titulo = models.CharField(max_length=50)
    # Django obliga a especificar el tamaño maximo del campo - max_length
    contenido = models.CharField(max_length=50)
    imagen = models.ImageField(upload_to = 'Servicios')
    # Para que automaticamente actualice las fechas usamos el argumento
    # auto_now_add = True
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now_add=True)
La estructura del directorio quedaría algo tal que así, después de subir la imagen.

directorio del programa de Django

Ya solo nos falta registrar la URL pública que hemos creado, en archivo URLS.py del proyecto, para que la tenga como URL de búsqueda.

Vamos a importar el archivo 'settings' para tener disponible tanto 'MEDIA_URL' y 'MEDIA_ROOT' y también tendremos que importar los archivos 'static'. Después de hecho esto solo nos queda agregar a URL_PATTERS la nueva URL de estos archivos estáticos:

PracticaDjango/PracticaDjango/urls.py

from django.contrib import admin
from django.urls import path, include
# Para registrar los archivos de las imagenes y poder verlas
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('Proyecto_web_app.urls'))
]
if settings.DEBUG:
    urlpatterns+=static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Hemos añadido el condicional a la función para servir archivos multimedia con el servidor de desarrollo de Django cuando estamos en modo de desarrollo.

Esta función condicional está disponible para desarrollo, pero NO DEBEMOS
USARLA EN PRODUCCIÓN. Django es muy ineficiente sirviendo archivos
estáticos. NUNCA sirvas tus archivos con Django en un entorno de Pro-
ducción. Aprenderemos como servir archivos estáticos en entornos de 
Producción más adelante.

y ahora si. Borramos el registro de prueba que habíamos creado. Lo volvemos a crear de nuevo. La imagen ahora estará en /media/Servicios. Si vamos de nuevo al panel de administración, pinchamos en 'Servicios Ofrecidos' y pinchamos en la imagen, Django nos la mostrará correctamente.

Ahora que Django nos muestra correctamente las imágenes, vamos a ver como mostrar toda esa información en la página de servicios que habiamos creado

Lo primero que tenemos que hacer es ir al archivo views.py de la aplicación que muestra las páginas de nuestra aplicación "Proyecto_web_app" y localizar la vista dedicada a los servicios. Lo único que hace esta vista es un renderizado de la plantilla servicios. Pero como ahora le vamos a pasar los servicios que hemos registrado en la base de datos, tenemos que importarlos primero y después decirle a nuestra vista de servicios que los muestre en esa plantilla, uno debajo de otro.

PracticaDjango/Proyecto_web_app/views.py

from django.shortcuts import render, HttpResponse
# Para cargar los servicios y mostrarlos en la página web
from Servicios.models import Servicio

# Create your views here.

def home(request):
    return render(request, 'Proyecto_web_app/inicio.html')

def servicios(request):
    # Para importar todos los servicios que creamos en el panel de admon.
    datos = Servicio.objects.all()
    return render(request, 'Proyecto_web_app/servicios.html', {'servicios': datos})

...
Después iremos a nuestro template y tendremos que decirle que nos muestre todos los servicios que hemos creado. Esto lo haremos recorriéndolos con un bucle for.

PracticaDjango/Proyecto_web_app/templates/Proyecto_web_app/servicios.html

<!--Cargamos la plantilla base-->
{% extends "Proyecto_web_app/base.html" %}

<!--Cargamos el contenido estático-->
{% load static %}

<!-- Establecemos el titulo de la página -->
{% block title %}Servicios{% endblock %}

<!-- Definimos su contenido -->
{% block content %}
    <h2>Esta es la página de SERVICIOS.</h2>
    <!--Para recorrer los servicios que vayamos creando-->
{% for servicio in servicios %}
<div>
    <p>
        <h2>{{servicio.titulo}}</h2>
        <p>{{servicio.contenido}}</p>
        <p>
            <img src="{{servicio.imagen.url}}">
        </p>
    </p>
</div>
{% endfor %}

{% endblock %}
y con esto si iniciamos el servidor y entramos en la url de servicios desde el navegador web ya nos tendría que mostrar los servicios ofrecidos.





No hay comentarios:

Publicar un comentario