Trabajando con una base de datos en Flask: de junio a junio

Me inspiró escribir este artículo por el deseo de ayudar a los mismos recién llegados a Python en general y a trabajar con Flask en particular, como yo mismo. Mientras trabajaba en la tarea de una explicación holística y comprensible en el estilo que nosotros, los recién llegados, no encontramos, como. Tuve que buscar la información poco a poco. No habrá fotos. Un artículo puramente técnico. Las personas con experiencia agradecerán comentarios y sugerencias para mejorar el código.

Entonces empecemos.

Comencé a aprender Python justo después del Año Nuevo. Después de cuatro meses, me di cuenta de que la teoría, además del entrenamiento en tareas ficticias que no entran en producción, no aprenderás especialmente, decidí buscar tareas de "combate". Para hacer esto, le pregunté a mis amigos si tenían tareas reales que debían programarse. Un "amigo mío" me pidió que implementara un bot para Telegram (echo de menos la esencia del bot; no importa, necesitaba tomar cualquier tarea que tenga un cliente específico e implementarlo como el cliente quiera).

En la búsqueda de una solución, es natural que lo primero con lo que empecé es el conocido marco de trabajo Python Telegram Bot y pyTelegramBotAPI.. Al principio, como principiante, me pareció un hallazgo: fue posible, especialmente sin comprender los matices del código, comenzar rápidamente a "aserrar" el bot real. Durante un par de días me encontré con el hecho de que no puedo crear la funcionalidad que necesito. Parece que hizo todo de acuerdo con la documentación, pero no se decidió. Entonces me di cuenta de que no entendía en absoluto cómo funciona el marco "bajo el capó" y por qué algo que debería funcionar no funciona para mí; dónde y qué comandos deberían llamarse y con qué métodos. En general, decidí dejar a un lado el marco e intentar comprender más profundamente cómo funciona la API de Telegram y cómo puedes trabajar con ella directamente, lo que me dará más control sobre la situación y también me permitirá estudiar más de cerca toda la cocina de trabajar con la API. Es posible que no vuelva a utilizar los marcos Python Telegram Bot y pyTelegramBotAPI como innecesarios.O tal vez volveré para simplificar mi trabajo en la creación de mi propia bicicleta con Telegram API. Pero incluso si regreso, entenderé mucho más sobre el trabajo de estos marcos.

Tuve un pequeño curso no revisado sobre Udemy solo para crear un bot para Telegram. En el momento de escribir este artículo, este era el único curso de Udemy en el que una persona resolvería un problema sin Python Telegram Bot y pyTelegramBotAPI (no daré un enlace para que no sea un anuncio). Para resolverlo, usó Flask. Aquí, por cierto, después de atravesar un cierto "camino militar" tuve la oportunidad de escribir mi curso sobre este tema, aunque, por supuesto, es demasiado temprano, no aportaré ningún valor especial. Pero si usted, un programador con más experiencia que lee este artículo, sabe mucho sobre esto, puede crear su propio curso y estaré encantado de comprarlo por $ 10.99 (un precio de "descuento" típico para Udemy) para aprender algo nuevo.

En general, desde el curso, me di cuenta de que Flask me permitirá hacer la vida más fácil para procesar las solicitudes GET y POST.

Como este artículo está dedicado específicamente a trabajar con la base de datos, hablaré sobre esto. Aunque, todo el tiempo, es tentador describir otras sutilezas, como: transferir la configuración de conexión a un archivo "secreto", recibir y procesar los datos recibidos de la API de Telegram, cómo recibir webhooks de Telegram en una computadora local sin un certificado SSL Si tiene interés, hágamelo saber y escribiré un artículo por separado.

Aproximadamente 3-4 días de mi tiempo de junio me llevó a comprender que necesitaba alejarme del marco antes de poder recibir con confianza los datos del Telegram, procesar el texto (análisis) y, según lo que escribió el usuario, envíele los comandos necesarios, así como los botones (incluso sobrescribir el mensaje y cambiar los botones).

El siguiente paso para mí fue conectar la base de datos para almacenar usuarios que trabajan con el bot, así como datos sobre su interacción en el bot como parte de la tarea que lo confrontaba.

Lo primero que hice en Google fue un artículo de Habré con una traducción del mega-libro de texto Miguel Grinberg (el original de su blog está aquí ).

El cuarto capítulo trata de conectarse a una base de datos usando Flask y SQLAlchemy ORM. Entonces pensé: "Guau, qué genial, ahora el problema está resuelto". Curiosamente, pero la estructura de archivos que sugirió el autor no funcionó para mí.

Lo hice por analogía como el suyo:

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

En main.py, hago todo el trabajo principal con la API de Telegram.
En app \ models.py, creo clases para la base de datos.
En la aplicación \ __ init__.py, todo se hizo como escribió Miguel Grinberg.

Pero por alguna razón, en main.py, no querían sacarme de db import app, app. Pasé aproximadamente una hora buscando problemas, soluciones en Internet. Como resultado, Oleg Molchanov se encontró con un canal de YouTube y su video "Creación de un blog en frasco (lecciones) - Creación de publicaciones (modelos) y SQLAlchemy". Allí observé cómo se conecta a la base de datos e intenté hacerlo de esta manera (sin poner el archivo models.py en el directorio de la aplicación, sin crear __init__.py.

En general, ahora la estructura de mi proyecto es fácil de deshonrar (y un poco feo, lo que me confunde, tal vez en el futuro entenderé cómo mejorar la estructura):

imagen

Como puede ver, tengo app.py, models.py, main.py, app.sqlite (el resto de los archivos no se relacionan con el tema actual).

En app.py tengo 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)

En 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)

En 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 ya entendió, de la configuración eliminé los valores directos para la conexión y los procesé en .env para que no brillaran en el producto o en SVN.

Primero, se me escribió app.db (donde está app.sqlite ahora) en SQLALCHEMY_DATABASE_URI como la base de datos. Sí, sí, y anotó, como se indica en las instrucciones de Miguel Grinberg. Espero que entiendas que necesitas reemplazarlo con .sqlite. Antes de eso, pensé en una hora después de intentos dolorosos de ejecutar el código.

Puede crear bases de datos y tablas en sqlite yendo a la consola de depuración (por ejemplo, en PyCharm tengo Herramientas -> Python o Consola de depuración:

imagen

Aquí, primero conectamos el método que necesitamos en el archivo deseado (lo tengo de los modelos import db), y después de una conexión exitosa, especificamos el comando db.create_all (). Después de eso, se creará una base de datos con todas las tablas necesarias. Vale la pena saber que si hago desde import app db y ejecuto el comando db.create_all (), entonces el archivo de la base de datos parece haber sido creado, pero será una tontería y no la base de datos (no entendí por qué).

Cuando resolví este problema, pensé de nuevo que ahora no quedan dificultades. Solo queda escribir una función para main.py, que, bajo ciertos eventos, escribirá datos del Telegram en la base de datos. Bueno, me conecté desde modelos de importación de usuarios y en el lugar correcto llamé a la función:

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)

En la última función, primero en lugar de user = db.session.query (Users) .filter (Users.username == f '{username}'). First_or_404 () stand Users.query.filter_by (username = '{username}'). first_or_404 (). Aquí también pasé aproximadamente media hora para comprender que esta consulta en la base de datos no funciona y no recibe ningún dato, por lo que no se envía la consulta a la base de datos. Google solicitó un pequeño artículo en el que la gente decía que es mejor usar db.session.query (Usuarios). Por eso, no sé, no perdí el tiempo analizando. Después de eso, los datos comenzaron a escribirse en la base de datos.

Esto concluye este artículo porque describí lo que quería describir y resolví el problema de conectar la base de datos a Flask.

Este artículo está escrito solo porque "junio" amamos al leer las instrucciones. Yo también intenté encontrar instrucciones para conectar la base de datos a Flask. No encontré ninguna instrucción completa, así que tuve que abrirme paso a través de los obstáculos. Bueno, decidí describir mi experiencia como la próxima que buscará instrucciones ya hechas.

PD: ¿Quién está buscando una solución preparada? Luego, le pido al repositorio en GitHub

PSS que me alegrará si alguien hace una revisión del código y da sus recomendaciones para mejorar.

Gracias por la atención.

All Articles