domingo, 8 de enero de 2023

7.- Django - Bases de datos. PostgreSQL en local.

Como ya comentamos Django utiliza por defecto Sqlite3 como gestor de la base de datos. Eso está muy bien para proyectos personales o de poco tamaño. Sin embargo para proyectos más profesionales que necesiten más rendimiento podemos usar otro tipo, como por ejemplo PostgreSQL. 

Para poder trabajar con PostgreSQL vamos a necesitar una interfaz que nos permita gestionar este gestor de base de datos. 

Nos vamos a la página oficial, a la página de descargas y seleccionamos la versión, que sea acorde con nuestro sistema operativo. Yo estoy trabajando con una versión Ubuntu de Linux, así que seguiré los pasos que dicen en la página para instalarlo. Lógicamente tu usa las instrucciones que sean acordes a tu sistema operativo.

# Instalamos la aplicación.
$ sudo apt-get install postgresql postgresql-contrib

# Entramos en la consola de postgresql
$ sudo su - postgres
postgres@lenovo:~$ psql
psql (14.5 (Ubuntu 14.5-0ubuntu0.22.04.1))
Type "help" for help.

# Creamos un usuario y su contraseña. 
# En el ejemplo usuario = "chemaHG" y contraseña="%gH345bPQ"
postgres=# create user chemahg with password '%gH345bPQ';
CREATE ROLE

# Damos permisos de master al usuario recien creado.
postgres=# alter user chemahg with superuser;
ALTER ROLE

# Creamos una base de datos de ejemplo. Se pude poner el nombre que se quiera.
# En el ejemplo tutorial_db
postgres=# create database tutorial_db owner chemahg;
CREATE DATABASE

# Para listar las bases de datos que tenemos hasta el momento:
postgres=# \l
postgres=# \list

# Para salir del terminal de postgresql y volver al terminal.
postgres-# \q
postgres@lenovo:~$ exit
logout

Para manejar las bases de datos de PostgreSql de forma visual podemos instalar el gestor pgAdmin.

En la pagina de pgAdmin podemos ver las formas de instalación para las diferentes plataformas.

En Ubuntu es seguir las instrucciones que nos ponen en la página de descargas, que son bastante largas.

# Si no tienes el programa curl para linux instalarlo.
$ sudo apt-get install curl

# Instalar la clave pública para el repositorio (si no lo hicimos 
previamente()
curl -fsS https://www.pgadmin.org/static/packages_pgadmin_org.pub | sudo gpg --dearmor -o /usr/share/keyrings/packages-pgadmin-org.gpg

# Crear el archivo de configuración del repositorio.
sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/packages-pgadmin-org.gpg] https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/$(lsb_release -cs) pgadmin4 main" > /etc/apt/sources.list.d/pgadmin4.list && apt update'

# Ahora hay que elegir entre uno de los tres modos de instalación. Yo voy a
instalar la versión para escritorio. 

# Install for both desktop and web modes:
sudo apt install pgadmin4

# Install for desktop mode only:
sudo apt install pgadmin4-desktop

# Install for web mode only: 
sudo apt install pgadmin4-web
Si instalas la versión web hay que configurar el servidor con:
sudo /usr/pgadmin4/bin/setup-web.sh
Ahora hay que cambiar algunas cosas en la configuración. Buscamos la aplicación y la lanzamos (es un icono con un elefante azul). Nada más comenzar el programa nos pedirá que instalemos una contraseña maestra. Como el usuario que creamos antes está configurado como master podemos poner la misma.

Luego le damos a crear un nuevo servidor.

En la primera pantalla donde pone "name", ponemos el nombre que queramos para este servidor. Yo le pondré "mibase"



En el apartado "Connection"



Donde pone Host name/addres ya que estamos trabajando en local ponemos "localhost" y rellenamos también los campos Username y Password con la contraseña del usuario que creamos previamente.

Le damos a guardar y ya deberíamos tener funcionando la base de datos. Podemos ver la que creamos previamente llamada tutorial-db y desde aquí podemos crear la base de datos usando lenguaje SQL o también podemos usar el entorno contextual para manejar la base.




Ahora viene lo importante. Tenemos que conectar esta base que hemos creado "tutorial_db" con DJANGO. Para poder hacer esto necesitamos tener instalada una librería que por defecto no esta instalada en DJANGO que es psycogp

Lo instalamos con:

$ pip3 install psycopg2-binary
Collecting psycopg2-binary
  Downloading psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 6.2 MB/s eta 0:00:00
Installing collected packages: psycopg2-binary
Successfully installed psycopg2-binary-2.9.5
Luego tenemos que ir a Django y dentro del directorio de la carpeta de trabajo buscamos el archivo settings.py (en mi caso dentro de la carpeta tiendaVirtual) y buscamos la clave que gestiona la conexión con la base de datos. Por defecto tenemos la de sqlite, que debería ser algo así:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }

y tenemos que sustituirla por esta:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'tutorial_db',
        'USER': 'chemahg',
        'PASSWORD': 'Ademar74',
        'HOST': '127.0.0.1',
        'DATABASE_PORT': '5432',
    }
}

En donde:

'ENGINE' es el motor de conexión de la base de datos que puedes usar el que aparece en el ejemplo de arriba y si no te funciona usar este otro:

'django.db.backends.postgresql_psycopg2'

'NAME' es el nombre que le hemos puesto a la base de datos que hemos creado al principio.

'USER' es el nombre del usuario que creamos para gestionar la base de datos.

'PASSWORD' es la contraseña de ese usuario.

'HOST' es la dirección de la base de datos local, se pude poner '127.0.0.1' o 'localhost'

'DATABASE_PORT' es el puerto por defecto que usa la aplicación (5432)

Una vez realizado lo anterior tenemos que hacer los comandos que siempre hemos de realizar cuando modifiquemos la estructura o tipo de la base de datos.

(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py makemigrations
No changes detected
# No se detectan cambios porque no se ha modificado la estructura de la base de datos.
(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, gestionPedidos, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying gestionPedidos.0001_initial... OK
  Applying sessions.0001_initial... OK

Si todo esta ok ya están las tablas creadas en la base de datos. Podemos ir al gestor de escritorio de pgadmin y entrando en la base de datos /databases/tutorial_db/Schemas/tables/ y ahí estarán las tablas y campos que hemos creado.

Vamos a introducir un registro en la tabla de clientes. Estando en consola abrimos el Shell de Django. Lo primero que tenemos que hacer es importar el modelo y como voy a introducir clientes, le decimos que importe clientes. Una vez importado, creamos una variable con el nombre que queramos. Luego propiedad igual a valor para introducir los datos del cliente. Después utilizamos la variable con el método .save() para ejecutar la instrucción.

(miEntorno) ~/Cursos/DJANGO/tiendaVirtual$ python manage.py shell
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from gestionPedidos.models import Clientes
>>> cli1 = Clientes(nombre="Pepe", direccion="Calle Avellana", telefono=684000000, email="turron@mailinator.com")
>>> cli1.save()
y si vas al gestor de escritorio de PostgreSQL  ya debería haberse creado el registro:


nuevo registro visto a traves del administrador de escritorio de postgresql




Realizar consultas de tipo SELECT con DJANGO usando criterios.


Lo que vamos a ver es válido tanto para postgreSQL como para cualquier otro gestor de base de datos. Necesitamos incluir algunos datos para poder realizar las búsquedas. Lo haremos como hemos visto anteriormente o a través del gestor del escritorio. Voy a incluir en la base de datos unos cuantos artículos. 

Después vamos a ver como buscar registros basándonos en un campo en concreto. Como los artículos que tengo almacenados son videojuegos, voy a buscar los elementos que pertenezcan al elemento "Acción" dentro del campo "categoría".

En lenguaje SQL si usamos pgAdmin4 la instrucción sería:

SELECT *
FROM public."gestionPedidos_articulos"
WHERE categoria='Acción'

SALIDA:

1	"the last of us II"	        "Acción"	10
4	"Read Dead Redeption II"	"Acción"	19
Trasladando esto a código de DJANGO

(miEntorno) ~/Cursos/DJANGO/tiendaVirtual$ python manage.py shell
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from gestionPedidos.models import Articulos
>>> Articulos.objects.filter(categoria="Acción")
<QuerySet [<Articulos: Articulos object (1)>, <Articulos: Articulos object (4)>]>
La clave está en el método filter (equivale a WHERE en SQL) y dentro ponemos el criterio o criterios separados por coma. Al pulsar enter nos da el resultado que quizás no es el que nosotros esperamos. Nos devuelve QuerySet con una lista en la que hay dos objetos que se corresponden con lo que le hemos pedido. El que tiene el id 1 y el que tiene el id 4. Pero nosotros seguramente esperábamos que apareciera otro tipo de información, como el nombre del juego, su categoría y su precio. 

Para poder hacer esto tenemos que decirle a nuestro código python que trasforme la respuesta a una cadena de caracteres y eso lo hacemos con la función __str__().

Lo vamos a introducir en el archivo models.py dentro de la categoría "Articulos"

gestionPedidos/models.py

...
class Articulos(models.Model):
    nombre = models.CharField(max_length=30)
    categoria = models.CharField(max_length=20)
    precio = models.IntegerField()

    class Meta:
        ordering = ["nombre"]

    def __str__(self):
        return f"id {self.id}, nombre: {self.nombre}, categoría: {self.categoria}, precio: {self.precio}"
...

Hecho esto no valdría con volver a repetir la instrucción. SIEMPRE que modifiquemos algo en models.py hay que volver a realizar las migraciones pertinentes.

(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py makemigrations
No changes detected
(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, gestionPedidos, sessions
Running migrations:
  No migrations to apply.
(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py shell
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> Articulos.objects.filter(categoria="Acción")
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'Articulos' is not defined
>>> from gestionPedidos.models import Articulos
>>> Articulos.objects.filter(categoria="Acción")
<QuerySet [
<Articulos: id 1, nombre: the last of us II, categoría: Acción, precio: 10>, 
<Articulos: id 4, nombre: Read Dead Redeption II, categoría: Acción, precio: 19>
]>

Ahora si podemos leer sus valores a través de la lista que se nos proporciona.


Como utilizar más de un criterio.


Por ejemplo localizar todos los registros cuya categoría sea "Acción" y que tengan un precio superior a 15 €. En lenguaje SQL sería algo parecido a:

SELECT *
FROM public."gestionPedidos_articulos"
WHERE categoria='Acción' AND precio>15

En principio estaríamos tentados de escribir este código para obtener la consulta:

chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ source ../miEntorno/bin/activate
(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py shell
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from gestionPedidos.models import Articulos
>>> Articulos.objects.filter(categoria="Acción", precio>15)
  File "<console>", line 1
    Articulos.objects.filter(categoria="Acción", precio>15)
                                                          ^
SyntaxError: positional argument follows keyword argument
Pero esto nos provoca un error porque el símbolo > o < están siendo utilizados constantemente por el Shell de Python (no nos pasará esto en otros entornos más adelante) con lo que tenemos que sustituirlos por otros:

> nombreCampo__gte
< nombreCampo__lte

Para mayor que:

>>> Articulos.objects.filter(categoria="Acción", precio__gte=15)
<QuerySet [<Articulos: id 4, nombre: Read Dead Redeption II, 
categoría: Acción, precio: 19>]>
Para menor que:

>>> Articulos.objects.filter(categoria="Acción", precio__lte=15)
<QuerySet [<Articulos: id 1, nombre: the last of us II, categoría: Acción, precio: 10>]>
Para horquillas de valores podemos combinar los comandos anteriores. Por ejemplo un juego de acción mayor de 5 euros y menor de 40:

>>> Articulos.objects.filter(categoria="Acción", precio__gte=5, precio__lte=40)
<QuerySet [<Articulos: id 1, nombre: the last of us II, categoría: Acción, precio: 10>, 
<Articulos: id 4, nombre: Read Dead Redeption II, categoría: Acción, precio: 19>]>
o también utilizar el comando __range()

>>> Articulos.objects.filter(categoria="Acción", precio__range=(5,40))
<QuerySet [<Articulos: id 1, nombre: the last of us II, categoría: Acción, precio: 10>, 
<Articulos: id 4, nombre: Read Dead Redeption II, categoría: Acción, precio: 19>]>


Existen muchos más referencias para campos de búsqueda que usan los dos guiones bajos __ que puedes encontrar en la documentación de Django sobre como realizar consultas o también una lista completa en aquí o en la documentación de Django en QuerySet Api reference


Artículos ordenados por un campo. (ORDER BY en SQL)


 Por ejemplo todos los juegos que tengan un precio superior a 25 €.

>>> Articulos.objects.filter(precio__gte=25).order_by('precio')
<QuerySet [<Articulos: id 5, nombre: FIFA23, categoría: Deporte, precio: 40>, 
<Articulos: id 2, nombre: Village, categoría: terror, precio: 49>]>
Nos devuelve todos los registros que cumplen ese criterio pero ordenados de menor a mayor precio.

Si el criterio para ordenar es un campo de texto, entonces lo ordena alfabéticamente.

Si lo queremos ordenar por precio descendente. Delante del campo incluimos un signo menos de esta forma:

>>> Articulos.objects.filter(precio__gte=25).order_by('-precio')
<QuerySet [<Articulos: id 2, nombre: Village, categoría: terror, precio: 49>, 
<Articulos: id 5, nombre: FIFA23, categoría: Deporte, precio: 40>]>

No hay comentarios:

Publicar un comentario