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

Я читал об этом уже несколько дней, из документации Flask, старых сообщений о переполнении стека и google. Я нашел много тем, посвященных flask.g и app.context(). Я не понял, и я чувствую себя совершенно невежественным и перестал понимать, что происходит. поэтому мне просто нужен самый простой способ сделать локальную переменную внутри функции приложения фляги управляемой и доступной из другой функции фляги.

вкратце, у меня есть две функции в моем приложении для фляг: одна для обработки данных (длительная обработка), а другая для уничтожения обработки путем завершения объекта. функция убийства вызывается по URL-адресу "mywebsite/kill". Я попытался сделать объект глобальным, добавив «глобальный» к переменной x ( global x ), и это сработало нормально, за исключением того, что объект стал общим для всех запросов Flask и это нарушило функцию многопоточности Flask.

Поэтому мне нужно сделать x доступным для функции kill() и одновременно избегать использования обычного global.

Спасибо, что помогли мне с псевдокодом ниже, и не копируйте вставку в знаменитую документацию Hieroglyphs flask.

псевдопример:

app = Flask(__name__)

@app.route("/process")
def process():
    x= process_obj()    #Long Processing
    return render_template('result.html')

@app.route("/kill")
def kill():
    x.close()

person Mahmoud Bassyouni    schedule 19.11.2019    source источник


Ответы (1)


Прежде чем перейти к Flask и поделиться, давайте посмотрим, что вы хотите сделать с чисто программной стороны. Вы хотите использовать переменную в двух функциях.

Есть несколько способов сделать это. Один из них — использовать globals, который вы пробовали и обнаружили, что это не решение. Некоторые другие способы, которые приходят мне на ум:

  1. Инкапсуляция связанных функций с помощью класса
  2. Передача общей информации в качестве аргумента другой функции
  3. Поддержание регистратора для хранения и извлечения по желанию

1. Инкапсуляция с использованием классов

Поскольку Flask спроектирован так, чтобы быть простым и использует функции как первоклассные граждане для создания представлений, инкапсуляция с использованием классов не дала бы этой цели. Правильная альтернатива — использовать Flask Blueprints для группировки функций, которые будут иметь общие данные. Это означало бы использование глобальных переменных в модуле чертежа, и мы столкнулись бы с той же проблемой, что и с globals.

2. Передача значения в качестве аргумента

Это прямой способ поделиться ценностями в обычной ситуации. В этом случае, когда каждая функция привязана к URL-адресу в качестве представления, это может работать или не работать в зависимости от возможности отслеживания вашего объекта процесса x. Предположим, что у вас есть функция get_process(process_id), тогда вы могли бы сделать что-то вроде этого:

@app.route("/process")
def process():
    x = process_obj()
    return render_template('result.html', x=x)

В вашем шаблоне отобразите ссылку как:

<a href="{% url_for('kill', id=x.id) %}">Kill Process</a>

Затем получите значение в своем представлении kill следующим образом:

@app.route("/kill")
def kill():
    id = request.args.get("id", None)
    if not id:
        abort(400)
    x = get_process(id)
    x.close()

Примечание. Если этот процесс, о котором вы говорите в псевдокоде, управляется диспетчером задач, таким как Celery или Python-RQ, то вышеприведенное решение должно работать, поскольку они поставляются со способами получения заданий на основе их идентификатора. .

3. Ведение регистратора

Если вы не используете диспетчер задач и самостоятельно выполняете что-то вроде многопроцессорной обработки или какой-либо формы фонового процесса aysnc, в этой системе должен быть регистратор, который позволит вам отслеживать и извлекать процессы, которые вы запускаете.

Я бы посоветовал не делать этого и выбрать хорошо протестированную библиотеку, такую ​​как Celery, Python RQ, Huey и т. д., чтобы сделать это за вас.

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

Следующий псевдокод должен помочь вам:

# import your necessary ssh library objects if required
from flask import current_app, _app_ctx_stack


class MyObjectRegistrar(object):
    def __init__(self, app=None):
        self.app = app
        self.my_objects = {}
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        # load any config into your registrar from the app config if necessary

    def add(self, id, obj):
        self.my_objects[id] = obj

    def get(self, id):
        return self.my_objects.get(id, None)

    def remove(self, id):
        del self.my_objects[id]

С указанным выше регистратором, загруженным как расширение Flask:

from flask import Flask
from my_registrar import MyObjectRegistrar

app = Flask(__name__)
registrar = MyObjectRegistrar(app)

Это даст вам объект регистратора, который вы можете использовать в своих представлениях, например:

@app.route("/process")
def process():
    obj = process_obj()
    registrar.add("some-unique-id", obj)
    return render_template("results.html", id="some-unique-id")

@app.route("/kill")
def kill():
    id = request.args.get("id")
    x = registrar.get(id)
    x.close()
    registrar.remove(id)
    return render_template("killed.html")

Примечание. Это не обязательно должно быть расширение Flask. Если вы не видите смысла в наличии объекта Flask App, вы можете просто создать одноэлементный класс в обычном модуле Python.

person Arunmozhi    schedule 20.11.2019
comment
Спасибо Арунможи за ваш комментарий. Однако я не имел в виду ничего, связанного с процессами диспетчера задач. process_obj(), это функция, связанная с библиотекой соединений SSH, которую я использую для подключения и управления реальными сетевыми устройствами. Я просто использовал имя процесса, так как функция требует много времени для подключения, сбора и обработки данных. Есть ли какая-нибудь информация о 3-м варианте? это кажется ближайшим решением. - person Mahmoud Bassyouni; 20.11.2019
comment
@MahmoudBassyouni Я добавил обновление с псевдокодом, чтобы помочь вам начать работу. При необходимости посмотрите разработку расширения Flask flask.palletsprojects.com/en/1.1.x/ расширение. - person Arunmozhi; 21.11.2019