Consulta MySQL desde una función

Iniciado por DeMoNcRaZy, 20 Diciembre 2021, 01:08 AM

0 Miembros y 1 Visitante están viendo este tema.

DeMoNcRaZy

Buenas, tengo un problema duda respecto a insertar datos en la base de datos mediante consultas mysql.

Tengo el archivo main.py

Código (python) [Seleccionar]
from tkinter import *
from tkinter import ttk
from conexion import *

####### FUNCIONES #######

def click():
    ttk.Label(mainframe, text="Haz echo CLICK").grid(column=40, row=70, sticky=W)
    ttk.Button(mainframe, text="BOTON 2", command=click2).grid(column=40, row=90, sticky=W)

def click2():
    ttk.Label(mainframe, text="POR FIN!!").grid(column=40, row=110, sticky=W)
    ttk.Button(mainframe, text="BOTON 2").grid(column=40, row=130, sticky=W)

def crearCliente():
    crearCliente1 = "INSERT INTO test(id,name,email) VALUES ('5','prueba','prueba')"
   
    ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)


####### VENTANA Tkinter #######

root = Tk()
root.title("Mostrar datos")
root.geometry("400x400")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)


ttk.Label(mainframe, text=results).grid(column=3, row=2, sticky=W)
ttk.Label(mainframe, text="Probando Label").grid(column=30, row=20, sticky=W)

ttk.Button(mainframe, text="BOTON", command=crearCliente).grid(column=40, row=40, sticky=W)



root.mainloop()


Y el archivo conexión.py:

Código (python) [Seleccionar]
import pymysql

# Abre conexion con la base de datos
db = pymysql.connect(host='localhost',
                             user='root',
                             password='root',
                             database='test',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)
##################################################
cursor = db.cursor()

# Seleccionador base de datos
sql = "SELECT name FROM test WHERE id = 2"

# Ejecutar comando
cursor.execute(sql)

# Mostrar
results = cursor.fetchall()

# Desconexion
db.close()


Tengo esto, y funciona bien hasta el sentido de que tengo importado el archivo de conexión en el main (archivo principal) lo cual tengo una variable llamada "results" que se muestra perfectamente en el main, y eso que Estrellas declarada en el archivo conexión.

Y ahora estoy intentado crear una consulta a la base de datos en la función CrearCliente de añadir un nuevo dato y no se me añade. No me da ningún error ni nada pero no se crea los datos en la base de datos. ¿que estoy haciendo mal?

Cualquier información adicional lo agradeceria.

Saludos.
Esta página web no está disponible - Google Chrome

Drakaris

Buenos dias.
¿Porque falla y como solucionarlo?
Código (python) [Seleccionar]

# Seleccionador base de datos
def crearCliente():
    crearCliente1 = "INSERT INTO test(id,name,email) VALUES ('5','prueba','prueba')"


El problema esta en la sentencia SQL.
La sintaxis es correcta pero los tipos de datos asociados de la columna no cuadran.

El id es una columna PRIMARY KEY, que identifica ese registro en concreto. Este normalmente es auto incremental que por cada insert que tu hagas este se incrementa.
El tipo de dato para un id autoincrementado es INT.

Como es INT (integer=entero), es un numero, por la cual no puedes añadir en el VALUES un '5', sino un 5.

El '5' no es igual a 5.
'5' con comillas simples ( ' ) o comillas dobles ( " ) significa que es un VARCHAR(), texto
5 (sin comillas) es un numero.


Propuesta de mejora

Configuraria en el gestor de bases de datos que la columna id fuera autoincrementado.

Yo no se como creas las tablas...si lo haces por interfaz grafica o SQL. Pero en SQL sería....
Código (SQL) [Seleccionar]

CREATE TABLE test1 (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    email VARCHAR(100) NOT NULL,
    CONSTRAINT test_PK PRIMARY KEY (id)
);


Si es AUTOINCREMENT no hace falta que insertes el 5 cuando hacer el INSERT.

Código (SQL) [Seleccionar]

INSERT INTO test(name,email) VALUES ('prueba','prueba');

Recomendacion de seguridad
Yo no recomiendo como programador de esta app, que este ataque a la base de datos con el usuario root.
El usuario ROOT es el que tiene más privilegios, por lo cual tiene también acceso a las otras DB, este no se suele usar para desarollo.
Crea otro usuario especifico y este que unicamente tenga acceso a la DB especifica test.
Código (sql) [Seleccionar]

CREATE USER 'test'@'localhost' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON test.* TO 'test'@'localhost';

La ultima sentencia le doy todos los permisos al usuario test que tenga acceso a la DB test.
Lo increible, no es lo que ves, sino como es

DeMoNcRaZy

Cita de: Drakaris en 20 Diciembre 2021, 20:53 PM
Buenos dias.
¿Porque falla y como solucionarlo?
Código (python) [Seleccionar]

# Seleccionador base de datos
def crearCliente():
    crearCliente1 = "INSERT INTO test(id,name,email) VALUES ('5','prueba','prueba')"


El problema esta en la sentencia SQL.
La sintaxis es correcta pero los tipos de datos asociados de la columna no cuadran.

El id es una columna PRIMARY KEY, que identifica ese registro en concreto. Este normalmente es auto incremental que por cada insert que tu hagas este se incrementa.
El tipo de dato para un id autoincrementado es INT.

Como es INT (integer=entero), es un numero, por la cual no puedes añadir en el VALUES un '5', sino un 5.

El '5' no es igual a 5.
'5' con comillas simples ( ' ) o comillas dobles ( " ) significa que es un VARCHAR(), texto
5 (sin comillas) es un numero.


Propuesta de mejora

Configuraria en el gestor de bases de datos que la columna id fuera autoincrementado.

Yo no se como creas las tablas...si lo haces por interfaz grafica o SQL. Pero en SQL sería....
Código (SQL) [Seleccionar]

CREATE TABLE test1 (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    email VARCHAR(100) NOT NULL,
    CONSTRAINT test_PK PRIMARY KEY (id)
);


Si es AUTOINCREMENT no hace falta que insertes el 5 cuando hacer el INSERT.

Código (SQL) [Seleccionar]

INSERT INTO test(name,email) VALUES ('prueba','prueba');

Recomendacion de seguridad
Yo no recomiendo como programador de esta app, que este ataque a la base de datos con el usuario root.
El usuario ROOT es el que tiene más privilegios, por lo cual tiene también acceso a las otras DB, este no se suele usar para desarollo.
Crea otro usuario especifico y este que unicamente tenga acceso a la DB especifica test.
Código (sql) [Seleccionar]

CREATE USER 'test'@'localhost' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON test.* TO 'test'@'localhost';

La ultima sentencia le doy todos los permisos al usuario test que tenga acceso a la DB test.



Muchas gracias poe su respuesta se lo agradezco por dicha explicación.
Y tomaré nota respecto a lo de seguridad.

Pero respecto a lo crear una tabla no lo crea, he creado unas tabla nuevas con el SQL que me proporcionó igual.

La aplicación se ejecuta bien no da ningún fallo, pero no hace su función de insertar los datos.

Lo tengo así:

Código (python) [Seleccionar]
from tkinter import *
from tkinter import ttk
from conexion import *

####### FUNCIONES #######

def crearCliente():
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba','prueba')"   
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)


####### VENTANA Tkinter #######

root = Tk()
root.title("Mostrar datos")
root.geometry("400x400")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

##### Textos y Botones #####

ttk.Label(mainframe, text=results).grid(column=3, row=2, sticky=W)

ttk.Button(mainframe, text="BOTON", command=crearCliente).grid(column=40, row=40, sticky=W)



root.mainloop()


No da ningún fallo lo único que no introduce los datos en la base de datos. Ya que es una base de datos externa de "freemysqlhosting.net" por si tuviera algo que ver.

Cualquier información adicional lo agradezco.

Saludos.
Esta página web no está disponible - Google Chrome

Drakaris

Bunos dias!

Me alegro que hayas podido mejorar en crear la tabla con PK y id autoincrementado.
Por lo que veo, esta todo bién. Me gustaría ver el archivo conexion.py pues puede que este fallando algo.

Ten en cuenta que si estas trabajando con un Web Hosting, este en su panel te dará la direccion al que tienes que ir. Puedo intuir que deberia estar así actualmente tu conexion.py
Código (python) [Seleccionar]

import pymysql

# Abre conexion con la base de datos
db = pymysql.connect(host='freemysqlhosting.net',
                             user='root',
                             password='root',
                             database='test',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)
# [...]


Esto es un ejemplo, para saberlo tendrías que mirar en tu CPanel de tu proveedor de Hosting web. Un ejemplo sería:

https://imgur.com/gallery/itKoNsD

Ten en cuenta: que es este caso el host es 260mb.net pero para conexion con DB es sql113.260mb.net. Puede que el tuyo también sea algo así.

Espero haberte ayudado! Buenas fiestas y que te lo pases bien! :laugh: ;D
Lo increible, no es lo que ves, sino como es

DeMoNcRaZy

Buenas,

Nuevamente gracias por su intervención y perdona que aun no lio haya resuelto, aun que me parece raro de echo lo veo bien todo y tal como usted me dice, si la conexión esta bien tal como me indicas, ya que también tengo una consulta mostrando una tabla de la base de datos en la aplicación así que la conexión esta bien.

Le dejo los archivo totalmente como lo tengo originalmente, contraseña db y tal datos reales:

conexión.py:
Código (python) [Seleccionar]
import pymysql

# Abre conexion con la base de datos
db = pymysql.connect(host='sql11.freemysqlhosting.net',
                             user='sql11459959',
                             password='kcF1ibf3Me',
                             database='sql11459959',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)
##################################################
cursor = db.cursor()

# Seleccionador base de datos
sql = "SELECT name FROM test WHERE id = 2"

# Ejecutar comando
cursor.execute(sql)

# Mostrar
results = cursor.fetchall()

# Desconexion
db.close()



Aquí el main.py:
Código (python) [Seleccionar]
from tkinter import *
from tkinter import ttk
from conexion import *

####### FUNCIONES #######

def crearCliente():
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba','prueba')"   
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)


####### VENTANA Tkinter #######

root = Tk()
root.title("Mostrar datos")
root.geometry("400x400")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

##### Textos y Botones #####

ttk.Label(mainframe, text=results).grid(column=3, row=2, sticky=W)

ttk.Button(mainframe, text="BOTON", command=crearCliente).grid(column=40, row=40, sticky=W)



root.mainloop()


Eso es como lo tengo originalmente y corre bien la aplicación de echo me muestra un dato de la base de datos que es el nombre, que yo lo solicito en la conexión.py u con la variable "results" lo muestro en el main.py

Si quiere puede usted copiar y pegar esos dos códigos y debería funcionarle igual que a mí. Los datos de la base de datos son los reales para la conexión. (No importa que exponga lo datos reales, es un base de datos gratis y la estoy usando para pruebas nada más)

Cualquier información adicional lo agradezco.

Muchas gracias por su tiempo y dedicación.

Saludos cordiales.
Esta página web no está disponible - Google Chrome

Drakaris

#5
Buenos dias!

RECOMENDACION: Elimina los datos de conexion de DB, ya que han sido probados, no te interesa que cualquiera pueda entrar. Auque no sea relevante para ti, y solo es de prueba. Además esta entrando como root y algúien podría eliminar la DB del hosting y liartela....

Con tu permiso, he entrado en tu DB y he comprobado que funciona correctamente. (solo he insertado unos datos en la tabla test1 y lo he vaciado sus registros)

No habia caido en que al hacer la funcion crearCliente()
Código (python) [Seleccionar]

def crearCliente():
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba','prueba')"  
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)


No lo estas ejecutando en ninguna DB . Pues indicas el SQL pero no haces nada con el. Primero deberias de crear la conexion (como lo hicistes en conexion.py) y después ejecutar la variable crearCliente. Quedaria algo así

Código (python) [Seleccionar]

def crearCliente():
  try:
       db = pymysql.connect(....)
       cursor = db.cursor()
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba','prueba')"
       cursor.execute(crearCliente)
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)
  except:
       ttk.Label(mainframe, text="Error en la DB").grid(column=40, row=150, sticky=W)
  finally:
     try:
         cursor.close()
         db.close()
     finally:
        None


Ademas pondría un try/catch por si hay algun error en el SQL/conexion,etc....

Además le añado un finally para asegurar que siempre se me cierra la conexion, de un error o no.

Espero haberte ayudado y  felices fiestas  ;D
Lo increible, no es lo que ves, sino como es

DeMoNcRaZy

Cita de: Drakaris en 23 Diciembre 2021, 12:37 PM
Buenos dias!

RECOMENDACION: Elimina los datos de conexion de DB, ya que han sido probados, no te interesa que cualquiera pueda entrar. Auque no sea relevante para ti, y solo es de prueba. Además esta entrando como root y algúien podría eliminar la DB del hosting y liartela....

Con tu permiso, he entrado en tu DB y he comprobado que funciona correctamente. (solo he insertado unos datos en la tabla test1 y lo he vaciado sus registros)

No habia caido en que al hacer la funcion crearCliente()
Código (python) [Seleccionar]

def crearCliente():
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba','prueba')"  
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)


No lo estas ejecutando en ninguna DB . Pues indicas el SQL pero no haces nada con el. Primero deberias de crear la conexion (como lo hicistes en conexion.py) y después ejecutar la variable crearCliente. Quedaria algo así

Código (python) [Seleccionar]

def crearCliente():
  try:
       db = pymysql.connect(....)
       cursor = db.cursor()
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba','prueba')"
       cursor.execute(crearCliente)
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)
  except:
       ttk.Label(mainframe, text="Error en la DB").grid(column=40, row=150, sticky=W)
  finally:
     try:
         cursor.close()
         db.close()
     finally:
        None


Ademas pondría un try/catch por si hay algun error en el SQL/conexion,etc....

Además le añado un finally para asegurar que siempre se me cierra la conexion, de un error o no.

Espero haberte ayudado y  felices fiestas  ;D



Nuevamente, mil gracias por todo su tiempo y esfuerzo.
Ahora ya si vamos un paso más, pero falta algún detalle que no cuadra, se ejecuta la aplicación perfectamente pero al pulsar el botón me manda al "expcet" "ERROR en la db" y no se inserta los datos en la base de datos.

Y en la consola del sublime text me salta este error cuando pulso el botón.
Me señala prueba.close()

Código (python) [Seleccionar]
finally:
try:
prueba.close()
db.close()
finally:
None


Y el error que pone en consola es esto:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tkinter/__init__.py", line 1921, in __call__
    return self.func(*args)
  File "/Users/tomas/Downloads/Pruebas/main.py", line 27, in crearCliente
    prueba.close()
UnboundLocalError: local variable 'prueba' referenced before assignment
[Finished in 10.2s]



Y este es el código total que tengo ahora mismo del main.py
Código (python) [Seleccionar]
from tkinter import *
from tkinter import ttk

####### FUNCIONES #######

city = (9, 'Kiev', 2887000)

def crearCliente():
try:
db = pymysql.connect(host='sql11.freemysqlhosting.net',
user='sql11459959',
password='kcF1ibf3Me',
database='sql11459959',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor)

prueba = db.cursor()
crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba', 'prueba')"
prueba.execute(crearCliente)
ttk.Label(mainframe, text="Haz creado un cliente").grid(column=40, row=150, sticky=W)

except:
ttk.Label(mainframe, text="Error en la DB").grid(column=40, row=150, sticky=W)

finally:
try:
prueba.close()
db.close()
finally:
None


####### VENTANA Tkinter #######

root = Tk()
root.title("Mostrar datos")
root.geometry("400x400")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

##### Textos y Botones #####

ttk.Label(mainframe, text="nada").grid(column=3, row=2, sticky=W)

ttk.Button(mainframe, text="BOTON", command=crearCliente).grid(column=40, row=40, sticky=W)



root.mainloop()



¿Cuál puede ser el problema ahora? Ahora que empieza a cuadrar las cosas, parece que hay una sola pieza que falta supongo, he estado mirando y tal pero me lanza al error de .close()

Muchas gracias por sus intervenciones y perdona todo este contratiempo, se lo agradezco mucho.

Y por supuesto ¡FELIZ NAVIDAD IGUALMENTE!
Esta página web no está disponible - Google Chrome

Drakaris

Buenas!
Disculpa las molestias cuando hice el finally que dentro hay otro try, debe de haber un except no otro finally, por la cual se debe quedar así
Código (python) [Seleccionar]

  finally:
     try:
         cursor.close()
         db.close()
     except:
        None

Pues queremos que si hay conexion se cierre y sino no hace nada.
Además da error porque requiere del modulo pymysql, cuyo modulo no has importado en ningun sitio
Código (python) [Seleccionar]

import pymysql


Si no lo tienes instalado, lo puedes instalar haceindo
Citarpip3 install pymysql

Buenas fiestas, y espero que te haya ayudado y te salga...Suerte  :D
Lo increible, no es lo que ves, sino como es

.xAk.

#8
¿Aún está la contraseña ahí escrita? voy leyendo por ahí arriba. Esto es problema de permisos seguramente, como habéis comentado. Actuar sobre la base de datos debe ser desde un origen determinado. Sigo leyendo por ahí arriba ... ya puedes ir cambiando la contraseña no sea que alguno de por ahí la haya visto

No es que te hagan algo malo o bueno, es que te la quitan, ya sea de pago o gratis



DeMoNcRaZy

Buenas, espero que las fiestas bien a tod@s!!

Gracias a tod@s por la intervención en el tema, me ha resultado de gran ayuda a como resolver algunas cosas y aprender como estructurar todo mejor.

Ahora tendría una pequeña duda más a resolver, y es que por ejemplo por casa clase que cree ejecutando un comando de mostrar o insertar datos de la base de datos tengo que crear una conexión a la base de datos, ejemplo yo tengo estas clases y en cada una he tenido que crear una conexión a la base de datos si no no me funcionaba y me daba error, ¿hay alguna manera de solo con una conexión usarla para todos los archivos?

Código (python) [Seleccionar]
import pymysql

con = pymysql.connect(host='sql11.freemysqlhosting.net',
                             user='sql11459959',
                             password='kcF1ibf3Me',
                             database='sql11459959',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)

def crearCliente():
    try:
        cur = con.cursor()
        crearCliente = "INSERT INTO test1(name,email) VALUES ('prueba', 'prueba')"
        cur.execute(crearCliente)
        con.commit()

        #con.close()

        print("Se guardo correctamente")
    except:
        print ("Error")

    finally:
        try:
            con.close()
        except:
            None


def mostrarCliente():
    try:
        con1 = pymysql.connect(host='sql11.freemysqlhosting.net',
                             user='sql11459959',
                             password='kcF1ibf3Me',
                             database='sql11459959',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)


        cur1 = con1.cursor()
        mostrarCliente = "SELECT * FROM test1 WHERE id = 19"
        cur1.execute(mostrarCliente)
        rows = cur1.fetchall()

        #con.close()

        print("El cliente es:", rows)
    except:
        print("Error2")

    finally:
        try:
            con1.close()
        except:
            None




crearCliente()
mostrarCliente()
print ("Se ha guardado")


Por cada clase tengo una conexión nueva a la base de datos si no me da falla, así me funciona todo bien de momento.

¿Hay alguna manera que con una conexión que haga en el mismo archivo o un archivo diferente ejemplo conexión.py lo importe al archivo principal y use esa conexión para todos los archivos?

¿O hay que hacer siempre una conexión nueva a la base de datos por cada clase y comando que ejecute?


Muchas gracias por todo, y nuevamente ¡¡FELICES FIESTAS!!
Esta página web no está disponible - Google Chrome