Trabalhando com um banco de dados no Flask: de junho a junho

Fui inspirado a escrever este artigo pelo desejo de ajudar os mesmos recém-chegados ao Python em geral e trabalhar com o Flask em particular, como eu. Enquanto trabalhava na tarefa de uma explicação holística e compreensível, no estilo que nós, recém-chegados, não encontramos. Eu tive que procurar as informações pouco a pouco. Não haverá fotos. Um artigo puramente técnico. Pessoas experientes serão gratas por comentários e dicas sobre como melhorar o código.

Então vamos começar.

Comecei a aprender Python logo após o Ano Novo. Depois de quatro meses, percebi que a teoria, além de treinar em tarefas fictícias que não dão certo, você não aprenderá especialmente, decidi procurar tarefas de "combate". Para fazer isso, perguntei aos meus amigos se eles tinham alguma tarefa real que precisava ser programada. Um "meu amigo amigo" me pediu para implementar um bot para o Telegram (sinto falta da essência do bot; isso não importa; eu precisava executar qualquer tarefa que tenha um cliente específico e implementá-lo como o cliente desejar).

Em busca de uma solução, é natural que a primeira coisa que comecei sejam os conhecidos frameworks Python Telegram Bot e pyTelegramBotAPI.. No começo, como iniciante, pareceu-me apenas uma descoberta - era possível, especialmente sem entender as nuances do código, começar rapidamente a "serrar" o bot real. Por alguns dias, me deparei com o fato de que não consigo criar a funcionalidade necessária. Parece que ele fez tudo de acordo com a documentação, mas não foi decidido. Então percebi que não entendia como a estrutura funciona "sob o capô" e por que algo que deveria funcionar não funciona para mim; onde e quais comandos devem ser chamados e com quais métodos. Em geral, decidi deixar de lado a estrutura e tentar entender mais profundamente como a API do Telegram funciona e como você pode trabalhar diretamente com ela, o que me dará mais controle sobre a situação e também me permitirá estudar mais de perto toda a cozinha de trabalhar com a API. É possível que eu não volte a usar as estruturas Python Telegram Bot e pyTelegramBotAPI como desnecessárias.Ou talvez eu volte para simplificar meu trabalho na criação de minha própria bicicleta com a API do Telegram. Mas mesmo se eu voltar, entenderei muito mais sobre o trabalho dessas estruturas.

Eu tive um curso não revisado na Udemy apenas para criar um bot para o Telegram. No momento da redação deste artigo, este era o único curso da Udemy em que uma pessoa resolvia um problema sem o Python Telegram Bot e o pyTelegramBotAPI (não darei um link, para que isso não seja um anúncio). Para resolvê-lo, ele usou o Flask. Aqui, a propósito, depois de seguir um certo "caminho militar", tive a oportunidade de escrever meu curso sobre esse tópico, embora, é claro, seja muito cedo - não trarei nenhum valor extra. Mas se você, um programador mais experiente que está lendo este artigo, sabe muito sobre isso, pode criar seu próprio curso e ficarei feliz em comprá-lo por US $ 10,99 (um preço típico de "desconto" para a Udemy) para aprender algo novo.

Em geral, desde o curso, percebi que o Flask me permitirá facilitar a vida do processamento de solicitações GET e POST.

Como este artigo é especificamente dedicado ao trabalho com o banco de dados, falarei sobre isso. Embora, o tempo todo, seja tentador descrever outras sutilezas, como: transferir as configurações de conexão para um arquivo "secreto", receber e processar dados recebidos da API do Telegram, como receber webhooks do Telegram em um computador local sem um certificado SSL. Se você tiver interesse, entre em contato e escreverei um artigo separado.

Cerca de três a quatro dias do meu período de junho, demorei a entender que eu precisava me afastar da estrutura antes de poder receber dados do Telegram com confiança, processar o texto (análise) e, dependendo da o que o usuário escreveu, envie a ele os comandos necessários, bem como os botões (incluindo substituir a mensagem e alterar os botões).

O próximo passo para mim foi conectar o banco de dados para armazenar usuários que trabalham com o bot nele, além de dados sobre sua interação no bot como parte da tarefa que o confrontou.

A primeira coisa que fiz no Google foi um artigo de Habré com uma tradução do mega-livro Miguel Grinberg (o original de seu blog está aqui ).

O quarto capítulo trata da conexão com um banco de dados usando o Flask e o SQLAlchemy ORM. Então pensei: "Uau, que legal, agora o problema está resolvido." Curiosamente, mas a estrutura de arquivos sugerida pelo autor não funcionou para mim.

Eu fiz por analogia como a dele:

microblog\
  venv\
  app\
    __init__.py
    models.py
  main.py

No main.py, faço todo o trabalho principal com a API do Telegram.
No app \ models.py, crio classes para o banco de dados.
No app \ __ init__.py, tudo foi feito como Miguel Grinberg escreveu.

Mas, por alguma razão, no main.py, eles não queriam me retirar do app import db, app. Passei cerca de uma hora procurando problemas, soluções na Internet. Como resultado, Oleg Molchanov encontrou um canal no YouTube e seu vídeo "Criando um blog no Flask (lições) - Criando postagens (modelos) e SQLAlchemy". Lá, observei como ele faz uma conexão com o banco de dados e tentei seguir esse caminho (sem colocar o arquivo models.py no diretório do aplicativo, sem criar __init__.py.

Em geral, agora a estrutura do meu projeto é simples de desonrar (e um pouco feia, o que me confunde, talvez no futuro, entenderei como melhorar a estrutura):

imagem

Como você pode ver, eu tenho app.py, models.py, main.py, app.sqlite (o restante dos arquivos não está relacionado ao tópico atual).

No app.py, tenho este código:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)

Em models.py:

from datetime import datetime
from app import db

class Users(db.Model):
    #   
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(120), index=True, unique=True)
    last_name = db.Column(db.String(128))
    first_name = db.Column(db.String(128))
    created = db.Column(db.DateTime, default=datetime.now())
    tasks = db.relationship('Tasks', backref='tasks')

    #     
    # def __init__(self, *args, **kwargs):
    #     super(Users, self).__init__(*args, **kwargs)

    def __init__(self, username, last_name, first_name):
        self.username = username
        self.last_name = last_name
        self.first_name = first_name

    def __repr__(self):
        return '<User {}>'.format(self.username)


class Tasks(db.Model):
    #    
    __tablename__ = 'tasks'
    id = db.Column(db.Integer, primary_key=True)
    owner_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
    name = db.Column(db.String(120), index=True)
    start = db.Column(db.Boolean, default=False)
    finish = db.Column(db.Boolean, default=False)
    created_on = db.Column(db.DateTime, default=datetime.now())
    updated_on = db.Column(db.DateTime(), default=datetime.utcnow, onupdate=datetime.utcnow)

    #     
    def __init__(self, *args, **kwargs):
        super(Tasks, self).__init__(*args, **kwargs)

    # def __init__(self, name, last_name, first_name):
    #     self.name = name
    #     self.last_name = last_name
    #     self.first_name = first_name

    def __repr__(self):
        return '<Tasks {}>'.format(self.name)

No config.py:

import os
from dotenv import load_dotenv
load_dotenv()

basedir = os.path.abspath(os.path.dirname(__file__))


#    
class Config(object):
    DEBUG = True
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
    # SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{user}:{pw}@{url}/{db}'.format(user=os.environ.get('POSTGRES_USER'),
    #                                                                                 pw=os.environ.get('POSTGRES_PW'),
    #                                                                                 url=os.environ.get('POSTGRES_URL'),
    #
    #                                                                                 db=os.environ.get('POSTGRES_DB'))
    SQLALCHEMY_DATABASE_URI = (os.environ.get('DATABASE_URL') or
                               'sqlite:///' + os.path.join(basedir, 'app.sqlite')) + '?check_same_thread=False'
    SQLALCHEMY_TRACK_MODIFICATIONS = False  # silence the deprecation warning

Como você já entendeu, das configurações, removi os valores diretos da conexão e os renderizei em .env, para que eles não brilhassem no prod ou no SVN.

Primeiro, o app.db (onde o app.sqlite está agora) foi gravado em SQLALCHEMY_DATABASE_URI como banco de dados. Sim, sim, e anotado, conforme indicado nas instruções Miguel Grinberg. Espero que você entenda que precisa substituí-lo por .sqlite. Antes disso, pensei em uma hora depois de tentativas dolorosas de executar o código.

Você pode criar bancos de dados e tabelas no sqlite acessando o console de depuração (por exemplo, em PyCharm, eu tenho Ferramentas -> Python ou Debug Console:

imagem

Aqui, primeiro conectamos o método que precisamos no arquivo desejado (eu o tenho dos modelos import db) e, após uma conexão bem-sucedida, especifique o comando db.create_all (). Depois disso, um banco de dados com todas as tabelas necessárias será criado. Vale a pena saber que, se eu fizer o app import db e executar o comando db.create_all (), o arquivo do banco de dados parecerá criado, mas haverá algum tipo de absurdo, e não o banco de dados (não entendi o porquê).

Quando resolvi esse problema, pensei novamente que agora não há mais dificuldades. Resta apenas gravar uma função para main.py, que, sob certos eventos, grava dados do Telegram no banco de dados. Bem, eu conectei de modelos importamos Users e no lugar certo chamamos a função:

try:
    find_user_in_db(username, first_name, last_name)
except NameError:
    print("user  ")
except:
    print("An exception occurred")

def add_users_to_db(username, last_name, first_name):
    """
        
    :return:
    """
    data = Users(username, last_name, first_name)
    db.session.add(data)
    db.session.commit()

def find_user_in_db(username, first_name, last_name):
    """
               
    :param username:
    :param first_name:
    :param last_name:
    :return:
    """
    user = db.session.query(Users).filter(Users.username == f'{username}').all()
    if not user:
        add_users_to_db(username, first_name, last_name)

Na última função, primeiro em vez de user = db.session.query (Users) .filter (Users.username == f '{nomedeusuário}'). Primeiro_ou_404 () estava Users.query.filter_by (nomedeusuário = '{nomedeusuário}'). first_ou_404 (). Aqui, também gastei cerca de meia hora para entender que essa consulta no banco de dados não funciona e não recebe nenhum dado; portanto, a consulta ao banco de dados não é enviada. O Google publicou um pequeno artigo no qual as pessoas diziam que é melhor usar o db.session.query (Usuários). Por que, eu xs, não perdi tempo analisando. Depois disso, os dados começaram a ser gravados no banco de dados.

Isso conclui este artigo, conforme descrevi o que queria descrever e resolvi o problema de conectar o banco de dados ao Flask.

Este artigo foi escrito apenas pelo motivo de "junho" amarmos ao ler as instruções. Eu também tentei encontrar instruções para conectar o banco de dados ao Flask. Como não encontrei instruções completas, tive que atravessar os obstáculos. Decidi descrever minha experiência como a próxima a procurar instruções prontas.

PS: Quem está procurando uma solução pronta, então pergunto ao repositório no GitHub

PSS. Ficarei feliz em saber se alguém faz uma revisão de código e dá suas recomendações para aprimoramento.

Obrigado pela atenção.

All Articles