Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

class JogoDao / class UsuarioDao ???? Cade a explicação ????

Até este momento não foi explicado de maneira clara como foi construído os métodos da classe JogoDao/ UsuarioDao , quanto a criação do banco e das tabelas com os comandos sql, tudo ok, fiz o curso de introdução ao Mysql e compreendi perfeitamente a lógica da criação das tabelas, porém, em nenhum momento até aqui o instrutor explicou como ele construiu os métodos das classes, fez apenas uma explicação extremamente superficial. Estamos aqui para aprender, sendo assim, é importante que cada passo dado pelo instrutor seja explicado de maneira clara. A classe referida deveria ter sido criada junto com o aluno, como vinha acontecendo até agora.

Em que curso posso encontrar mais explicações a respeito da comunicação entre banco de dados e python ?

4 respostas

Oi Bruno, tudo bem?

Com o framework flask não temos cursos que abordem mais explicações entre a comunicação de um banco e Python.

Sobre as classes JogoDao e UsuarioDao, tentarei explicar com mais clareza como elas estão funcionando, começando pela JogoDao, que é responsável por fazer as consultas referentes ao jogo em nosso banco de dados:

  • No método construtor, o init recebe como parâmetro uma instância do banco de dados, ou seja, quando instanciarmos um objeto da classe JogoDao temos que passar como parâmetro o nosso banco:

Método init

 def __init__(self, db):
        self.__db = db

Chamada da classe JogoDao

db = MySQL(app)
jogo_dao = JogoDao(db)

Observe que nesta chamada da classe JogoDao, o MySQL está recebendo como parâmetro as configurações do nosso banco de dados, bem como usuário, banco, senha... e elas são feitas por meio de uma estrutura de dicionário, onde as chaves(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD....) são definidas com base na documentação.

app.config['MYSQL_HOST'] = "localhost"
app.config['MYSQL_USER'] = "root"
app.config['MYSQL_PASSWORD'] = "admin"
app.config['MYSQL_DB'] = "jogoteca"
app.config['MYSQL_PORT'] = 3306

Recapitulando o que é feito neste primeiro momento:

  • Passamos as configurações do nosso banco para instância do MySQL
  • Pegamos esta instância do MySQL que foi configurada e passamos para o nosso método inicializador na classe JogoDao, que por enquanto só guardará a instância que foi passada: self.__db = db

Dando prosseguimento a classe JogoDao, iremos ter o método salvar:

 def salvar(self, jogo):
        cursor = self.__db.connection.cursor()

        if (jogo.id):
            cursor.execute(SQL_ATUALIZA_JOGO, (jogo.nome, jogo.categoria, jogo.console, jogo.id))
        else:
            cursor.execute(SQL_CRIA_JOGO, (jogo.nome, jogo.categoria, jogo.console))
            jogo.id = cursor.lastrowid
        self.__db.connection.commit()
        return jogo

Neste método, a linha: cursor = self.__db.connection.cursor() é responsável por fazer a conexão com o banco, onde, este método irá nos retornar um objeto do nosso banco de dados. Então o que é acontece é o seguinte:

  • Fizemos todas as configurações do banco
  • Passamos para a instância do MySQL
  • Passamos esta instância do MySQL para a nossa classe JogoDao e a classe guardou esta instância(no método init)

Só que agora, queremos utilizar esta instância do nosso banco que estava guardada. E para ela ficar apta para uso, ou seja, nos permitir inserir, deletar, modificar... precisamos chamar o método : connection.cursor() para a variável que possui a instância do nosso banco, por isso de: self.__db.connection.cursor().

Daí, logo abaixo temos um if que verifica se o jogo possui id. Essa verificação é pelo fato de que se o jogo já possuir um id quer dizer que ele já foi criado. Certo?! Então o que queremos fazer é atualizar este jogo:

if (jogo.id):
            cursor.execute(SQL_ATUALIZA_JOGO, (jogo.nome, jogo.categoria, jogo.console, jogo.id))

O método execute é responsável por executar um SQL. Ele recebe como primeiro parâmetro a SQL e como segundo parâmetro uma tupla com as variáveis que queremos mandar para o nosso banco.

Caso o jogo não tenha um id, quer dizer que ele ainda não foi criado, então entraremos na condição de else:

else:
    cursor.execute(SQL_CRIA_JOGO, (jogo.nome, jogo.categoria, jogo.console))
    jogo.id = cursor.lastrowid

Temos aqui a mesma estrutura de um execute que foi explicado acima. E por fim, na linha: jogo.id = cursor.lastrowid, o método lastrowid é responsável por nos retornar o id do último jogo inserido no banco.

Lembrando que o id é inserido no banco de forma automática, pois, na criação da tabela declaramos que ele será um autoincrement:

`id` int(11) NOT NULL AUTO_INCREMENT,

Você deve estar se perguntando: porque é importante retornar o último id? Neste caso, a utilidade dele é porque quando vamos atualizar algum jogo temos que passar também o id deste jogo.

No fim do método de criar, temos:

self.__db.connection.commit()
        return jogo

Quando fazemos connection.commit() é para que as informações sejam de fato realizadas no banco, esse método é essencial, sem ele, as informações não seriam inseridas. E por último, retornamos o jogo, pois lembra que jogo.id foi alterado? É por isso que retornamos o jogo.

Ficou claro esta parte? Fique a vontade para questionar se houver ficado alguma dúvida até aqui.

Vamos agora para o método listar, ele se assemelha em alguns pontos com o método salvar, pois, ele também faz a conexão com o banco de dados e utiliza o método execute.

 def listar(self):
        cursor = self.__db.connection.cursor()
        cursor.execute(SQL_BUSCA_JOGOS)
        jogos = traduz_jogos(cursor.fetchall())
        return jogos

O que difere neste método é a utilização do cursor.fetchall() que será responsável por buscar todas as linhas de resultado da consulta da SQL_BUSCA_JOGOS. E como retorno, nos dará uma tupla de tuplas. Algo como:

((4, 'Super Mario', 'RPG', 'SNES'), (32, 'FIFA', 'Esporte', 'SNES'))

Porém, observe que o o cursor.fetchall() é passado como parâmetro da função traduz_jogos. Vamos analisar o que ela faz:

def traduz_jogos(jogos):
    def cria_jogo_com_tupla(tupla):
        return Jogo(tupla[1], tupla[2], tupla[3], id=tupla[0])
    return list(map(cria_jogo_com_tupla, jogos))

Dentro desta função traduz_jogos iremos quebrar esta tupla, ou seja, separá-la para que consigamos montar um Jogo com os dados que temos no banco. Então se temos um tupla:

(4, 'Super Mario', 'RPG', 'SNES')

O que acontece é:

  • Tupla na posição 0 é o id
  • Tupla na posição 1 é o nome do jogo
  • Tupla na posição 2 é a categoria
  • Tupla na posição 3 é o tipo do console

Por isso que retornamos a classe Jogo com as posições da tupla.

 return Jogo(tupla[1], tupla[2], tupla[3], id=tupla[0])

E quando retornamos um: list(map(cria_jogo_com_tupla, jogos)) é para que a função cria_jogo_com_tupla será aplicada em todos os jogos até que não tenha mais tuplas.

No método busca_por_id, também temos alguns casos comuns, com exceção do método fetchone que é responsável por retornar um único registro da consulta. Lembrando que ele também retorna uma tupla.

def busca_por_id(self, id):
        cursor = self.__db.connection.cursor()
        cursor.execute(SQL_JOGO_POR_ID, (id,))
        tupla = cursor.fetchone()
        return Jogo(tupla[1], tupla[2], tupla[3], id=tupla[0])

Por fim, para findar a classe JogoDao, temos o método deletar:

def deletar(self, id):
        self.__db.connection.cursor().execute(SQL_DELETA_JOGO, (id, ))
        self.__db.connection.commit()

Neste método, observe que o instrutor fez algo diferente, ele encadeou a chamada de um execute quando fez a conexão com o banco. E isso é normal, não dará erro algum.

Para mandar as alterações para o banco ele utilizou o commit(). Novamente, sem o commit não temos nada feito, ele é crucial para que consigamos enviar informações para o banco.

Vale ressaltar alguns pontos da classe JogoDao:

  • O método listar e o método busca_por_id não é necessário utilizar o commit(), porque queremos apenas pegar alguma informação do banco

  • No método listar só passamos como parâmetro para o execute a SQL que faz a consulta já que queremos apenas pegar os dados por meio de um select

  • O método traduz_jogos, que está fora da classe JogoDao é importante para consigamos montar uma instância da classe Jogo(do arquivo models.py) com os dados que temos no banco.

Se houver ficado dúvidas é só falar.

solução!

A classe UsuarioDao possui alguns pontos em comum com a classe JogoDao, por exemplo:

  • o método init da classe UsuarioDao também guarda uma instância do nosso banco.

Vamos analisar agora o método buscar_por_id:

 def buscar_por_id(self, id):
        cursor = self.__db.connection.cursor()
        cursor.execute(SQL_USUARIO_POR_ID, (id,))
        dados = cursor.fetchone()
        usuario = traduz_usuario(dados) if dados else None
        return usuario

Note que nele, primeiramente fazemos a conexão com o nosso banco, por meio do connection.cursor() e posterior a isso, executamos a SQL responsável por buscar um usuário por id. Depois, utilizamos novamente o método fetchone para nos retornar apenas um único usuário do banco, onde este retorno será uma tupla. Já na linha:

 usuario = traduz_usuario(dados) if dados else None

A lógica é a seguinte:

  • Se a função traduz_usuario nos retornar algum dado, guarde estes dados na variável usuário
  • Caso a função traduz_usuario não nos retorne nada, coloque na variável usuario o valor de None

E por fim, retornamos o usuário.

Eu sei que são muitas informações, mas fique tranquilo. Caso não entenda algo é só falar.

Espero ter ajudado. Abraços e bons estudos!

Nádia, muito obrigado pela explicação! Depois de um tempo eu já havia conseguido desenvolver minha lógica para realizar essas ações na aplicação, mas sua explicação enriqueceu mais ainda meu entendimento. Agradeço!