العمل بقاعدة بيانات في قارورة: من يونيو إلى يونيو

لقد ألهمتني كتابة هذا المقال برغبتي في مساعدة نفس القادمين الجدد إلى Python بشكل عام والعمل مع Flask على وجه الخصوص ، كما أنا نفسي. أثناء العمل على مهمة التفسير الشامل والمفهوم بالأسلوب الذي لم نعثر عليه نحن الوافدين الجدد. كان علي أن أبحث عن المعلومات شيئا فشيئا. لن تكون هناك صور. مقال فني بحت. سيكون الأشخاص ذوو الخبرة ممتنين للتعليقات وللحصول على نصائح حول تحسين الرمز.

لذلك دعونا نبدأ.

لقد بدأت تعلم بايثون مباشرة بعد العام الجديد. بعد أربعة أشهر ، أدركت أن النظرية ، بالإضافة إلى التدريب على المهام الخيالية التي لا تتأرجح ، لن تتعلم بشكل خاص ، قررت البحث عن المهام "القتالية". للقيام بذلك ، سألت أصدقائي إذا كان لديهم أي مهام حقيقية تحتاج إلى برمجتها. طلب مني أحد "صديقي الصديق" تنفيذ روبوت لـ Telegram (أفتقد جوهر البوت ؛ لا يهم - كنت بحاجة لأخذ أي مهمة لها عميل معين وتنفيذها كما يريد العميل).

بحثًا عن حل ، من الطبيعي أن يكون أول شيء بدأت به هو Python Telegram Bot و pyTelegramBotAPI.. في البداية ، كمبتدئ ، بدا لي مجرد اكتشاف - كان من الممكن بشكل خاص دون فهم الفروق الدقيقة في التعليمات البرمجية لبدء "نشر" الروبوت الحقيقي بسرعة. لبضعة أيام ، صادفت حقيقة أنه لا يمكنني إنشاء الوظائف التي أحتاجها. يبدو أنه فعل كل شيء وفقًا للتوثيق ، ولكن لم يتم تقرير ذلك. ثم أدركت أنني لم أفهم على الإطلاق كيف يعمل الإطار "تحت غطاء المحرك" ولماذا لا يعمل شيء يجب أن يعمل بالنسبة لي ؛ أين وما هي الأوامر التي ينبغي استدعاؤها وبأي طرق. بشكل عام ، قررت وضع الإطار جانباً ومحاولة فهم أعمق لكيفية عمل واجهة برمجة تطبيقات Telegram نفسها وكيف يمكنك التعامل معها مباشرة ، الأمر الذي سيمنحني مزيدًا من التحكم في الموقف ، كما يتيح لي أيضًا دراسة المطبخ الكامل للعمل مع واجهة برمجة التطبيقات (API) عن كثب. من الممكن أنني لن أعود إلى استخدام أطر Python Telegram Bot و pyTelegramBotAPI باعتبارها غير ضرورية.أو ربما أعود لتبسيط عملي على إنشاء دراجتي الخاصة باستخدام Telegram API. ولكن حتى لو عدت ، فسأفهم الكثير عن عمل هذه الأطر.

كان لدي دورة صغيرة لم تتم مراجعتها على Udemy فقط لإنشاء روبوت لـ Telegram. في وقت كتابة هذه السطور ، كانت هذه هي دورة Udemy الوحيدة التي يمكن فيها للشخص أن يحل مشكلة بدون Python Telegram Bot و pyTelegramBotAPI (لن أعطي رابطًا ، حتى لا يكون هذا إعلانًا). لحلها ، استخدم قارورة. هنا ، بالمناسبة ، بعد المرور عبر "مسار عسكري" معين ، أتيحت لي الفرصة لكتابة دراستي حول هذا الموضوع ، على الرغم من أنه كان من السابق لأوانه بالطبع - لن أجلب أي قيمة خاصة. ولكن إذا كنت ، المبرمج الأكثر خبرة الذي يقرأ هذه المقالة ، تعرف الكثير عن هذا ، فيمكنك إنشاء دورة تدريبية خاصة بك ويسعدني شرائها منك مقابل 10.99 دولارًا (سعر "خصم" نموذجي لـ Udemy) لتعلم شيء جديد.

بشكل عام ، من الدورة ، أدركت أن Flask سيسمح لي بجعل الحياة أسهل لمعالجة طلبات GET و POST.

نظرًا لأن هذه المقالة مخصصة خصيصًا للعمل مع قاعدة البيانات ، سأتحدث عن هذا. على الرغم من أنه من المغري طوال الوقت وصف التفاصيل الدقيقة الأخرى ، مثل: نقل إعدادات الاتصال إلى ملف "سري" ، وتلقي ومعالجة البيانات المستلمة من Telegram API ، وكيفية استقبال رسائل الويب من Telegram على جهاز كمبيوتر محلي بدون شهادة SSL. إذا كان لديك اهتمام ، فأخبرني بذلك وسأكتب مقالة منفصلة.

حوالي 3-4 أيام من وقتي في حزيران (يونيو) ، استغرق الأمر أن أفهم أنني بحاجة إلى الابتعاد عن الإطار قبل أن أتمكن من تلقي البيانات بثقة من Telegram ومعالجة النص (التحليل) واعتمادًا ما كتبه المستخدم ، أرسل له الأوامر الضرورية ، وكذلك الأزرار (بما في ذلك الكتابة فوق الرسالة وتغيير الأزرار).

كانت الخطوة التالية بالنسبة لي هي ربط قاعدة البيانات لتخزين المستخدمين الذين يعملون مع البوت الموجود فيها ، بالإضافة إلى بيانات حول تفاعلهم في البوت كجزء من المهمة التي واجهته.

أول شيء فعلته في Google كان مقالًا من Habré مع ترجمة للكتاب الضخم Miguel Grinberg ( أصل مدونته هنا ).

يتناول الفصل الرابع الاتصال بقاعدة بيانات باستخدام Flask و SQLAlchemy ORM. ثم فكرت: "واو ، كم هو رائع ، الآن تم حل المشكلة". من الغريب ، لكن بنية الملف التي اقترحها المؤلف لم تنجح معي.

فعلت عن طريق القياس مثل له:

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

في main.py ، أقوم بكل الأعمال الرئيسية باستخدام Telegram API.
في app \ models.py ، أقوم بإنشاء فئات لقاعدة البيانات.
في التطبيق \ __ init__.py ، تم كل شيء كما كتب ميغيل جرينبرغ.

ولكن لسبب ما ، في main.py ، لم يريدوا أن يسحبوني من التطبيق استيراد db ، التطبيق. أمضى حوالي ساعة في البحث عن المشاكل والحلول على الإنترنت. ونتيجة لذلك ، صادف أوليغ مولتشانوف قناة على YouTube ومقطع الفيديو الخاص به "إنشاء مدونة عن قارورة (دروس) - إنشاء مشاركات (نماذج) و SQLAlchemy". هناك شاهدت كيف يقوم بالاتصال بقاعدة البيانات وحاولت الذهاب بهذه الطريقة (دون وضع ملف models.py في دليل التطبيق ، بدون إنشاء __init__.py.

بشكل عام ، الآن أصبح هيكل مشروعي بسيطًا للغاية (وقبيحًا قليلاً ، الأمر الذي يربكني ، ربما في المستقبل سأفهم كيفية تحسين الهيكل):

صورة

كما ترى ، لدي app.py، models.py، main.py، app.sqlite (باقي الملفات لا تتعلق بالموضوع الحالي).

في app.py لدي هذا الرمز:

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)

في 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)

في 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

كما فهمت بالفعل ، من الإعدادات قمت بإزالة القيم المباشرة للاتصال وعرضتها بتنسيق .env بحيث لا يتألقون في المنتج أو في SVN.

أولاً ، تمت كتابة app.db (حيث أصبح app.sqlite الآن) في SQLALCHEMY_DATABASE_URI كقاعدة بيانات. نعم ، نعم ، وكتبت ، كما هو موضح في التعليمات ميغيل جرينبرغ. أتمنى أن تفهم أنك بحاجة إلى استبداله بـ .slite. قبل ذلك ، فكرت في ساعة بعد محاولات مؤلمة لتشغيل التعليمات البرمجية.

يمكنك إنشاء قواعد البيانات والجداول في sqlite بالانتقال إلى وحدة تحكم التصحيح (على سبيل المثال ، في PyCharm لدي أدوات -> Python أو Debug Console:

صورة

هنا ، نقوم أولاً بتوصيل الطريقة التي نحتاجها في الملف المطلوب (لدي من نماذج استيراد db) ، وبعد الاتصال الناجح ، حدد الأمر db.create_all (). بعد ذلك ، سيتم إنشاء قاعدة بيانات تحتوي على جميع الجداول الضرورية. تجدر الإشارة إلى أنه إذا فعلت ذلك من استيراد التطبيق db وقمت بتشغيل الأمر db.create_all () ، فسيبدو أنه قد تم إنشاء ملف قاعدة البيانات ، ولكنه سيكون نوعًا من الهراء ، وليس قاعدة البيانات (لم أفهم السبب).

عندما قمت بحل هذه المشكلة ، فكرت مرة أخرى أنه لم يعد هناك أي صعوبات. يبقى فقط لكتابة دالة main.py ، والتي ستقوم ، في ظل أحداث معينة ، بكتابة البيانات من Telegram إلى قاعدة البيانات. حسنًا ، لقد اتصلت من النماذج التي تستورد المستخدمين وفي المكان المناسب تسمى الوظيفة:

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)

في الوظيفة الأخيرة ، أولاً بدلاً من user = db.session.query (Users) .filter (Users.username == f '{username}'). وقفت First_or_404 () Users.query.filter_by (username = '{username}'). first_or_404 (). هنا ، قضيت أيضًا حوالي نصف ساعة لفهم أن هذا الاستعلام في قاعدة البيانات لا يعمل ولا يتلقى أي بيانات ، لذلك لم يتم إرسال الاستعلام إلى قاعدة البيانات. طالب جوجل بمقال صغير واحد حيث قال الناس أنه من الأفضل استخدام db.session.query (المستخدمون). لماذا ، I xs ، لم يضيع الوقت في التحليل. بعد ذلك ، بدأت كتابة البيانات في قاعدة البيانات.

بهذا نختتم هذه المقالة لأنني وصفت ما أردت وصفه وحل مشكلة توصيل قاعدة البيانات بقارورة.

تمت كتابة هذه المقالة فقط لسبب أننا "يونيو" نحب عند قراءة التعليمات. أنا نفسي حاولت أيضًا العثور على تعليمات لربط قاعدة البيانات بـ Flask. لم أجد أي تعليمات كاملة ، لذلك كان علي أن أشق طريقي عبر العقبات. حسنًا ، قررت أن أصف تجربتي على أنها التجربة التالية التي ستبحث عن تعليمات جاهزة.

PS الذي يبحث عن حل جاهز ، ثم أطلب من المستودع على GitHub

PSS سأكون سعيدًا إذا قام شخص ما بمراجعة الرمز وتقديم توصياته للتحسين.

شكرا للانتباه.

All Articles