如何在Flask中执行一段代码块仅一次?

5

我想要记录使用FLASK的应用程序中登录的用户。我尝试使用@app.before_request,但问题是我需要访问通过flask globals传递的用户名,如果使用此装饰器,则获取到的值为none。

此外,像下面这样使用全局变量也行不通。如何在请求上下文中获取全局变量?

import logging
import time
from flask import request, flash
from flask import g
from forms import QueryForm, RequestForm, Approve_Reject_Btn_Form
from query.sqlQuery import SQLQuery
from database.oracle import Database
import datetime
from requests import Session

logger = logging.getLogger(__name__)
login_count = 0
'''
Homepage route - Displays all the tables in the homepage
'''
@app.route('/')
@app.route('/index')
def index():
  try:
    if not g.identity.is_authenticated():
      return render_template('homepage.html')
    else:
      try:
        global login_count
        if login_count == 0:
          username = g.identity.name
          user_ip = request.headers.get('IP_header')
          current_time = time.strftime('%c')
          db = Database(db_config.username, db_config.password)
          query = "INSERT INTO UserIP (username, login_time, ip_address) values ('%s', systimestamp, '%s')" % (username, user_ip)
          dml_query(query)
          logger.debug('User : %s, ip : %s, noted at time : %s, login_count : %s', username , user_ip, current_time, login_count)
          login_count = 1
1个回答

13

就你关于“如何在Flask中只执行一次代码块”的问题而言,类似下面的代码应该能够很好地工作:

app = Flask(__name__)

@app.before_first_request
def do_something_only_once():
    app.logger.setLevel(logging.INFO)
    app.logger.info("Initialized Flask logger handler")

你的问题的第二部分是如何设置全局变量,例如你的示例中的登录计数器。对于这种情况,我建议您使用外部缓存。请参见下面使用 werkzeug SimpleCache 类的示例。在生产环境中,你应该将 SimpleCache 替换为 redis 或 mongodb。

from werkzeug.contrib.cache import SimpleCache

class Cache(object):
    cache = SimpleCache(threshold = 1000, default_timeout = 3600)

    @classmethod
    def get(cls, key = None):
        return cls.cache.get(key)
    @classmethod
    def delete(cls, key = None):
        return cls.cache.delete(key)
    @classmethod
    def set(cls, key = None, value = None, timeout = 0):
        if timeout:
            return cls.cache.set(key, value, timeout = timeout)
        else:    
            return cls.cache.set(key, value)
    @classmethod
    def clear(cls):
        return cls.cache.clear()

您可以像这样使用 Cache 类。

from mycache import Cache

@app.route('/')
@app.route('/index')
def index():
    if not g.identity.is_authenticated():
        return render_template('homepage.html')
    else:
        login_count = Cache.get("login_count")
        if login_count == 0:
            # ...
            # Your code
            login_count += 1
            Cache.set("login_count", login_count)

编辑1:添加了after_request示例

根据Flask文档,after_request修饰器用于在每个请求之后运行已注册的函数。它可以用于使缓存无效、更改响应对象或几乎任何需要特定响应修改的操作。

@app.after_request
def after_request_callback(response):
    # Do something with the response (invalidate cache, alter response object, etc)
    return response

感谢您详细的回答。我还想知道您是否了解 after_request 装饰器的工作原理,并能否用一个简短的示例更新答案。此外,由于每个 Gunicorn 进程都会初始化自己的缓存并且会失去作用,所以我应该使用 memcache 而不是 simpleCache 吗? - ramu
https://dev59.com/9Yjca4cB1Zd3GeqPxn9H - ramu
是的,Memcache、Redis或MongoDB是生产系统的更好选择。我的示例只是为了让您了解如何创建登录计数器。 - Boris
那么使用memcache,我就不会遇到这个问题了吗? - ramu
需要注意的一点是:如果您正在使用像gunicorn这样的工作系统,每个工作进程在启动期间只会运行该函数一次。但是,在该模型下,由于Flask应用程序被反复重新实例化,因此仍将有许多函数执行,因此如果您尝试使用此方法进行静态变量设置并使用某些非确定性内容,请小心处理。 - bsplosion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接