Flask: настройка приложения и атрибутов, специфичных для запроса?

Я пишу приложение, которое подключается к базе данных. Я хочу создать это соединение db один раз, а затем повторно использовать это соединение на протяжении всего срока службы приложения.

Я также хочу аутентифицировать пользователей. Пользователь auth будет жить только для жизни запроса.

Как я могу различать объекты, хранящиеся для жизни флеш-приложения, в отличие от конкретного запроса? Где я могу их хранить, чтобы все модули (и последующие чертежи) имели к ним доступ?

Вот пример приложения:

from flask import Flask, g

app = Flask(__name__)

@app.before_first_request
def setup_database(*args, **kwargs):
    print 'before first request', g.__dict__
    g.database = 'DATABASE'
    print 'after first request', g.__dict__

@app.route('/')
def index():
    print 'request start', g.__dict__
    g.current_user = 'USER'
    print 'request end', g.__dict__

    return 'hello'

if __name__ == '__main__':
    app.run(debug=True, port=6001)

Когда я запустил это (Flask 0.10.1) и переместился к http://localhost:6001/, вот что отображается в консоли:

$ python app.py 
 * Running on http://127.0.0.1:6001/
 * Restarting with reloader

before first request {}
after first request {'database': 'DATABASE'}
request start {'database': 'DATABASE'}
request end {'current_user': 'USER', 'database': 'DATABASE'}
127.0.0.1 - - [30/Sep/2013 11:36:40] "GET / HTTP/1.1" 200 -

request start {}
request end {'current_user': 'USER'}
127.0.0.1 - - [30/Sep/2013 11:36:41] "GET / HTTP/1.1" 200 -

Таким образом, первый запрос работает как ожидалось: flask.g хранит мою базу данных, и когда запрос начинается, он также имеет мою информацию о пользователе.

Однако, по моему второму запросу, flask.g очищается! Моя база данных нигде не найдена.

Теперь я знаю, что flask.g используется для применения только к запросу. Но теперь, когда он привязан к приложению (начиная с 0.10), я хочу знать, как связывать переменные со всем приложением, а не только с одним запросом.

Что мне не хватает?

edit: Я специально интересуюсь MongoDB - и в моем случае поддерживаю соединения с несколькими базами данных Mongo. Могу ли я просто создать эти соединения в __init__.py и повторно использовать эти объекты?

Ответы

Ответ 1

flask.g будет хранить вещи только на время запроса. В документации упоминалось, что значения хранятся в контексте приложения, а не в запросе, но это скорее проблема реализации: она не изменяет тот факт, что объекты в flask.g доступны только в одном потоке, а во время время жизни одного запроса.

Например, в разделе официального раздела по подключению к базе данных соединение выполняется один раз в начале запроса, а затем завершается в конце запроса.

Конечно, если вы действительно этого хотели, вы могли бы создать соединение с базой данных один раз, сохранить его в __init__.py и ссылаться на него (как глобальную переменную) по мере необходимости. Однако вы не должны этого делать: соединение может закрыть или тайм-аут, и вы не сможете использовать соединение в нескольких потоках.

Поскольку вы не указали, КАК вы будете использовать Mongo в Python, я предполагаю, что вы будете использовать PyMongo, поскольку это обрабатывает все пул соединений для вас.

В этом случае вы бы сделали что-то вроде этого...

from flask import Flask
from pymongo import MongoClient
# This line of code does NOT create a connection
client = MongoClient()

app = Flask()

# This can be in __init__.py, or some other file that has imported the "client" attribute
@app.route('/'):
def index():
    posts = client.database.posts.find()

Вы могли бы, если хотите, сделать что-то вроде этого...

from flask import Flask, g
from pymongo import MongoClient
# This line of code does NOT create a connection
client = MongoClient()

app = Flask()

@app.before_request
def before_request():
    g.db = client.database

@app.route('/'):
def index():
    posts = g.db.posts.find()

Это не совсем так, но может быть полезно для логики, которую вы хотите выполнять по каждому запросу (например, установка g.db в определенную базу данных в зависимости от пользователя, который вошел в систему).

Наконец, вы можете понять, что большая часть работы по настройке PyMongo с Flask, вероятно, сделана для вас в Flask-PyMongo.

Ваш другой вопрос касается того, как вы отслеживаете материал, характерный для пользователя, который зарегистрировался. Ну, в этом случае вам нужно хранить некоторые данные, которые связаны с соединением. flask.g очищается в конце reququest, так что ничего хорошего.

То, что вы хотите использовать, sessions. Это место, где вы можете хранить значения, которые (с реализацией по умолчанию) хранятся в файле cookie в браузере пользователя. Поскольку cookie будет передаваться вместе с каждым запросом, который пользовательский браузер делает на вашем веб-сайте, у вас будут доступны данные, которые вы ввели в сеанс.

Имейте в виду, что сеанс НЕ хранится на сервере. Он превращается в строку, которая передается пользователю обратно. Поэтому вы не можете хранить на нем такие вещи, как соединения с DB. Вместо этого вы будете хранить идентификаторы (например, идентификаторы пользователей).

Убедившись, что аутентификация пользователя работает, ОЧЕНЬ трудно получить право. Проблемы безопасности, которые необходимо соблюдать, являются удивительно сложными. Я бы настоятельно рекомендовал использовать что-то вроде Flask-Login, чтобы обработать это для вас. Вы по-прежнему можете использовать session для хранения других элементов по мере необходимости, или вы можете позволить дескриптору Flask-Login определять идентификатор пользователя и сохранять значения, которые вам нужны в базе данных, и извлекать их из базы данных в каждом запросе.

Итак, в целом, есть несколько разных способов сделать то, что вы хотите сделать. У каждого есть свои обычаи.

  • Глобалы хороши для элементов, которые являются потокобезопасными (например, PyMongo MongoClient).
  • flask.g может использоваться для хранения данных в течение срока службы запроса. При использовании фляжных приложений на основе SQLAlchemy, общая задача - обеспечить, чтобы все изменения происходили сразу, в конце запроса, используя метод after_request. Использование flask.g для чего-то подобного очень полезно.
  • Флажок session может использоваться для хранения простых данных (строк и номеров, а не объектов соединения), которые могут использоваться для последующих запросов, поступающих от одного и того же пользователя. Это полностью зависит от использования файлов cookie, поэтому в любой момент пользователь может удалить файл cookie, и все в "сеансе" будет потеряно. Поэтому вы, вероятно, захотите хранить большую часть своих данных в базах данных, а сеанс используется для идентификации данных, относящихся к пользователю в сеансе.

Ответ 2

"связанный с приложением" не означает, что вы думаете, что это значит. Это означает, что g привязан к текущему запросу. Quoth the docs:

Флажок предоставляет вам специальный объект, который гарантирует, что он действителен только для активного запроса и который будет возвращать разные значения для каждого запроса.

Следует отметить, что Учебники Flask специально не сохраняют объекты базы данных, но это не нормативный для любого применения значительного размера. Если вы действительно заинтересованы в погружении в кроличью скважину, я предлагаю инструмент объединения пулов базы данных. (например, этот, упомянутый в ответе SO, см. выше)

Ответ 3

Я предлагаю вам использовать сеанс для управления пользовательской информацией. Сеансы помогают вам хранить информацию. Несколько запросов и флэков уже предоставляют вам рамки сеанса.

from flask import session
session['usename'] = 'xyz'

Посмотрите на расширение Flask-Login. Он хорошо разработан для обработки пользовательских аутентификаций.

Для базы данных я предлагаю просмотреть расширение Flask-SQLAlchemy. Это заботит инициализацию, объединение, разрывы и т.д. Для вас из коробки. Все, что вам нужно сделать, это определить URI базы данных в конфигурации и связать ее с приложением.

from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)