Última actualización: 15 de diciembre de 2023

2.2. ZODB

La ZODB es una base de datos de objetos. Hace que sea muy fácil almacenar diferentes tipos de datos de contenido en un gráfico, lo que admite subclases (algo que SQL a menudo hace mal).

Dado que la base de datos almacena objetos y los objetos están definidos en código Python, siempre necesitará el código fuente Python correspondiente para crear instancias de los objetos almacenados dentro de ZODB. Esto puede parecer incómodo al principio, pero necesita tener MySQL ejecutándose para leer lo que hay dentro de los archivos MySQL almacenados en su disco, etc.

Advertencia

La base de datos ZODB no se puede utilizar sin el código fuente de Python utilizado para crear los datos. Los datos no se pueden leer con ninguna herramienta basada en SQL y existen pequeñas herramientas para manejar los datos sin procesar.

2.2.1. Instalación

Para instalar el paquete ZODB ejecute el siguiente comando, el cual a continuación se presentan el correspondiente comando de tu sistema operativo:

$ pip install ZODB

Puede probar si la instalación se realizo correctamente, ejecutando el siguiente comando correspondiente a tu sistema operativo:

$ python -c "import ZODB ; print(ZODB.__package__)"

Si muestra el numero de la versión instalada de ZODB, tiene correctamente instalada la paquete. Con esto, ya tiene todo listo para continuar.

Adicionalmente puedes instalar un cliente de base de datos ZODB, a continuación se presentan alternativas:

2.2.1.1. ZODB browser

El ZODB browser le permite inspeccionar objetos persistentes almacenados en ZODB, ver sus atributos y los cambios históricos realizados en ellos.

Es un paquete de herramientas de línea de comandos para administrar archivos de base de datos ZODB, incluido el programa zodbbrowser.exe para la shell de línea de comandos.

Para instalar el administrador de base de datos ZODB ZODB browser de forma nativa para sistemas operativos Linux y Windows, a continuación se presentan los modos de instalación:

$ pip install zodbbrowser==0.17.1

Puede probar si la instalación se realizo correctamente, ejecutando el siguiente comando:

Puede probar si la instalación se realizo correctamente, ejecutando el siguiente comando:

> zodbbrowser --help
Usage: zodbbrowser [options] [FILENAME | --zeo ADDRESS]

Open a ZODB database and start a web-based browser app.

Options:
  -h, --help        show this help message and exit
  --zeo=ADDRESS     connect to ZEO server instead (host:port or socket name)
  --storage=NAME    connect to given ZEO storage
  --listen=ADDRESS  specify port (or host:port) to listen on
  -q, --quiet       be quiet
  --debug           enable debug logging
  --rw              open the database read-write (default: read-only)

Si tiene disponibles el comando zodbbrowser, tiene correctamente instalada el cliente de base de datos nativa ZODB por linea de comando.

Nota

Mas información consulte https://pypi.org/project/zodbbrowser/

Ejecute el comando zodbrowser especificando un nombre de archivo ZODB, ejecutando el siguiente comando:

$ zodbbrowser /ruta/al/archivo/Data.fs

El comando anterior muestra el siguiente mensaje:

Listening on http://localhost:8070/

Abra http://localhost:8070 en un navegador web. Tenga en cuenta que no hay controles de acceso; todos los demás usuarios de la máquina local podrán acceder al contenido de la base de datos.

Al abrir la dirección anterior debería mostrar la interfaz gráfica de zodbbrowser, como se muestra a continuación:

../_images/zodbbrowser.png

Figura 2.1, Figura 2.1, ZODB browser

Si muestra la interfaz gráfica de zodbbrowser, tiene correctamente instalada el cliente de base de datos ZODB.

2.2.2. Cadenas de conexión

Para definir el método connect debe definir las cadenas de conexión con ZODB como se describe a continuación:

DB_PATH

Ruta absoluta o relativa del archivo de base de datos ZODB.

DB_FILE

Nombre del archivo de base de datos ZODB.

A continuación presento un ejemplo en Python implementando una cadena de conexión para una base de datos ZODB:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import os
import persistent
import transaction
import ZODB, ZODB.FileStorage
from pathlib import Path

DB_PATH = os.path.dirname(os.path.abspath(__file__)) + os.sep + "filestorage/"
Path(DB_PATH).mkdir(parents=True, exist_ok=True)
DB_FILE = ZODB.FileStorage.FileStorage(DB_PATH + "productos.fs")
DB = ZODB.DB(DB_FILE)

connection = DB.open()

El ejemplo anterior se describe a continuación:

  • En la linea 1, se importa la librería os de la librería estándar Python.

  • En la linea 2, se importa la librería ZODB de la librería estándar Python.

  • En la linea 4, se define en la constante DB_PATH la ruta absoluta usada para guardar la base de datos.

  • En la linea 5, se define en la constante DB_FILE el nombre de la base de datos.

  • En la linea 6, se define en la constante DB la ruta completa usada para leer la base de datos.

De esta forma se crea una cadena de conexión para ZODB para ser usada por el método open.


2.2.3. Insertar registros

Si requiere insertar registro en una tabla, a continuación tiene un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
"""Programa para la inserción de registro(s) de la ZODB"""

import logging
import os
import persistent
import transaction
import ZODB, ZODB.FileStorage
from ZODB.POSException import StorageError
from pathlib import Path

logging.basicConfig(level=logging.INFO)

DB_PATH = os.path.dirname(os.path.abspath(__file__)) + os.sep + "filestorage/"
Path(DB_PATH).mkdir(parents=True, exist_ok=True)
DB_FILE = ZODB.FileStorage.FileStorage(DB_PATH + "productos.fs")
DB = ZODB.DB(DB_FILE)


class Producto(persistent.Persistent):
    """Clase Producto"""
    def __init__(self, id, descripcion):
        """Método constructor de clase de Producto

        Args:
            id (int): ID del producto
            descripcion (str): Descripción del producto
        """
        self.id = id
        self.descripcion = descripcion

    def __str__(self):
        """Método de representación de informal del objeto,
        usado para crear la salida que se le mostrará al usuario"""
        return "Id: {0}\nDescripción: {1}".format(self.id, self.descripcion)

    def __repr__(self):
        """Método de representación de formal del objeto,
        usado para depuración y desarrollo"""
        return f'{self.__class__.__name__}:({repr(self.id)}, {repr(self.descripcion)})'


def insertar_registro():
    """Función para la inserción de registro de la ZODB"""

    try:
        # Crear la instancia de DB y pasar el nombre del archivo
        conexion = DB.open()

        # Crear la instancia de conexion y llamar al método open de db
        nodo = conexion.root()
        logging.info(f"¡Conectado a la base de datos {os.path.basename(DB_FILE.getName())}!\n")

        # Crear una instancia de la clase Producto
        producto1 = Producto(1, "Carro")
        nodo['producto1'] = producto1

        # Crear una instancia de la clase Producto
        producto2 = Producto(2, "Moto")
        nodo['producto2'] = producto2

        # Crear una instancia de la clase Producto
        producto3 = Producto(3, "Bicicleta")
        nodo['producto3'] = producto3

        # Crear una lista de instancia de objetos Producto
        nodo['productos'] = [producto1, producto2, producto3]

        # Guardar los cambios en la base de datos
        transaction.commit()
        logging.info("¡Fueron insertado(s) los registro(s) correctamente en la ZODB!\n")


    except StorageError as error:
        print("¡Fallo la inserción de registro(s) en la ZODB!", error)
    finally:
        if conexion:
            # Cerrar la conexión a la base de datos
            conexion.close()
            logging.info(
                "¡La conexión ZODB a la base de datos {} fue cerrada!\n".format(
                    os.path.basename(DB_FILE.getName())
                )
            )


if __name__ == "__main__":
    insertar_registro()

Importante

Usted puede descargar el código usado en esta sección haciendo clic en el siguiente enlace: zodb_record_insert.py.

Truco

Para ejecutar el código zodb_record_insert.py abra una consola de comando, acceda al directorio donde se encuentra el programa:

leccion2/
    └── zodb/
        └── zodb_record_insert.py

Si tiene la estructura de archivo previa, entonces ejecute el siguiente comando:

$ python zodb_record_insert.py

El anterior código al ejecutar debe mostrar el siguiente mensaje:

INFO:root:¡Conectado a la base de datos productos.fs!

INFO:root:¡Fueron insertado(s) los registro(s) correctamente en la ZODB!

INFO:root:¡La conexión ZODB a la base de datos productos.fs fue cerrada!

2.2.4. Consultar registros

Si requiere consultar registros de tabla, a continuación tiene un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
"""Modulo de buscar registros en la ZODB"""

import logging
import os
import ZODB, ZODB.FileStorage
from ZODB.POSException import StorageError
from pathlib import Path

logging.basicConfig(level=logging.INFO)

DB_PATH = os.path.dirname(os.path.abspath(__file__)) + os.sep + "filestorage/"
Path(DB_PATH).mkdir(parents=True, exist_ok=True)
DB_FILE = ZODB.FileStorage.FileStorage(DB_PATH + "productos.fs")
DB = ZODB.DB(DB_FILE)


def consultar_registro():
    """Función para la consulta de registro(s) de la ZODB"""

    try:
        # Crear la instancia de DB y pasar el nombre del archivo
        conexion = DB.open()

        # Crear la instancia de conexion y llamar al método open de db
        nodo = conexion.root()
        logging.info(f"¡Conectado a la base de datos {os.path.basename(DB_FILE.getName())}!\n")

        print("Todos los registros: ")
        print(nodo.items())

        print("Todos los valores: ")
        print(nodo.values())

        if 'producto1' in nodo.items():
            print("Producto: ", nodo['producto1'])
            print(f"\tId: {nodo['producto1'].id}")
            print(f"\tDescripción del producto: {nodo['producto1'].descripcion}")
        else:
            print("No tiene registros el la ZODB")


    except StorageError as error:
        print("¡Fallo la consulta de registro(s) en la ZODB!", error)
    finally:
        if conexion:
            # Cerrar la conexión a la base de datos
            conexion.close()
            logging.info(
                "¡La conexión ZODB a la base de datos {} fue cerrada!\n".format(
                    os.path.basename(DB_FILE.getName())
                )
            )


if __name__ == "__main__":
    consultar_registro()

Importante

Usted puede descargar el código usado en esta sección haciendo clic en el siguiente enlace: zodb_record_read.py.

Truco

Para ejecutar el código zodb_record_read.py abra una consola de comando, acceda al directorio donde se encuentra el programa:

leccion2/
    └── zodb/
        └── zodb_record_read.py

Si tiene la estructura de archivo previa, entonces ejecute el siguiente comando:

$ python zodb_record_read.py

El anterior código al ejecutar debe mostrar el siguiente mensaje:

INFO:root:¡Conectado a la base de datos sistema.db!

Total de filas son: 3

Mostrar cada fila:

        Id: 1
        Nombre: Leonardo
        Código postal: Caballero
        Teléfono: 5001

        Id: 2
        Nombre: Ana
        Código postal: Poleo
        Teléfono: 6302

        Id: 3
        Nombre: Pedro
        Código postal: Lopez
        Teléfono: 4001

INFO:root:¡La conexión ZODB a la base de datos sistema.db fue cerrada!

2.2.5. Actualizar registros

Si requiere actualizar registro de tabla, a continuación tiene un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""Modulo de actualizar registros en la ZODB"""

import logging
import os
import transaction
import ZODB, ZODB.FileStorage
from ZODB.POSException import StorageError
from pathlib import Path

logging.basicConfig(level=logging.INFO)

DB_PATH = os.path.dirname(os.path.abspath(__file__)) + os.sep + "filestorage/"
Path(DB_PATH).mkdir(parents=True, exist_ok=True)
DB_FILE = ZODB.FileStorage.FileStorage(DB_PATH + "productos.fs")
DB = ZODB.DB(DB_FILE)


def actualizar_registro():
    """Función para la actualización de registro de la tabla"""

    try:
        # Crear la instancia de DB y pasar el nombre del archivo
        conexion = DB.open()

        # Crear la instancia de conexion y llamar al método open de db
        nodo = conexion.root()
        logging.info(f"¡Conectado a la base de datos {os.path.basename(DB_FILE.getName())}!\n")

        print(nodo['producto1'].descripcion)
        nodo['producto1'].descripcion='Vehiculo'
        print(nodo['producto1'].descripcion)

        print(nodo['producto2'].descripcion)
        nodo['producto2'].descripcion='Motocicleta'
        print(nodo['producto2'].descripcion)

        print(nodo['producto3'].descripcion)
        nodo['producto3'].descripcion='Bici'
        print(nodo['producto3'].descripcion)

        # Guardar los cambios en la base de datos
        transaction.commit()

    except StorageError as error:
        print("¡Fallo la actualización de registro(s) en la ZODB!", error)
    finally:
        if conexion:
            # Cerrar la conexión a la base de datos
            conexion.close()
            logging.info(
                "¡La conexión ZODB a la base de datos {} fue cerrada!\n".format(
                    os.path.basename(DB_FILE.getName())
                )
            )


if __name__ == "__main__":
    actualizar_registro()

Importante

Usted puede descargar el código usado en esta sección haciendo clic en el siguiente enlace: zodb_record_update.py.

Truco

Para ejecutar el código zodb_record_update.py abra una consola de comando, acceda al directorio donde se encuentra el programa:

leccion2/
    └── zodb/
        └── zodb_record_update.py

Si tiene la estructura de archivo previa, entonces ejecute el siguiente comando:

$ python zodb_record_update.py

El anterior código al ejecutar debe mostrar el siguiente mensaje:

INFO:root:¡Conectado a la base de datos sistema.db!

INFO:root:¡Fueron actualizado(s) 2 registro(s) correctamente en la tabla!

INFO:root:¡La conexión ZODB a la base de datos sistema.db fue cerrada!

2.2.6. Eliminar registros

Si requiere eliminar registro de tabla, a continuación tiene un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"""Modulo de eliminar registros en la ZODB"""

import logging
import os
import transaction
import ZODB, ZODB.FileStorage
from ZODB.POSException import StorageError
from pathlib import Path

logging.basicConfig(level=logging.INFO)

DB_PATH = os.path.dirname(os.path.abspath(__file__)) + os.sep + "filestorage/"
Path(DB_PATH).mkdir(parents=True, exist_ok=True)
DB_FILE = ZODB.FileStorage.FileStorage(DB_PATH + "productos.fs")
DB = ZODB.DB(DB_FILE)


def eliminar_registro():
    """Función para la eliminación de registro de la ZODB"""

    try:
        # Crear la instancia de DB y pasar el nombre del archivo
        conexion = DB.open()

        # Crear la instancia de conexion y llamar al método open de db
        nodo = conexion.root()
        logging.info(f"¡Conectado a la base de datos {os.path.basename(DB_FILE.getName())}!\n")

        # Mostrar el objeto a eliminar
        print(nodo['producto1'].descripcion)

        if 'producto1' in nodo.items():
            # Eliminar el objeto de la raíz
            nodo.pop("producto1")
            # Guardar los cambios en la base de datos
            transaction.commit()
            logging.info("¡Registro eliminado correctamente!\n")
        else:
            logging.info("¡No tiene ningún registro en la ZODB!\n")

    except StorageError as error:
        print("¡Fallo la eliminación de registro(s) en la ZODB!", error)
    finally:
        if conexion:
            # Cerrar la conexión a la base de datos
            conexion.close()
            logging.info(
                "¡La conexión ZODB a la base de datos {} fue cerrada!\n".format(
                    os.path.basename(DB_FILE.getName())
                )
            )


if __name__ == "__main__":
    eliminar_registro()

Importante

Usted puede descargar el código usado en esta sección haciendo clic en el siguiente enlace: zodb_record_delete.py.

Truco

Para ejecutar el código zodb_record_delete.py abra una consola de comando, acceda al directorio donde se encuentra el programa:

leccion2/
    └── zodb/
        └── zodb_record_delete.py

Si tiene la estructura de archivo previa, entonces ejecute el siguiente comando:

$ python zodb_record_delete.py

El anterior código al ejecutar debe mostrar el siguiente mensaje:

INFO:root:¡Conectado a la base de datos sistema.db!

INFO:root:¡Registro eliminado correctamente!

INFO:root:¡La conexión ZODB a la base de datos sistema.db fue cerrada!

Asi de esta forma puede ingresar, consultar, actualizar y eliminar registro en una tabla en una base de datos ZODB.


2.2.7. Práctica - Caso real

A continuación se presenta una práctica más real de implementar el uso de proyectos con ZODB, a continuación la estructura de proyecto llamado zodb:

zodb/
└── sistema/
    ├── classes/
    │   ├── __init__.py
    │   └── producto.py
    ├── filestorage
    │   ├── inventario.fs
    │   ├── inventario.fs.index
    │   ├── inventario.fs.lock
    │   └── inventario.fs.tmp
    ├── __init__.py
    ├── main.py
    ├── requirements.txt
    └── settings.py

A continuación se presenta y explica el uso de cada archivo para este proyecto:

Archivo producto.py

Modulo de configuraciones del programa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"""Modulo de Clases"""

import persistent

class Producto(persistent.Persistent):
    """Clase Producto"""
    def __init__(self, id, descripcion):
        """Método constructor de clase de Producto

        Args:
            id (int): ID del producto
            descripcion (str): Descripción del producto
        """
        self.id = id
        self.descripcion = descripcion

    def __str__(self):
        """Método de representación de informal del objeto,
        usado para crear la salida que se le mostrará al usuario"""
        return "Id: {0}\nDescripción: {1}".format(self.id, self.descripcion)

    def __repr__(self):
        """Método de representación de formal del objeto,
        usado para depuración y desarrollo"""
        return f'{self.__class__.__name__}:({repr(self.id)}, {repr(self.descripcion)})'

Archivo main.py

Modulo de configuraciones del programa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"""Modulo de Clase ZODB"""

import logging
import transaction

from settings import *
from classes.producto import Producto
from ZODB.POSException import StorageError

def crear_conexion(ruta):
    """Crear conexión con un servidor SQLite

    Args:
        ruta (str): La ruta completa usada para leer la base de datos

    Returns:
        root (Connection): Representación conexión a la base de datos SQLite
    """
    try:
        connection = DB.open()
        root = connection.root()
        logging.info(
            f"¡Conexión a la base de datos '{os.path.basename(ruta)}' fue exitosa!\n"
        )
    except StorageError as e:
        print(f"ERROR: ¡Se produjo una falla al almacenar: '{e}'!")

    return root


#def insertar_registro(bd, registros):

Archivo settings.py

Modulo de configuraciones del programa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
"""Modulo de configuraciones"""

import os
from pathlib import Path
import ZODB, ZODB.FileStorage

DB_FILE = "inventario.fs"
DB_DIR = os.path.dirname(os.path.abspath(__file__)) + os.sep + "filestorage/"
Path(DB_DIR).mkdir(parents=True, exist_ok=True)
STORAGE = ZODB.FileStorage.FileStorage(DB_DIR + DB_FILE)

DB = ZODB.DB(STORAGE)

Archivo inventario.fs

Archivo de base de datos de ZODB llamado inventario.fs la cual no se incluye ya que cada vez que se inicia el programa main.py se elimina y crea nuevamente, para cuidar la creación de los datos iniciales.

Archivo requirements.txt

Archivo de requirements.txt de la herramienta de gestión de paquetes pip.

1
2
3
# Install the packages from PyPi repository.
ZODB==5.8.1
zodbbrowser==0.17.1

Teniendo creada la anterior estructura de proyecto, vuelva a ejecutar ahora el modulo con el siguiente comando, el cual a continuación se presentan el correspondiente comando de tu sistema operativo:

Antes de ejecutar debes instalar sus dependencias, con el siguiente comando:

$ pip install -r requirements.txt

Truco

Para ejecutar el código fuente de esta practica debe invocar al modulo main.py, abra una consola de comando, acceda al directorio donde se encuentra la estructura previa y ejecute el siguiente comando:

$ python main.py

El anterior código al ejecutar debe mostrar el siguiente mensaje:

ItemsView({'producto1': Producto:(1, Carro), 'producto2': Producto:(2, Moto), 'producto3': Producto:(3, Bicicleta), 'productos': [Producto:(1, Carro), Producto:(2, Moto), Producto:(3, Bicicleta)]})
Id: 1
Descripción: Camioneta
Camioneta

Asi de esta forma puede ingresar, consultar y actualizar registro en un archivo serializado de objetos python ZODB.


Ver también

Consulte la sección de lecturas suplementarias


¿Cómo puedo ayudar?

¡Mi soporte está aquí para ayudar!

Mi horario de oficina es de lunes a sábado, de 9 AM a 5 PM. GMT-4 - Caracas, Venezuela.

La hora aquí es actualmente 7:35 PM GMT-4.

Mi objetivo es responder a todos los mensajes dentro de un día hábil.

Contrata mi increíble soporte profesional