Nuestra primer aplicación en flask – Tutorial (Parte 3) – CRUD

CRUD-INTROBienvenidos a “Nuestra primer aplicación en flask – Tutorial (Parte 3) – CRUD“. Hoy vamos a continuar con nuestra primer aplicación en python usando Flask, en este post vamos a aprender a utilizar el concepto CRUD y vamos a conectar nuestro modelo a la base de datos para acabar con un modelo operativo que finalmente conectaremos al controlador para luego terminar de enlazar las vistas.

 

 

 

 

En este post:

  1. Aprenderás el concepto de gestión de datos CRUD.
  2. Vamos a crear los métodos para crear, leer, actualizar y borrar los contactos de nuestra base de datos JSON.
  3. Vamos a corroborar su funcionamiento ejecutando los métodos directamente desde el models.py
  4. Y ya tendremos listo nuestro modelo!

 

ADVERTENCIA
Quiero aclarar antes de comenzar que obviamente es mejor: Utilizar atributos privados y propiedades de clase. Definir métodos de validación de los datos ingresados por el cliente. Entre otras cosas. Pero, lo que intento es quitar toda la “basura” (mejor dicho, conceptos y prácticas irrelevantes para este ejercicio) para que el lector pueda comprender los conceptos básicos que intento explicar.. Si desarrollo una aplicación como debería hacerlo al 100% el lector novato se va a perder en ramificaciones de conceptos como ¿Para qué sirve definir atributos privados? entre otras y al final tendrá cientos de conceptos nuevos girando en torno a una simple aplicación. Además que mi post sería interminable porque intento explicar a lujo de detalles! Así que aclarado esto, podemos continuar!

 

¿Qué es CRUD?

CRUD- Create, Read, Update, Delete
Ref: https://www.wallarm.com/what/crud-meaning

Ya hemos hablado anteriormente, pero a modo de repaso el concepto CRUD está estrictamente ligado a la gestión de datos y sus siglas hacen referencia en inglés a Create, Read, Update y Delete (Crear, Leer, Actualizar o modificar y Borrar) por lo que abocándonos a lo que nos compete en el post serán los métodos que vamos a definir para interactuar con nuestra base de datos en este caso JSON aunque es mayormente utilizado con bases de datos relacionales como MySQL, pero es conveniente ir aprendiendo a utilizarlo y aplicarlo cada vez que trabajemos con bases de datos.

 

Aplicando el concepto CRUD en nuestro modelo

Para aplicar este concepto debemos definir los métodos correspondientes en nuestra clase y cada método trabajará de una forma determinada. Por ejemplo, el método “Crear” corresponderá a crear nuevos datos en la base de datos y en nuestro caso será “Crear una nueva persona” que será almacenada en la base de datos dentro de la lista que es el valor de la clave “Personas“. Por lo que dicho método deberá contener el código encargado de añadir nuevos datos en formato de diccionario en la ubicación (index) correspondiente y asignando una “clave única e irrepetible” para distinguir el nuevo dato añadido de los demás; lo que habitualmente conocemos como “ID”.

Lo primero es la organización de los datos

CLAVE de IDENTIFICACIÓN (ID)
Me gustaría aclarar que “id” no es solo una clave de identificación de un usuario o “persona / contacto” en nuestro caso. Si no también una manera de diferenciar inequívocamente cada “registro / diccionario” que añadimos y no solamente es buena práctica, sino que utilizarla es obligatorio porque se define como un principio básico de la organización de datos, ya sea trabajes con bases de datos relacionales o no relacionales. Una base de datos sin identificadores únicos e irrepetibles de los registros, no es una base de datos, es un jodido desorden, no es información, son datos aislados y atómicos, regados. Si quieres leer un poco sobre esto lo puedes hacer aquí:

Bases de datos – Clave primaria

 

Creando los métodos CRUD de nuestra clase Personas

Vamos a crear los métodos correspondientes en clase “Personas” de nuestro models.py, pero vamos a comentar en bloques para organizar nuestro código y comprender lo que estamos haciendo:

#BLOQUE IMPORT
import os
import json

#BLOQUE APERTURA ARCHIVO JSON
THIS_FOLDER = os.path.dirname(os.path.abspath(__file__)) #-> Detectamos el directorio de este archivo ".py" que contiene este código.
my_file = os.path.join(THIS_FOLDER + '/DB/' 'base_de_datos.json') #-> A ese directorio le sumamos el del archivo. La ruta total se almacena en my_file
#Aquí "os" detecta el directorio donde se ejecuta nuestro archivo python es decir en este caso "models.py" y a partir de allí almacena esta ruta en THIS_FOLDER. Luego ya sabiendo que la ruta es donde está "models.py" bastará agregarle el directorio DB y finalmente indicar el nombre de archivo.

# --> Cargar archivo JSON   
with open (my_file, "r") as f: #->Abrimos pasando como arg la variable my_file que contiene ruta completa Y ALMACENAMOS EN "f"
    datos = json.load(f) #-> Almacenamos en la variable "datos" la interpreteación de nuestro JSON que está almacenado en "f"


#Ahora datos es un diccionario.
#Almacenamos los diccionarios en una sola variable:
datos_personas = datos['Personas']
#Ahora "datos_personas" almacena los diccionarios.
#Así que nos basta con indicar el index y el nombre de CLAVE PARA IMPRIMIR UN VALOR
#print(datos_personas[0]['test'])

#BLOQUE CLASE PERSONA Y CRUD


class Persona(object):

    def __init__(self, id, nombre, apellido, apodo, telefono, direccion):
        self.id = id
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion


    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto():
        pass
    
    #Método READ -> Leer una persona o contacto de la base de datos
    def leer_contacto():
        pass

    #Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto():
        pass

    #Método DELETE -> Borrar una persona o contacto de la base de datos
    def eliminar_contacto():
        pass


#persona_test = Persona(0, "Mariano", "Laca", "Pyromaniac", "34343434", "pythones.net").__dict__
#print(persona_test)

Fíjate que he comentado cada método para que logres comprender como aplicamos el concepto CRUD. Obviamente, he pasado de cada método con “pass” y ahora vamos a definir que acción realiza cada uno sobre la base de datos. Para comenzar lo haremos con el método “crear_contacto()” correspondiente a CREATE..

Pero aún tenemos un problema sin resolver!. Los objetos que se creen a partir de esta clase no tienen una clave de identificación única (“id”) y da algo de flojera estar asignando una id cada vez que creamos un objeto. Y ni hablar si nos equivocamos y asignamos la misma id.

El atributo “id” debe asignarse automáticamente y de forma auto-incremental cada vez que se cree un objeto a partir de la clase. Pero, esto se modifica dentro de la clase y no en el método Create o “crear_contacto”.

¿Cómo crear una id auto-incremental en Python sin librerías?

Lo podemos hacer sin usar ninguna librería:

  1. Creamos una variable para usar de contador dentro de la clase.
  2. En el método constructor de la clase retiramos “id” ya que no vamos a necesitar pasar un argumento para el parámetro “id” porque será asignado automáticamente.
  3. Dentro del método constructor asignamos que el atributo “id” será el valor de la variable de la clase mediante “Persona.contador_id” y finalmente sumamos uno a esta variable para que el siguiente objeto creado a partir de esta clase tome ese valor y que no sea el mismo.
Id Auto-incremental sin usar librerías en una clase en python:
Código sin usar librerías:

class Persona(object):
    contador_id = 0 #Creamos una variable contador
    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        Persona.contador_id +=1 #Sumamos uno al valor actual de la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion

Por ahora yo lo usaré así…

 

Fíjate creando algunas instancias y solicitando un print, recuerda no pasar ningún valor al atributo id, te dejo el código completo del modelo.py hasta ahora:

#BLOQUE IMPORT
import os
import json

#BLOQUE APERTURA ARCHIVO JSON
THIS_FOLDER = os.path.dirname(os.path.abspath(__file__)) #-> Detectamos el directorio de este archivo ".py" que contiene este código.
my_file = os.path.join(THIS_FOLDER + '/DB/' 'base_de_datos.json') #-> A ese directorio le sumamos el del archivo. La ruta total se almacena en my_file
#Aquí "os" detecta el directorio donde se ejecuta nuestro archivo python es decir en este caso "models.py" y a partir de allí almacena esta ruta en THIS_FOLDER. Luego ya sabiendo que la ruta es donde está "models.py" bastará agregarle el directorio DB y finalmente indicar el nombre de archivo.

# --> Cargar archivo JSON   
with open (my_file, "r") as f: #->Abrimos pasando como arg la variable my_file que contiene ruta completa Y ALMACENAMOS EN "f"
    datos = json.load(f) #-> Almacenamos en la variable "datos" la interpreteación de nuestro JSON que está almacenado en "f"


#Ahora datos es un diccionario.
#Almacenamos los diccionarios en una sola variable:
datos_personas = datos['Personas']
#Ahora "datos_personas" almacena los diccionarios.
#Así que nos basta con indicar el index y el nombre de CLAVE PARA IMPRIMIR UN VALOR
#print(datos_personas[0]['test'])

#BLOQUE CLASE PERSONA Y CRUD


class Persona(object):
    contador_id = 0 #Creamos una variable contador
    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        Persona.contador_id +=1 #Sumamos uno al valor actual de la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion



    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto():
        pass
    
    #Método READ -> Leer una persona o contacto de la base de datos
    def leer_contacto():
        pass

    #Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto():
        pass

    #Método DELETE -> Borrar una persona o contacto de la base de datos
    def eliminar_contacto():
        pass


persona_test = Persona("Mariano", "Laca", "Pyromaniac", "34343434", "pythones.net").__dict__
persona_test2 = Persona("Mariano", "Laca", "Pyromaniac", "34343434", "pythones.net").__dict__
print(persona_test)
print(persona_test2)
persona_test3 = Persona("Mariano", "Laca", "Pyromaniac", "34343434", "pythones.net").__dict__
print(persona_test3)

Resultado:

id_autoincrement_python

Fíjate que aunque usé los mismos datos para cada registro, cada uno será irrepetible porque tendrá asignada una id diferente!.

Pero, aquí luego nos surgirá un problema.. Para ir adelantando ¿Qué pasará cuando reinicies la aplicación Flask?. ¿La variable contador llamada “contador_id” volverá a 0 (cero)?. Lo solucionaremos pronto, por ahora vamos a continuar con el método Create. ¿Sino como podremos saber que sucede al crear una nueva persona o instancia?

 

Método Create – crear_contacto()

El método Create nos permitirá crear personas o registros de contactos en nuestra aplicación y almacenarlos en nuestra base de datos JSON. Para implementar correctamente el concepto CRUD en python nuestro método Create o “crear_contacto()” debe hacer lo siguiente:

  1. Poder crear nuevos objetos “personas” a partir de la clase Personas.
  2. Asignar una clave única e irrepetible a cada objeto generado (id). [Ya lo solucionamos desde la clase.]
  3. Almacenar el registro en nuestra base de datos JSON.

Es realmente muy fácil realizar esto, pero hay un detalle a tener en cuenta:

No podemos usar la misma instancia de apertura del archivo JSON porque lo abrimos como “lectura” usando el parámetro “r

with open (my_file, "r") as f: #->Abrimos pasando como arg la variable my_file que contiene ruta completa Y ALMACENAMOS EN "f"
    datos = json.load(f) #-> Almacenamos en la variable "datos" la interpreteación de nuestro JSON que está almacenado en "f"

Y en el método Create vamos a necesitar modificar el archivo.

Y otro detalle a tener en cuenta es que vamos a “dumpear todo el contenido del archivo”. Básicamente abriremos el JSON lo cargaremos en una variable, modificaremos los datos en esa variable y luego volveremos a abrir el JSON como escritura “w” para volcar todo el contenido de la variable nuevamente en el archivo.

Para ello recordemos que teniendo en cuenta el bloque de apertura de lectura del JSON que te dejé más arriba estamos almacenando la totalidad del archivo dentro de la variable “datos” y es esa variable la que vamos a utilizar para modificar el archivo y volverlo a dumpear. Para ello:

  1. En el método crear_contacto(self) debemos crear una instancia de la clase, es decir almacenar los atributos que le pasamos en una variable que se convertirá en un objeto.
  2. Modificar la variable “datos” que contiene el archivo en forma de lectura, añadiendo este nuevo objeto como ítem de la lista que es el valor para la clave “Personas” dentro de nuestro JSON.
  3. Finalmente una vez añadido el nuevo objeto usando el método de listas .append(“nombredelavariable”) debemos dumpear el nuevo contenido a la base de datos, lo que hará que se modifique completamente. Esto lo hacemos usando el método json.dump(datos, archivo).

Fíjate como lo he realizado yo:

class Persona(object):
    contador_id = 0 #Creamos una variable contador
    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        Persona.contador_id +=1 #Sumamos uno al valor actual de la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion

    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto(self):
        #Creamos nueva instancia
            nueva_persona = Persona(
                self.nombre,
                self.apellido,
                self.apodo,
                self.telefono, 
                self.direccion).__dict__ #Guardamos la nueva persona es decir "instancia" dentro de la variable "nueva_persona" como DICCIONARIO
                #Abrimos nuevamente el JSON pero en modo de escritura "w" y con un nuevo nombre de instancia "fr"
            with open (my_file, "w") as fr: #->Estamos abriendo exactamente de la misma forma pero con "w"
                datos['Personas'].append(nueva_persona) #->Entramos a los valores de la clave "Personas" que son Lista y añadimos el nuevo objeto (diccionario)
                json.dump(datos, fr, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.

Expliquemos un poco más el código pero no es muy dificil de comprender, simplemente creamos una nueva instancia llamada “nueva_persona” que obtiene los argumentos para los parámetros que le pasaremos a la primer instancia de la clase Personas. Una vez los obtiene los convierte en diccionario y los almacena en esta variable.

Luego añadimos los nuevos datos a la variable “datos” que contiene todo el archivo JSON en modo “r” que lo abrimos en las primeras líneas (pero usando .append() porque se trata de la lista de diccionarios recuerdas?) y finalmente vuelve a abrir el archivo base_de_datos.json pero esta vez en modo escritura para “dumpear” o “volcar” todo el contenido nuevo almacenado en “datos” a la base de datos.

Si ahora probamos añadir lo siguiente (Instancia + llamada al método) al final del archivo:

persona_test4 = Persona("Mariano", "Laca", "Pyromaniac", "34343434", "pythones.net") #->Instancia
persona_test4.crear_contacto() #-> Llamamos al método create

Verás como se modifica la base de datos JSON pasando de estar así, a estar así:

CRUD-C-JSON3

Como ves se guarda muy prolijo y si probamos añadir otro elemento verás como la “id” se guardará con un valor incremental. Obviamente debes corroborar que así funcione!

 

 

No olvides que....

¡Si tienes alguna duda o se te dificulta no dudes en pasar y preguntar en nuestro chat!

Ir al chat de la comunidad Pythones!

 

 

Ahora ya puedes borrar estos primeros 2 elementos que utilizamos de “test” en nuestra base de datos json.

alertaPero, ¿qué pasará si nosotros detenemos el funcionamiento de nuestro servidor Flask y luego lo iniciamos de vuelta?

Al iniciar nuevamente la variable que usamos de contador tendrá el valor “0” (cero) y cada nuevo elemento se creará nuevamente contando el número de “id” a partir de cero. Entonces se nos generaría una inconsistencia de datos.

 

 

¿Cómo evitar que nuestro id se repita luego de reiniciar la app?

Bueno solucionar este problema es bastante sencillo, simplemente podemos crear dentro de la misma base de datos JSON otra clave que contenga el valor de la id actual y que la variable contador de nuestra clase tome el valor directamente de la base de datos. Entonces por más que reiniciemos la aplicación el valor de la “id” irá aumentando y será recordado siempre.

La serie de pasos para resolver este problema:

  1. Dentro de nuestra base de datos “base_de_datos.json” creamos una nueva clave “Configuraciones” y dentro incluímos la clave “contador_id_db” con el valor en 0 (cero) o “1” (uno) en caso de la cantidad de elementos que tengas almacenados como personas en ella.
  2. Nuestra variable contador de la clase Personas llamada “contador_id” deberá tomar el valor de la base de datos JSON de la clave “contador_id_db“.
  3. Al crear una nueva instancia, es decir, al crear un objeto persona de la clase Personas la id debe asignarse a la persona o instancia en cuestión y deberá también cambiar en la base de datos JSON el valor de la clave “contador_id_db” AL MOMENTO DE CREARLA (en el método).
  4. Así de esta manera cada vez que se cree una nueva instancia se leerá la “id” y se mostrará, pero al momento de usar el método Create (crear_contacto()) se incrementará en +1 y ya jamás retrocederá en valor.

Lo podemos hacer así bien sencillo:

  • Modificamos base_de_datos.json añadiendo la clave “Configuraciones” que tomará como valor una lista donde alojaremos la clave y valor “contador_id_db” : 1, en mi caso:
{
    "Personas": [
        {
            "id": 1,
            "nombre": "Mariano",
            "apellido": "Laca",
            "apodo": "Pyromaniac",
            "telefono": "34343434",
            "direccion": "pythones.net"
        }
    ],

    "Configuraciones":[
        {"contador_id_db" : 1}
    ]
}
  • Ahora desde la clase “Personas” en el modelo debemos poder acceder y leer este valor usando:

[“Configuraciones”][0][“contador_id_db”]

 

clase persona2

Fíjate que no uso bucles For para llegar hasta el valor de “contador_id_db” en la base de datos. Lo que hago es ir al valor de la clave “Configuraciones” que es una lista y luego con el index en “0” (cero) apuntamos al primer elemento de la lista que tomó como valor y luego darle nuevamente la clave de ese diccionario que me interesa que es “contador_id_db“:

JSON Explicación de acceso
Solo es un ejemplo.. El valor de contador debería ser 2..

 

Y luego imprimo para ver si es el valor correcto dentro de la clase. Así que ya sabemos como acceder a la clave y nuestra variable de clase “contador_id” toma el valor directamente de la base de datos:

 

Explicación consultar id de la base de datos

El código nos quedará algo así, explico paso a paso:

  1. Accedemos a el index 0 de nuestra lista de configuraciones del JSON y lo almacenamos en una variable usando la instancia “datos” que como ya sabes es en la que abrimos el JSON en modo lectura antes.
  2. Mostramos el valor de la variable para confirmar que es correcta.
  3. La id de la persona al momento de realizar una instancia será igual a la de la variable o atributo “contador_id” que obtiene su valor de la base de datos JSON como explique en el ítem 1.

Nuestro models.py ha quedado así (he removido algunos comentarios):

#BLOQUE IMPORT
import os
import json
#BLOQUE APERTURA ARCHIVO JSON
THIS_FOLDER = os.path.dirname(os.path.abspath(__file__)) #-> Detectamos el directorio de este archivo ".py" que contiene este código.
my_file = os.path.join(THIS_FOLDER + '/DB/' 'base_de_datos.json') #-> A ese directorio le sumamos el del archivo. La ruta total se almacena en my_file
#Aquí "os" detecta el directorio donde se ejecuta nuestro archivo python es decir en este caso "models.py" y a partir de allí almacena esta ruta en THIS_FOLDER. Luego ya sabiendo que la ruta es donde está "models.py" bastará agregarle el directorio DB y finalmente indicar el nombre de archivo.

# --> Cargar archivo JSON   
with open (my_file, "r") as f: #->Abrimos pasando como arg la variable my_file que contiene ruta completa Y ALMACENAMOS EN "f"
    datos = json.load(f) #-> Almacenamos en la variable "datos" la interpreteación de nuestro JSON que está almacenado en "f"

datos_personas = datos['Personas']

# --> Cerrar lectura Archivo JSON
f.close()


#BLOQUE CLASE PERSONA Y CRUD
class Persona(object):
    contador_id = datos["Configuraciones"][0]["contador_id_db"]

    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        print("El contador está en: ", Persona.contador_id)
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion

    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto(self):    
        with open (my_file, "w") as modid:
            datos["Configuraciones"][0]["contador_id_db"]+=1#Cambiamos el valor de "contador_id_db" por el de la id +1
            json.dump(datos, modid, indent=4)
            modid.close() # Cerramos el JSON
            print("El contador está ahora en: ", datos["Configuraciones"][0]["contador_id_db"])

        #Creamos nueva instancia
            nueva_persona = Persona(
                self.nombre,
                self.apellido,
                self.apodo,
                self.telefono, 
                self.direccion).__dict__ #Guardamos la nueva persona es decir "instancia" dentro de la variable "nueva_persona" como DICCIONARIO
                #Abrimos nuevamente el JSON pero en modo de escritura "w" y con un nuevo nombre de instancia "fr"
            with open (my_file, "w") as fr: #->Estamos abriendo exactamente de la misma forma pero con "w"
                datos['Personas'].append(nueva_persona) #->Entramos a los valores de la clave "Personas" que son Lista y añadimos el nuevo objeto (diccionario)
                json.dump(datos, fr, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
                #Cerramos el JSON
                fr.close()


    #Método READ -> Leer una persona o contacto de la base de datos
    def leer_contacto():
        pass

    #Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto():
        pass

    #Método DELETE -> Borrar una persona o contacto de la base de datos
    def eliminar_contacto():
        pass

Pero si probamos realizar varias instancia y usar el método create para crear varias personas nos aparecerán todas con la misma “id”:

Fíjate, borramos la base de datos dejándola vacía y asignamos la id a “0” cero. Y probamos crear las siguientes personas:

persona_test4 = Persona(“Mariano”, “Laca”, “Pyromaniac”, “34343434”, “pythones.net”)
persona_test4.crear_contacto()
persona_test5 = Persona(“Martin”, “Paredes”, “el loco”, 343455555, “los buitres 123”)
persona_test5.crear_contacto()
persona_test6= Persona(“Marcos”, “Talo”, “El pepo”, 343455555, “sin nombre 123”)
persona_test6.crear_contacto()

¿Por qué?

Porque en el método Create debemos sumar 1 a la variable “contador_id” de la clase persona.. Ya que en la clase al momento de la instancia solo obtenemos el valor de la base de datos y nada más. Por lo que cada nueva persona obtendrá el mismo valor de “id”!. Así que añadimos esta modificación:

Código models.py:

#BLOQUE IMPORT
import os
import json
#BLOQUE APERTURA ARCHIVO JSON
THIS_FOLDER = os.path.dirname(os.path.abspath(__file__)) #-> Detectamos el directorio de este archivo ".py" que contiene este código.
my_file = os.path.join(THIS_FOLDER + '/DB/' 'base_de_datos.json') #-> A ese directorio le sumamos el del archivo. La ruta total se almacena en my_file
#Aquí "os" detecta el directorio donde se ejecuta nuestro archivo python es decir en este caso "models.py" y a partir de allí almacena esta ruta en THIS_FOLDER. Luego ya sabiendo que la ruta es donde está "models.py" bastará agregarle el directorio DB y finalmente indicar el nombre de archivo.

# --> Cargar archivo JSON   
with open (my_file, "r") as f: #->Abrimos pasando como arg la variable my_file que contiene ruta completa Y ALMACENAMOS EN "f"
    datos = json.load(f) #-> Almacenamos en la variable "datos" la interpreteación de nuestro JSON que está almacenado en "f"

datos_personas = datos['Personas']

# --> Cerrar lectura Archivo JSON
f.close()


#BLOQUE CLASE PERSONA Y CRUD
class Persona(object):
    contador_id = datos["Configuraciones"][0]["contador_id_db"]

    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        print("El contador está en: ", Persona.contador_id)
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion

    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto(self):    
        with open (my_file, "w") as modid:
            datos["Configuraciones"][0]["contador_id_db"]+=1#Cambiamos el valor de "contador_id_db" por el de la id +1
            json.dump(datos, modid, indent=4)
            modid.close() # Cerramos el JSON
            print("El contador está ahora en: ", datos["Configuraciones"][0]["contador_id_db"])
            #Una vez modificamos el valor de la variable en el JSON
            #Lo hacemos también en la variable de CLASE Personas:
            Persona.contador_id +=1
        #Creamos nueva instancia
            nueva_persona = Persona(
                self.nombre,
                self.apellido,
                self.apodo,
                self.telefono, 
                self.direccion).__dict__ #Guardamos la nueva persona es decir "instancia" dentro de la variable "nueva_persona" como DICCIONARIO
                #Abrimos nuevamente el JSON pero en modo de escritura "w" y con un nuevo nombre de instancia "fr"
            with open (my_file, "w") as fr: #->Estamos abriendo exactamente de la misma forma pero con "w"
                datos['Personas'].append(nueva_persona) #->Entramos a los valores de la clave "Personas" que son Lista y añadimos el nuevo objeto (diccionario)
                json.dump(datos, fr, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
                #Cerramos el JSON
                fr.close()


    #Método READ -> Leer una persona o contacto de la base de datos
    def leer_contacto():
        pass

    #Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto(self, id, atr):
        pass

    #Método DELETE -> Borrar una persona o contacto de la base de datos
    def eliminar_contacto():
        pass



persona_test4 = Persona("Mariano", "Laca", "Pyromaniac", "34343434", "pythones.net")
persona_test4.crear_contacto()
persona_test5 = Persona("Martin", "Paredes", "el loco", 343455555, "los buitres 123")
persona_test5.crear_contacto()
persona_test6= Persona("Marcos", "Talo", "El pepo", 343455555, "sin nombre 123")
persona_test6.crear_contacto()

Fíjate en la línea 41 le añadimos 1 al valor de la variable de clase “contador_id” así la siguiente persona obtendrá el valor actual de contador_id_db + 1.

Atención!
  1. Luego de esta modificación, recuerda borrar los datos de las personas creadas anteriormente en la base de datos JSON. Y volver “contador_id_db” a cero, para evitar inconsistencias en la base de datos. ¡Los valores de “id” en cada registro NO DEBEN REPETIRSE!
  2. Recuerda que el valor de la key “contador_id_db” debe corresponderse con la cantidad de personas almacenadas en la base de datos.
  3. También ten en cuenta que si por algún error se borrarán los elementos dentro de nuestro JSON recibirás un error como este:

Traceback (most recent call last):
File “/home/pyro/Documentos/Proyectos/Pruebas en Python/models.py”, line 11, in <module>
datos = json.load(f) #-> Almacenamos en la variable “datos” la interpreteación de nuestro JSON que está almacenado en “f”
File “/usr/lib/python3.10/json/__init__.py”, line 293, in load
return loads(fp.read(),
File “/usr/lib/python3.10/json/__init__.py”, line 346, in loads
return _default_decoder.decode(s)
File “/usr/lib/python3.10/json/decoder.py”, line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File “/usr/lib/python3.10/json/decoder.py”, line 355, in raw_decode
raise JSONDecodeError(“Expecting value”, s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Para solucionar este error simplemente crea el diccionario siguiendo la estructura de datos antes definida. Debes tener un diccionario con la clave “Personas” y “Configuracion”, en este último como valor una lista donde se incluya dentro otro diccionario con la clave “contador_id_db”.

Ahora si probamos crear varias instancias y llamamos al método Create automáticamente aumentará el “id” en nuestra base de datos y en los objetos creados.. Algo así:

 

Finalmente ahora que aprendimos a modificar estos datos podemos directamente saltar al método Update de nuestro sistema CRUD, para aprender a modificar algún campo o elemento de un determinado registro.

 

 

Método Update – actualizar_contacto()

Meme actualizar windows 10

El método Update o “actualizar_contacto()” como le hemos llamado en nuestro código debe permitirnos cambiar o modificar valores dentro de la base de datos JSON de cualquier contacto o persona. Para ello debería permitirnos:

  1. Iterar dentro de la base de datos los campos del registro de determinada persona en base a una referencia de ella.
  2. Deberíamos poder iterar el registro completo según la “id” de la persona o elemento de la lista que queremos modificar correspondiente a ese “id”.
  3. Abriremos el JSON como lectura “read“, modificaremos el campo requerido (por lo que nuestro método debe recibir como parámetro el “id” del elemento y también la clave del valor que queremos modificar; por ejemplo: queremos modificar la persona cuya id es igual a 2 y el elemento nombre.)
  4. Finalmente una vez modificado en la variable “datos” que almacena temporalmente nuestro JSON en modo lectura vamos a dumpear la modificación usando el método de escritura, básicamente es bastante similar al método Create.

Así que vamos a trabajar en el método Update, lo primero será añadirle dos parámetros importantes “id” y “atr“, este último corresponderá al atributo a modificar y también la modificación a aplicar para dicho atributo:

#Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto(id, atr, nuevo_valor):
        pass

 

Ahora básicamente dentro del método debemos:

  1. Usar un bucle for para iterar cada indice de nuestra lista de personas. Como recordarás la clave “Personas” en nuestra base de datos toma como valor una lista, donde dentro se encuentra cada persona como un diccionario. Así que el bucle for itera cada elemento (cada persona).
  2. Dentro del bucle for un condicional debe permitir modificar aquel elemento de la lista que coincide con la “id” que hemos pasado como atributo.
  3. Pero para esto debemos conocer el index() de la lista, porque puede suceder que la “id” no sea igual a la posición que tenga el elemento en la lista. Podrías tener la id = 1, y la posición en la lista (index) sea 0 (cero). Entonces estarías modificando otro elemento a pesar de haber pasado la id correcta.
  4. Como en en la llamada al método pasamos el nombre del atributo y el nuevo valor bastará con modificar la variable “datos” usando el index del elemento deseado y dumpear el nuevo contenido!

Fíjate:

#Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
 def actualizar_contacto(id, atr, nuevo_valor):
     for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
         if persona["id"] == id: #Si el "id" de ese diccionario es igual al arguemento id
             print (persona) #Imprime el diccionario
             
             #Obtenemos el indice de esa persona
             indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona
             datos["Personas"][indice][atr] = nuevo_valor #Accedemos a ese diccionario> clave y cambiamos su valor
             print (datos["Personas"][indice][atr]) #Imprimimos para confirmar el cambio en "datos" (lectura)
             
             #Ahora es momento de guardar el cambio en la base de datos JSON:
             with open (my_file, "w") as modificar: #->Estamos abriendo exactamente de la misma forma pero con "w"
                 json.dump(datos, modificar, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
             #Cerramos el JSON
                 modificar.close()

Vamos a explicar el código:

En el método actualizar tenemos 3 parámetros:

  1. El “id” de la persona que deseamos modificar.
  2. El parámetro “atr“, que será el “atributo” a modificar, que puede ser “nombre”, “telefono”, etc.
  3.  Y finalmente “nuevo_valor” que será el cambio a insertar como valor para la clave que pasamos en el parámetro “atr”.

Luego creamos un bucle for que itere los elementos de la lista “Personas” de nuestra base de datos, y se detenga al cumplir el condicional de que la “id” de la persona en la lista coincide con la “id” que pasamos como argumento al método.

Una vez encontrado el elemento de la lista a modificar debemos conocer su “indice”, recordemos que la listas se ordenan por indices que referencian los elementos y siempre comienzan a partir de 0 (cero). Y si la “id” no coincide con el “index” de la lista, vas a terminar modificando cualquier cosa.. Así que usamos la función .index(elemento) para conocer el índice de esa persona que cumplió el condicional.

Finalmente usando este índice podemos modificar el valor, así que pasamos en la variable datos (lectura del json) el índice, el atributo a modificar (argumento para parámetro del método) y el nuevo valor (asignamos el “nuevo_valor”). Una vez realizados los cambios en la variable “datos” los “dumpeamos” a la base de datos JSON y voilá.

Bastará con llamar al método usando la clase Persona:

Persona.actualizar_contacto(1, “nombre”, “mar”)

Resultado:

 

 

Método Read – leer_contacto()

Ahora vamos a ver el método Read, que en realidad es bastante sencillo.

El Método leer_contacto() debe devolvernos el diccionario del contacto o persona de la cual queremos conocer los datos. Y debe permitirnos buscar una persona por atributo (para que sea un poco más funcional que solo leer datos). Aquí podemos usar un código muy similar a el método de modificar:

  1. El método tendrá dos parámetros, el atributo por el cual deseamos buscar y el valor de ese atributo. Por ejemplo el “id” y el valor, o “nombre” y su valor.
  2. Creamos un bucle for que itere los elementos de la lista “Personas” donde cada iteración será un diccionario y deberá comprobar que el atributo que le hemos pasado como argumento coincida con el valor que también hemos pasado en la base de datos.
  3. Una vez encontrado obtendrá el índice del elemento y lo mostrará!. Pero si el valor es “all” mostrará todos los elementos!

OJO: Debemos tener en cuenta que podemos contar con más de una coincidencia; por ejemplo en el caso de pasar el atributo “nombre” puede haber dos personas con el mismo nombre!. Y debemos poder retornar ambas!. Para ello almacenaremos los resultados en un diccionario y lo retornaremos!

A ello, yo lo he realizado así:

#Método READ -> Leer una persona o contacto de la base de datos
def leer_contacto(atr, valor):
    encontradas = {} #Creamos un diccionario para almacenar los resultados

    for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
        if persona[atr] == valor or valor == 'all': #Si el atributo de ese diccionario es igual al que pasamos


            #Obtenemos el indice de esa persona
            indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona

            #Guardamos en el diccionario nuevo
            encontradas[indice] = persona #Se añade cada persona encontrada con index como clave


    return (encontradas) #Retornamos los datos encontrados

 

Resultado:

Explicando un poco, realmente es muy sencillo:

  1. Creamos un diccionario vacío.
  2. Como hicimos anteriormente iteramos cada elemento de la lista del diccionario “Personas”. Cuando el atributo pasado corresponda al valor que también hemos pasado se cumplirá el condicional.
  3. Al cumplirse el condicional obtenemos el index() de esa persona y lo almacenamos como clave en el diccionario cuyo valor será el diccionario de la persona encontrada!. Además al condicional le agregamos el operador “or” para el caso de que el valor sea “all” devuelva todos los elementos de la lista!
  4. Retornamos el diccionario ya que luego lo mostraremos en la vista claro!
Atención!
Debemos añadir una forma de poder retornar también todas las personas dentro de la lista, para la función de listar todas las personas. Para ello he modificado el condicional del método así:

if persona[atr] == valor or valor == ‘all:
Entonces al llamar al método de esta manera:
print(Persona.leer_contacto(‘id’, ‘all’))
Devolverá un diccionario de todos los elementos organizados con su index como clave!

 

Método Delete – eliminar_contacto()

Bueno este es muy sencillo!. Pero me gustaría aclarar:

Cuando se borra un elemento la “id” ni el contador de id,  jamás se reinician..

“Existió alguna vez un elemento con id = 2, fué borrado y nunca jamás volverá a existir otro con esa misma id”… Jamásssss

Yo lo he realizado así, sin muchas vueltas es casi el mismo código:

#Método DELETE -> Borrar una persona o contacto de la base de datos
def eliminar_contacto(id):
    for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
        if persona["id"] == id: #Si el "id" de ese diccionario es igual al arguemento id
            print ("Se va a borrar: ", persona) #Imprime el diccionario
            
            #Obtenemos el indice de esa persona
            indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona
            datos["Personas"].pop(indice) #Borramos usando el método de listas .pop()

            #Guardamos los cambios en el JSON
            with open (my_file, "w") as eliminar: #->Estamos abriendo exactamente de la misma forma pero con "w"
                json.dump(datos, eliminar, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
            #Cerramos el JSON
                eliminar.close()

Resultado:

Bien ya tenemos básicamente los métodos CRUD desarrollados y funcionando correctamente!

Por supuesto todo esto se podría simplificar aún más creando funciones para determinadas acciones y evitar repetir código en cada método!

Venga hagámoslo rápido que ya se ha hecho muy largo este post!

 

Mejorando el código – Funciones de lectura y escritura JSON

Nuestro models.py estaba así actualmente:

#BLOQUE IMPORT
import os
import json
#BLOQUE APERTURA ARCHIVO JSON
THIS_FOLDER = os.path.dirname(os.path.abspath(__file__)) #-> Detectamos el directorio de este archivo ".py" que contiene este código.
my_file = os.path.join(THIS_FOLDER + '/DB/' 'base_de_datos.json') #-> A ese directorio le sumamos el del archivo. La ruta total se almacena en my_file
#Aquí "os" detecta el directorio donde se ejecuta nuestro archivo python es decir en este caso "models.py" y a partir de allí almacena esta ruta en THIS_FOLDER. Luego ya sabiendo que la ruta es donde está "models.py" bastará agregarle el directorio DB y finalmente indicar el nombre de archivo.

# --> Cargar archivo JSON   
with open (my_file, "r") as f: #->Abrimos pasando como arg la variable my_file que contiene ruta completa Y ALMACENAMOS EN "f"
    datos = json.load(f) #-> Almacenamos en la variable "datos" la interpreteación de nuestro JSON que está almacenado en "f"

datos_personas = datos['Personas']

# --> Cerrar lectura Archivo JSON
f.close()


#BLOQUE CLASE PERSONA Y CRUD
class Persona(object):
    contador_id = datos["Configuraciones"][0]["contador_id_db"]

    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        print("El contador está en: ", Persona.contador_id)
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion

    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto(self):    
        with open (my_file, "w") as modid:
            datos["Configuraciones"][0]["contador_id_db"]+=1#Cambiamos el valor de "contador_id_db" por el de la id +1
            json.dump(datos, modid, indent=4)
            modid.close() # Cerramos el JSON
            print("El contador está ahora en: ", datos["Configuraciones"][0]["contador_id_db"])
            #Una vez modificamos el valor de la variable en el JSON
            #Lo hacemos también en la variable de CLASE Personas:
            Persona.contador_id +=1
        #Creamos nueva instancia
            nueva_persona = Persona(
                self.nombre,
                self.apellido,
                self.apodo,
                self.telefono, 
                self.direccion).__dict__ #Guardamos la nueva persona es decir "instancia" dentro de la variable "nueva_persona" como DICCIONARIO
                #Abrimos nuevamente el JSON pero en modo de escritura "w" y con un nuevo nombre de instancia "fr"
            with open (my_file, "w") as fr: #->Estamos abriendo exactamente de la misma forma pero con "w"
                datos['Personas'].append(nueva_persona) #->Entramos a los valores de la clave "Personas" que son Lista y añadimos el nuevo objeto (diccionario)
                json.dump(datos, fr, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
                #Cerramos el JSON
                fr.close()


    #Método READ -> Leer una persona o contacto de la base de datos
    def leer_contacto(atr, valor):
        encontradas = {} #Creamos un diccionario para almacenar los resultados

        for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
            if persona[atr] == valor or valor == 'all': #Si el atributo de ese diccionario es igual al que pasamos


                #Obtenemos el indice de esa persona
                indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona

                #Guardamos en el diccionario nuevo
                encontradas[indice] = persona #Se añade cada persona encontrada con index como clave

        return (encontradas) #Retornamos los datos encontrados

    

    #Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto(id, atr, nuevo_valor):
        for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
            if persona["id"] == id: #Si el "id" de ese diccionario es igual al arguemento id
                print (persona) #Imprime el diccionario
                
                #Obtenemos el indice de esa persona
                indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona
                datos["Personas"][indice][atr] = nuevo_valor #Accedemos a ese diccionario> clave y cambiamos su valor
                print (datos["Personas"][indice][atr]) #Imprimimos para confirmar el cambio en "datos" (lectura)
                
                #Ahora es momento de guardar el cambio en la base de datos JSON:
                with open (my_file, "w") as modificar: #->Estamos abriendo exactamente de la misma forma pero con "w"
                    json.dump(datos, modificar, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
                #Cerramos el JSON
                    modificar.close()  
            

    #Método DELETE -> Borrar una persona o contacto de la base de datos
    def eliminar_contacto(id):
        for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
            if persona["id"] == id: #Si el "id" de ese diccionario es igual al arguemento id
                print ("Se va a borrar: ", persona) #Imprime el diccionario
                
                #Obtenemos el indice de esa persona
                indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona
                datos["Personas"].pop(indice) #Borramos usando el método de listas .pop()

                #Guardamos los cambios en el JSON
                with open (my_file, "w") as eliminar: #->Estamos abriendo exactamente de la misma forma pero con "w"
                    json.dump(datos, eliminar, indent =4) #Finalmente almacenamos en la base de datos argumentando ademas un identado.
                #Cerramos el JSON
                    eliminar.close()  


 

Bien yo he tratado de hacerlo de una forma más comprensible, pero lo cierto es que podemos simplificar mucho el código repetitivo:

  1. Eliminando comentarios innecesarios.
  2. Creando una función para realizar cambios en la base de datos JSON. De esta manera no tendremos que estar abriendo el archivo múltiples veces en cada método, sino que simplemente llamaremos a una función o método encargado de realizar esta acción.

Así que el código de “models.py” ha quedado así:

#BLOQUE IMPORT
import os
import json

#BLOQUE APERTURA ARCHIVO JSON

THIS_FOLDER = os.path.dirname(os.path.abspath(__file__))
my_file = os.path.join(THIS_FOLDER + '/DB/' 'base_de_datos.json') 

# --> Función Leer archivo JSON   
def leer_json():
    with open (my_file, "r") as f:
        datos = json.load(f)
        f.close()
        return datos
datos = leer_json()

# --> Función modificar JSON
def modificar_json():
    with open (my_file, "w") as modid:
            json.dump(datos, modid, indent=4)
            modid.close()





#BLOQUE CLASE PERSONA Y CRUD
class Persona(object):
    contador_id = datos["Configuraciones"][0]["contador_id_db"]

    def __init__(self, nombre, apellido, apodo, telefono, direccion):
        print("El contador está en: ", Persona.contador_id)
        self.id = Persona.contador_id #Atriuto id es igual a la variable
        self.nombre = nombre
        self.apellido = apellido
        self.apodo = apodo
        self.telefono = telefono
        self.direccion = direccion

    #Método CREATE -> Crear una nueva persona o contacto de la base de datos
    def crear_contacto(self):    
            datos["Configuraciones"][0]["contador_id_db"]+=1 #Cambiamos el valor de "contador_id_db" (en la base de datos)
            modificar_json()
            print("El contador está ahora en: ", datos["Configuraciones"][0]["contador_id_db"])
            Persona.contador_id +=1 #Sumamos uno a la variable contador_id de la clase
        #Creamos nueva instancia
            nueva_persona = Persona(
                self.nombre,
                self.apellido,
                self.apodo,
                self.telefono, 
                self.direccion).__dict__ #Guardamos la nueva persona es decir "instancia" dentro de la variable "nueva_persona" como DICCIONARIO
            datos['Personas'].append(nueva_persona) #->Entramos a los valores de la clave "Personas" que son Lista y añadimos el nuevo objeto (diccionario)
            modificar_json()

    #Método READ -> Leer una persona o contacto de la base de datos
    def leer_contacto(atr, valor):
        encontradas = {} #Creamos un diccionario para almacenar los resultados

        for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
            if persona[atr] == valor or valor == 'all': #Si el atributo de ese diccionario es igual al que pasamos


                #Obtenemos el indice de esa persona
                indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona

                #Guardamos en el diccionario nuevo
                encontradas[indice] = persona #Se añade cada persona encontrada con index como clave

        return (encontradas) #Retornamos los datos encontrados

    

    #Método UPDATE -> Actualizar o modificar un dato de algún contacto en la bd
    def actualizar_contacto(id, atr, nuevo_valor):
        for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
            if persona["id"] == id: #Si el "id" de ese diccionario es igual al arguemento id
                print (persona) #Imprime el diccionario
                
                #Obtenemos el indice de esa persona
                indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona
                datos["Personas"][indice][atr] = nuevo_valor #Accedemos a ese diccionario> clave y cambiamos su valor
                print (datos["Personas"][indice][atr]) #Imprimimos para confirmar el cambio en "datos" (lectura)
                modificar_json()
            

    #Método DELETE -> Borrar una persona o contacto de la base de datos
    def eliminar_contacto(id):
        for persona in datos["Personas"]: #Cada "indice" es un diccionario de persona
            if persona["id"] == id: #Si el "id" de ese diccionario es igual al arguemento id
                print ("Se va a borrar: ", persona) #Imprime el diccionario
                
                #Obtenemos el indice de esa persona
                indice = datos["Personas"].index(persona) #La variable toma el valor de la posición de la persona
                datos["Personas"].pop(indice) #Borramos usando el método de listas .pop()
                modificar_json()

 

mme-minMucho mejor! ¿No?. ¿Se te ocurre otra forma de organizar y reducir aún más el código?

¿Y por qué no lo hiciste así desde el principio?

—— La verdad no se me había ocurrido. No les miento! Estaba demasiado concentrado en lo de más que olvide el detalle de usar una función para evitar repetir código..

Esto ha sido todo, nos vemos en la siguiente entrada para conectar el modelo al controlador y finalmente realizar las vistas!. Venga que ya falta poco para terminar nuestra primer aplicación!

 


 

 

Continúa leyendo!
Entrada anterior! Siguiente entrada!(En proceso)
Compartir es agradecer! :)

Hey no te vayas sin dejarme un comentario!

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Aprende a programar de manera profesional!

¿Todavía sin obtener una retribución justa de tu aprendizaje?

Curso de Python Udemy

Convierteté en un verdadero Jedy de Python y comienza a desarrollar aplicaciones de forma profesional obteniendo un certificado de Udemy!. Pasate por la sección de Cursos Premium y realiza los mejores cursos con certificación válida a nivel internacional por apenas unos dolares!