Psycopg2 & Flask - привязка соединения к before_request & teardown_appcontext - PullRequest
0 голосов
/ 11 февраля 2019

Приветствую, ребята, при рефакторинге моего приложения Flask я застрял при привязке соединения db к @app.before_request и закрытии его на @app.teardown_appcontext.Я использую обычный Psycopg2 и шаблон фабрики приложений.

Сначала я создал функцию для вызова вместе с фабрикой приложений, чтобы я мог использовать @app в качестве , предложенного Мигелем Гринбергом здесь :

def create_app(test_config=None):
    app = Flask(__name__, instance_relative_config=True)

    --

    from shop.db import connect_and_close_db
    connect_and_close_db(app)

    --

    return app

Затем я попробовал этот шаблон, предложенный на http://flask.pocoo.org/docs/1.0/appcontext/#storing-data:

def connect_and_close_db(app):

    @app.before_request
    def get_db_test():
        conn_string = "dbname=testdb user=testuser password=test host=localhost"
        if 'db' not in g:
            g.db = psycopg2.connect(conn_string)
        return g.db

    @app.teardown_appcontext
    def close_connection(exception):
        db = g.pop('db', None)

        if db is not None:
            db.close()

Это привело к:

TypeError: 'psycopg2.extensions.connection' object is not callable

У любого есть представление, что произошло и как заставить его работать?

Кроме того, мне интересно, как получить доступ к объекту соединения для создания курсора, когда его создание привязано к before_request?

1 Ответ

0 голосов
/ 12 февраля 2019

Это решение, вероятно, далеко от совершенства, и оно на самом деле не СУХОЕ.Я бы приветствовал комментарии или другие ответы, основанные на этом.

Чтобы реализовать поддержку psycopg2, вам, вероятно, нужно взглянуть на пул соединений .Существует также хорошее руководство о том, как реализовать это с Flask.

Основная идея - сначала создать пул соединений.Вы хотите, чтобы это было установлено при инициализации приложения фляги (это может быть в интерпретаторе python или через рабочий gunicorn, которых может быть несколько - в этом случае каждый рабочий имеет свой собственный пул соединений).Я решил сохранить возвращенный пул в конфигурации:

from flask import Flask, g, jsonify

import psycopg2
from psycopg2 import pool

app = Flask(__name__)

app.config['postgreSQL_pool'] = psycopg2.pool.SimpleConnectionPool(1, 20,
    user = "postgres",
    password = "very_secret",
    host = "127.0.0.1",
    port = "5432",
    database = "postgres")

Обратите внимание, что первые два аргумента SimpleConnectionPool - это соединения min & max.Это число подключений к вашему серверу базы данных, между 1 и 20 в данном случае.

Далее определите функцию get_db:

def get_db():
    if 'db' not in g:
        g.db = app.config['postgreSQL_pool'].getconn()
    return g.db

SimpleConnectionPool.getconn()используемый здесь метод просто возвращает соединение из пула, который мы присваиваем g.db и возвращаем.Это означает, что когда мы вызываем get_db() в любом месте кода, он возвращает то же соединение или создает соединение, если оно отсутствует.Нет необходимости в before.context декораторе.

Определите свою функцию разрыва:

@app.teardown_appcontext
def close_conn(e):
    db = g.pop('db', None)
    if db is not None:
        app.config['postgreSQL_pool'].putconn(db)

Она запускается, когда контекст приложения разрушается, и использует SimpleConnectionPool.putconn(), чтобы убрать соединение.

Наконец, определите маршрут:

@app.route('/')
def index():
    db = get_db()
    cursor = db.cursor()

    cursor.execute("select 1;")
    result = cursor.fetchall()
    print (result)

    cursor.close()
    return jsonify(result)

Этот код работает для меня, проверенный на запуск postgres в контейнере Docker.Несколько вещей, которые, вероятно, следует улучшить:

  • Это представление не очень СУХОЕ.Возможно, вы могли бы переместить часть этого в функцию get_db, чтобы она возвращала курсор.(!!!)

  • Когда выйдет интерпретатор Python, вы также должны найти способ закрыть соединение с app.config['postgreSQL_pool'].closeall

  • Хотя провереноБыл бы неплох какой-то способ мониторинга пула, чтобы вы могли наблюдать за соединениями пул / дБ под нагрузкой и убедиться, что пул ведет себя как положено.

(!!!) В другой стране документация sqlalchemy.scoped_session объясняет больше вещей, связанных с этим, с некоторой теорией о том, как ее «сеансы» работают в отношении запросов.Они реализовали его таким образом, что вы можете вызвать Session.query('SELECT 1'), и он создаст сеанс, если он еще не существует.


РЕДАКТИРОВАТЬ : Вот gist с шаблоном фабрики вашего приложения и примером использования в комментарии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...