现在我们已经了解了如何将有用的数据聚合到数据库中,我们将希望与世界共享该数据。在今天的讲座中,我们将学习瓶 web框架的基础知识,我们将看到如何使用瓶快速组合一个服务器赢博体育程序来提供我们的数据。
下面的第一个示例程序展示了如何使用flask来设置一个最小的web服务器。
从flask import flask app = flask (__name__) @app.route('/') def index():返回“Hello, World!” app.run(debug=True)
这里发生了两件事。首先,我们正在构建一个flask赢博体育程序对象,并告诉它开始运行。其次,我们正在建立一条单一的路线。
flask赢博体育实现了一个非常简单的web服务器。由于web服务器通过响应包含URL的请求来提供内容,因此我们必须指定服务器应该响应哪个URL以及应该为每个这样的URL返回什么信息。路由是URL的一个片段:当它与服务器的地址结合在一起时,我们得到一个服务器可以响应的完整URL。在flask中,我们通过向函数中添加Python装饰来设置路由,使该函数成为路由处理函数。函数应该返回某种类型的内容。在第一个示例中,index函数是完整URL为http://127.0.0.1:5000/的路由的路由处理程序
运行此程序将启动flask web服务器。您可以通过在浏览器中输入URL http://127.0.0.1:5000/来测试赢博体育程序是否正常工作。
在今天的主要示例中,我们将构建一个flask web服务器来提供来自数据库的数据。具体来说,我们将提供我们在之前的讲座中建立的太阳能数据库中的数据。
下一个示例程序演示了如何在flask web赢博体育程序中建立数据库连接并从中提供数据。
从flask import flask, g, jsonify import sqlite3 import datetime import time def getDB(): db = getattr(g, 'database', None)如果db为None: g.g edatabase = sqlite3.connect('solar.db') db = g.g edatabase return db app = flask (__name__) @app。teardown_appcontext def closeConnection(exception): db = getattr(g, 'database', None)如果db不是None: db.close() @app.route('/') def index():返回“Hello, World!”@app.route('api/<day>') def getDay(day): start_date = datetime。datetime(2019,4, int(day), 0,0,0) start_time = int(time.mktime(start_date.timetuple())) end_time = start_time + 3 * 24 * 60 * 60 c = getDB().cursor() sql = “SELECT time, power, temp, cloud FROM power WHERE time>=? ”AND time <=?" c.execute(sql, (start_time, end_time)) result = [{"time":t,"power":p,"temp":f,"cloud":c} for t,p,f,c在c.fetchall()] return jsonify(result) app.run(debug=True)
为了准备使用数据库,我们设置了一个getDB()函数来打开到数据库的连接。任何需要与数据库交互的路由处理程序都将调用此函数以获得与数据库的连接。为了提高效率,getDB()只打开数据库连接一次。打开连接后,getDB()立即将对数据库连接的引用存储在一个特殊的flask全局赢博体育程序对象g中。在后续调用getDB()时,代码将简单地从g对象中获取该引用,并将该引用返回给任何需要它的人。
一个行为良好的赢博体育程序还应该关闭它打开的赢博体育数据库连接,因此我们还设置了第二个函数closeConnection(),该函数关闭数据库连接。该函数的装饰器将该函数设置为一个teardown函数,该函数将在flask服务器关闭时自动调用。
为了提供来自数据库的数据,我添加了第二个路由处理程序函数getDay()。由于这个函数被设计为提供数据,所以它的路由以api开始:这是一个广泛使用的设置数据访问url的约定。getDay()的路由还包含一个路由参数
getDay()函数的目的是提供一整天的电力和天气数据。getDay()中的数据库代码对数据库执行查询以获取必要的数据。由于要将该数据转换为JSON,因此需要将从查询返回的每一行的数据转换为适合转换为JSON的格式。上面的代码将从每一行获取数据,并将其转换为一个字典,这些数据将提供给我们一个元组。将数据转换为JSON的转换过程将把每个字典映射为JSON对象。到JSON的最终转换由flask jsonify()函数处理。
您可以通过运行程序并在浏览器中输入URL http://127.0.0.1:5000/api/8来测试这个最新版本的代码。
对于今天的最后一个示例,我将构建一个更完整的flask赢博体育程序。这个最终版本将演示flask的其他特性,比如提供文件、使用模板和处理表单请求。
大多数提供数据的赢博体育程序还应该提供一些关于如何使用该赢博体育程序的文档。如何处理这个问题的一个简单约定是让赢博体育程序提供一个HTML文档页面来响应基础URL http://127.0.0.1:5000/。这个程序的下一个版本将用一个更完整的文档页面取代以前版本中简单的“Hello, World!”响应。
许多提供数据的web赢博体育程序也实现了某种安全机制,以防止未经授权的用户访问赢博体育程序。这个版本的赢博体育程序还将实现一个API密钥机制来保护赢博体育程序。为了给用户提供一种获取API密钥的方法,我们还将在文档页面中实现一个简单的表单,用户可以使用它来请求API密钥。
下面是我们的flask赢博体育最终版本的完整源代码:
从flask import flask, g, request, render_template, jsonify import sqlite3 import datetime import time import secrets def getDB(): db = getattr(g, 'database', None)如果db是None: g.b ase = sqlite3.connect(' solaret .db') db = g.b ase return db def makeKey(name): db = getDB() key = secrets.token_urlsafe(8) c = db.cursor() sql = "INSERT INTO users(name,apikey) VALUES(?,?)" c.f execute(sql,(name,key)) db.commit() return key def checkKey(key):c = getDB().cursor() sql = "SELECT name FROM users WHERE apikey='"+key+"'" c.execute(sql) name = c.fetchone()如果name为None:返回False返回True app = 瓶(__name__, static_folder= ") @app. getDB()teardown_appcontext def closeConnection(exception): db = getattr(g, 'database', None)如果db不是None: db.close() @app. close()route('/', methods=['GET']) def index(): return app.send_static_file('index.html') @app. htmlroute('/getKey', methods=['POST']) def getKey(): name = request。form['name'] key= makeKey(name) return render_template('key.html', key=key) @app。route('/api/<key>/day/<day>', methods=['GET']) def getDay(key, day):如果不是checkKey(key):返回jsonify([]) start_date = datetime。datetime(2019,4, int(day), 0,0,0) start_time = int(time.mktime(start_date.timetuple())) end_time = start_time + 24 * 60 * 60 c = getDB().cursor() sql = “SELECT time, power, temp, cloud FROM power WHERE time>=? ”AND time <=?" c.execute(sql, (start_time, end_time)) result = [{"time":t,"power":p,"temp":f,"cloud":c} for t,p,f,c在c.fetchall()] return jsonify(result) app.run(debug=True)
基路由的路由处理程序index()现在被设置为返回整个HTML页面。为此,我们在项目文件夹中设置一个单独的文件index.html,并通过调用flask send_static_file()函数让路由处理程序返回整个文件。另外,当我们设置瓶对象时,我们传入一个额外的参数,告诉瓶项目的静态文件的位置。对于这个简单的示例,将单独的静态文件与其他项目文件放在同一个文件夹中就足够了。更复杂的站点将希望在项目文件夹中设置一个单独的静态文件夹,并将赢博体育赢博体育程序的静态内容放在那里。
下面是index.html文件的HTML代码:
<!DOCTYPE html> <html> <head> <title>Solar Server</title> </head> <body> <h4>Welcome</h4> <p>欢迎来到Solar数据服务器。特定日期的数据可通过URL</p> <pre> http://127.0.0.1:5000/api/<key>/day/<day>;</pre> <p>where <key>;是您的API密钥和<;day>;整数形式,取值范围为5 ~ 30。</p> <h4>请求API密钥</h4> <p>没有API密钥?您可以使用下面的表格来申请。</p> <form action="getKey" method="post"> <label for="name">输入您的姓名:</label> <input type="text" name="name" /> <input type="submit" /> </form> </body> </html>
这段代码的一个特殊特性是一个HTML表单,用户可以用它来注册API密钥。表单的action属性将从该表单向URL http://127.0.0.1:5000/getKey发送请求。下面是该URL的路由处理程序:
@app。route('/getKey', methods=['POST']) def getKey(): name = request。form['name'] key= makeKey(name) return render_template('key.html', key=key)
HTML表单请求将以键/值对的形式将数据发送回服务器。上面HTML页面中的表单使用了一个键为‘name’的参数。为了访问用户为这个参数输入的值,路由处理程序会访问flask请求对象的表单数据。该数据是一个字典,包含表单发送的赢博体育键/值对。获得用户输入的名称后,我们将其传递给一个特殊的makeKey()函数,该函数将名称转换为API密钥并将该数据存储在赢博体育程序的数据库中。路由处理程序必须做的最后一件事是返回一个HTML页面,向用户显示他们的新API密钥。为了解决这个问题,我们使用flask模板机制。为了使用这种机制,我们为响应设置了一个HTML页面,并在HTML中嵌入模板表达式,用于任何我们事先不知道的内容。然后,我们通过调用flask render_template()函数来提供该模板页面,并为页面中出现的任何模板表达式传递该函数的值。
下面是模板页面key.html的代码:
<!DOCTYPE html> <html> <head> <title>API Key</title> </head> <body> <p>您的API Key为<b>{{Key}}</b></p> </body> </html>
现在我们已经实现了一个API密钥系统,我们可以修改数据服务URL的路由处理程序来查找和检查API密钥:
@app。route('/api/<key>/day/<day>', methods=['GET']) def getDay(key, day):如果不是checkKey(key):返回jsonify([]) start_date = datetime。datetime(2019,4, int(day), 0,0,0) start_time = int(time.mktime(start_date.timetuple())) end_time = start_time + 24 * 60 * 60 c = getDB().cursor() sql = “SELECT time, power, temp, cloud FROM power WHERE time>=? ”AND time <=?" c.execute(sql, (start_time, end_time)) result = [{"time":t,"power":p,"temp":f,"cloud":c} for c.fetchall()] return jsonify(result)
这段代码首先确认URL包含有效的API密钥。如果没有,则该函数只返回一些空数据。
现在我们的服务器赢博体育程序已经启动并运行,我们可以编写一个客户端程序来从服务器获取数据并对其进行处理。
下面是一个客户端程序的代码,它复制了上一讲中的绘图赢博体育程序。这一次,赢博体育程序从服务器请求数据,而不是直接从数据库读取数据:
导入请求导入时间导入日期导入matplotlibpyplot as plt start = int(input(“Enter起始日期:”))start_date = datetime.datetime(2019,4,start,0,0,0) start_time = int(time.mktime(start_date.timetuple())) results = [] for d in range(start,start+3): response = requests.get('http://127.0.0.1:5000/api/KBBX4xvQ5xI/day/'+str(d)) results = results + response.json() #处理结果t = [] p = [] tp = [] cl =[]对于结果中的条目:t.append((条目(“时间”)-start_time) / (60 * 60)) p.append(条目(“权力”)/ 33.7)tp.append(条目(临时的))cl.append(条目(“云”)* 100)plt.plot (t, p, r -, t, tp, b -, t, cl, y -) plt。title(‘起始日期’+str(start)) plt.savefig('plot.png')
这是一个档案,包含了今天课程的赢博体育资料。