IoT之Web--Flask

Posted 全民全栈IoT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IoT之Web--Flask相关的知识,希望对你有一定的参考价值。


01

Flask概述


什么是Web Framework?

Web Application Framework(Web应用程序框架)或简单的Web Framework(Web框架)表示一个库和模块的集合,使Web应用程序开发人员能够编写应用程序,而不必担心协议,线程管理等低级细节。

什么是Flask?

Flask 是一个微型的 Python 开发的 Web 框架,基于Werkzeug WSGI工具箱和Jinja2 模板引擎。Flask使用BSD授权。Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能。Flask没有默认使用的数据库、窗体验证工具。然而,Flask保留了扩增的弹性,可以用Flask-extension加入这些功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术。

WSGI

Web Server Gateway Interface(Web服务器网关接口,WSGI)已被用作Python Web应用程序开发的标准。WSGI是Web服务器和Web应用程序之间通用接口的规范。

Werkzeug

它是一个WSGI工具包,它实现了请求,响应对象和实用函数。 这使得能够在其上构建web框架。Flask框架使用Werkzeug作为其基础之一。

jinja2

jinja2是Python的一个流行的模板引擎。Web模板系统将模板与特定数据源组合以呈现动态网页。

Flask通常被称为微框架。 它旨在保持应用程序的核心简单且可扩展。Flask没有用于数据库处理的内置抽象层,也没有形成验证支持。相反,Flask支持扩展以向应用程序添加此类功能。一些受欢迎的Flask扩展将在本教程后续章节进行讨论。

“微”并不代表整个应用只能塞在一个 Python 文件内, 当然塞在单一文件内也没有问题。“微”也不代表 Flask 功能不强。微框架中的“微”字表示 Flask 的目标是保持核心简单而又可扩展。Flask 不会替你做出许多决定,比如选用何种数据库。类似的决定,如使用何种模板引擎,是非常容易改变的。Flask 可以变成你任何想要的东西,一切恰到好处,由你做主。

缺省情况下, Flask 不包含数据库抽象层、表单验证或者其他已有的库可以处理的东西。然而, Flask 通过扩展为你的应用添加这些功能,就如同这些功能是 Flask 生的一样。大量的扩展用以支持数据库整合、表单验证、上传处理和各种开放验证等等。Flask 可能是 “微小”的,但它已经为满足您的各种生产需要做出了充足的准备。


Hello world!


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'



02


什么是MVC


 MVC是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制)。

    1、视图

         视图(View)代表用户交互界面,对于Web应用来说,可以概括为html界面,但有可能为XHTML、XML和Applet。随着应用的复杂性和规模性,界面的处理也变得具有挑战性。一个应用可能有很多不同的视图,MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的请求,而不包括在视图上的业务流程的处理。业务流程的处理交予模型(Model)处理。比如一个订单的视图只接受来自模型的数据并显示给用户,以及将用户界面的输入数据和请求传递给控制和模型。

    2、 模型

         模型(Model):就是业务流程/状态的处理以及业务规则的制定。业务流程的处理过程对其它层来说是黑箱操作,模型接受视图请求的数据,并返回最终的处理结果。业务模型的设计可以说是MVC最主要的核心。目前流行的EJB模型就是一个典型的应用例子,它从应用技术实现的角度对模型做了进一步的划分,以便充分利用现有的组件,但它不能作为应用设计模型的框架。它仅仅告诉你按这种模型设计就可以利用某些技术组件,从而减少了技术上的困难。对一个开发者来说,就可以专注于业务模型的设计。MVC设计模式告诉我们,把应用的模型按一定的规则抽取出来,抽取的层次很重要,这也是判断开发人员是否优秀的设计依据。抽象与具体不能隔得太远,也不能太近。MVC并没有提供模型的设计方法,而只告诉你应该组织管理这些模型,以便于模型的重构和提高重用性。我们可以用对象编程来做比喻,MVC定义了一个顶级类,告诉它的子类你只能做这些,但没法限制你能做这些。这点对编程的开发人员非常重要。

       业务模型还有一个很重要的模型那就是数据模型。数据模型主要指实体对象的数据 保存(持续化)。比如将一张订单保存到数据库,从数据库获取订单。我们可以将这个模型单独列出,所有有关数据库的操作只限制在该模型中。

3、控制

        控制(Controller)可以理解为从用户接收请求, 将模型与视图匹配在一起,共同完成用户的请求。划分控制层的作用也很明显,它清楚地告诉你,它就是一个分发器,选择什么样的模型,选择什么样的视图,可以完成什么样的用户请求。控制层并不做任何的数据处理。例如,用户点击一个连接,控制层接受请求后, 并不处理业务信息,它只把用户的信息传递给模型,告诉模型做什么,选择符合要求的视图返回给用户。因此,一个模型可能对应多个视图,一个视图可能对应多个模型。

        模型、视图与控制器的分离,使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器改变了模型的数据,所有其它依赖于这些数据的视图都应反映到这些变化。因此,无论何时发生了何种数据变化,控制器都会将变化通知所有的视图,导致显示的更新。这实际上是一种模型的变化-传播机制。模型、视图、控制器三者之间的关系和各自的主要功能

   1)最上面的一层,是直接面向最终用户的"视图层"(View)。它是提供给用户的操作界面,是程序的外壳。

   2)最底下的一层,是核心的"数据层"(Model),也就是程序需要操作的数据或信息。

   3)中间的一层,就是"控制层"(Controller),它负责根据用户从"视图层"输入的指令,选取"数据层"中的数据,然后对其进行相应的操作,产生最终结果。

这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口(Interface),供上面一层调用。这样一来,软件就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。



03


Flask快速上手

一、flask中操作数据库

1、数据库配置

flask内置了对各种数据库的连接以及操作接口,但是在app初始化的时候配置好数据库


SQLALCHEMY_BINDS = {
    'users''sqlite:///' + os.path.join(sqlite_basedir, 'data.db')
}
app = Flask(__name__, static_url_path='')
app.config['SQLALCHEMY_BINDS'] = SQLALCHEMY_BINDS
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://' + db_user + ':' + db_passwd + '@' + db_basedir
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True


2、数据库表单对象定义


class ProtocolList(db.Model):
"""编解码插件ID"""
__tablename__ = 't_protocol'
n_id = db.Column(db.Integer, primary_key=True)
c_name = db.Column(db.String(50), nullable=False)
n_create_time = db.Column(db.String(50), nullable=False)
c_plugin_name = db.Column(db.String(100), nullable=False)
c_protocol_type = db.Column(db.String(100), nullable=False)

def __init__(self, n_id, c_name, n_create_time, c_plugin_name, c_protocol_type):
self.n_id = n_id
self.c_name = c_name
self.n_create_time = n_create_time
self.c_plugin_name = c_plugin_name
self.c_protocol_type = c_protocol_type


3、数据库增删改查基本操作

增:

protocol = ProtocolList(n_id=n_id, c_name=c_name, \
n_create_time=n_create_time,\
c_protocol_type=c_protocol_type,
c_plugin_name=c_plugin_name)
db.session.add(protocol)
db.session.commit()


删:

ProtocolList.query.filter(ProtocolList.n_id == id).delete()
db.session.commit()


改:

device =ProtocolList.query.filter(DeviceList.n_id == id).all()[0]
device.c_serial_num = request.form['SerialNumber']
device.n_protocol_id = request.form.get('ProtocalId'1)
device.c_name = request.form.get('Name')
device.c_sensor_type = request.form.get('SensorType')
device.n_create_time = time.mktime(datetime.now().timetuple())
device.sensor_ip = request.form.get('SensorIp')
device.sensor_port = request.form.get('SensorPort')
db.session.commit()


查:

ProtocolList.query.filter(ProtocolList.n_id == n_id).all()


二、蓝图和视图

视图是一个应用对请求进行响应的函数。Flask 通过模型把进来的请求 URL 匹配到 对应的处理视图。视图返回数据, Flask 把数据变成出去的响应。Flask 也可以反 过来,根据视图的名称和参数生成 URL 。

创建蓝图

Blueprint 是一种组织一组相关视图及其他代码的方式。与把视图及其他 代码直接注册到应用的方式不同,蓝图方式是把它们注册到蓝图,然后在工厂函数中 把蓝图注册到应用。

示例 有三个蓝图,一个用于设备管理功能,一个用于插件管理功能,一个用于登录管理功能。每个蓝图的代码 都在一个单独的模块中。


device/_init_.py

from flask import Blueprint
device = Blueprint('device',__name__)


plugin/_init_.py

from flask import Blueprint
plugin = Blueprint('plugin',__name__)


sign/_init_.py

from flask import Blueprint
sign = Blueprint('sign',__name__)


app_init.py

from device import device
from sign import sign
from plugin import plugin
app.register_blueprint(device, url_prefix='/device')
app.register_blueprint(plugin, url_prefix='/plugin')
app.register_blueprint(sign, url_prefix='/sign')


三、模板

应用已经写好验证视图,但是如果现在运行服务器的话,无论访问哪个 URL ,都会 看到一个 TemplateNotFound 错误。这是因为视图调用了 render_template() ,但是模板还没有写。模板文件会储存在 flaskr 包内的 templates 文件夹内。

模板是包含静态数据和动态数据占位符的文件。模板使用指定的数据生成最终的文档。Flask 使用 Jinja 模板库来渲染模板。

在教程的应用中会使用模板来渲染显示在用户浏览器中的 HTML 。在 Flask 中, Jinja 被配置为 自动转义 HTML 模板中的任何数据。即渲染用户的输入是安全的。任何用户输入的可能出现歧意的字符,如 < 和 > ,会被 转义 ,替换为 安全 的值。这些值在浏览器中看起来一样,但是没有副作用。

Jinja 看上去并且运行地很像 Python 。Jinja 语句与模板中的静态数据通过特定的 分界符分隔。任何位于 {{ 和 }} 这间的东西是一个会输出到最终文档的静态式。 {% 和 %} 之间的东西表示流程控制语句,如 if 和 for 。与 Python 不同,代码块使用分界符分隔,而不是使用缩进分隔。因为代码块内的 静态文本可以会改变缩进。

应用中的每一个页面主体不同,但是基本布局是相同的。每个模板会 扩展 同一个 基础模板并重载相应的小节,而不是重写整个 HTML 结构。

项目中的base.html比较复杂,所以下面的实例用一个比较简单模板作为演示


flaskr/templates/base.html

<!doctype html>
<title>{% block title %}{% endblock %} - Flaskr</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<nav>
  <h1>Flaskr</h1>
  <ul>
    {% if g.user %}
      <li><span>{{ g.user['username'] }}</span>
      <li><a href="{{ url_for('auth.logout') }}">Log Out</a>
    {% else %}
      <li><a href="{{ url_for('auth.register') }}">Register</a>
      <li><a href="{{ url_for('auth.login') }}">Log In</a>
    {% endif %}
  </ul>
</nav>
<section class="content">
  <header>
    {% block header %}{% endblock %}
  </header>
  {% for message in get_flashed_messages() %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
  {% block content %}{% endblock %}
</section>



模板中定义三个块,这些块会被其他模板重载。

1、{% block title %} 会改变显示在浏览器标签和窗口中的标题。

2、{% block header %} 类似于 title ,但是会改变页面的标题。

3、{% block content %} 是每个页面的具体内容,如登录表单或者博客帖子。

其他模板直接放在 templates 文件夹内。为了更好地管理文件,属于某个蓝图 的模板会被放在与蓝图同名的文件夹内。


注册

flaskr/templates/auth/register.html

{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="username">Username</label>
    <input name="username" id="username" required>
    <label for="password">Password</label>
    <input type="password" name="password" id="password" required>
    <input type="submit" value="Register">
  </form>
{% endblock %}


{% extends 'base.html' %} 告诉 Jinja 这个模板基于基础模板,并且需要替换 相应的块。所有替换的内容必须位于 {% block %} 标签之内。

一个实用的模式是把 {% block title %} 放在 {% block header %} 内部。这里不但可以设置 title 块,还可以把其值作为 header 块的内容, 一举两得。

input 标记使用了 required 属性。这是告诉浏览器这些字段是必填的。如果用户使用不支持这个属性的旧版浏览器或者不是浏览器的东西创建的请求, 那么你还是要在视图中验证输入数据。总是在服务端中完全验证数据,即使客户端 已经做了一些验证,这一点非常重要。


登录

本模板除了标题和提交按钮外与注册模板相同。


flaskr/templates/auth/login.html

{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Log In{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="username">Username</label>
    <input name="username" id="username" required>
    <label for="password">Password</label>
    <input type="password" name="password" id="password" required>
    <input type="submit" value="Log In">
  </form>
{% endblock %}


、使用 WTForms 进行表单验证

当你必须处理浏览器提交的表单数据时,视图代码很快会变得难以阅读。有一些库可 以简化这个工作, WTForms 便是其中之一 ,下面我们将介绍这个库。如果你必 须处理许多表单,那么应当尝试使用这个库。

如果要使用 WTForms ,那么首先要把表单定义为类。我推荐把应用分割为多个模块( 大型应用 ),并为表单添加一个独立的模块。

使用一个扩展获得大部分 WTForms 的功能

Flask-WTF 扩展可以实现本方案的所有功能,并且还提供一些辅助小工具。使用这个扩展可以更好的使用表单和 Flask 。你可以从 PyPI 获得这个扩展。

表单

下面是一个典型的注册页面的示例:

from wtforms import Form, BooleanField, StringField, PasswordField, validators

class DeviceForm(FlaskForm):
    c_serial_num = wtforms.StringField('SerialNumber',validators=[DataRequired(message="序列号不能为空"),Length(min=6, max=20,message='长度不对')])
    c_name = wtforms.StringField('Name')
    n_create_time = time.mktime(datetime.now().timetuple())
    n_protocol_id = wtforms.SelectField('ProtocalId',validate_choice=False)
    c_sensor_type = wtforms.StringField('SensorType')
    sensor_ip = wtforms.StringField('SensorIp',validators=[IPAddress(message="ip地址格式不对xxx.xxx.xxx.xxx")])
    sensor_port = wtforms.StringField('SensorPort')
{% endblock %}


在视图函数中,表单用法示例如下:

 
class DeviceEditView(MethodView):

    def get(self,id=None):
        if session.get('admin'Noneis None:
            return redirect(url_for('sign.login'))
        else:
            categories = ProtocolList.query.all()
            item = DeviceList.query.filter(DeviceList.n_id == id).all()
            return render_template('edit.html', categories=categories, item=item[0])

    def post(self,id):
        device = DeviceList.query.filter(DeviceList.n_id == id).all()[0]
        device.c_serial_num = request.form['SerialNumber']
        device.n_protocol_id = request.form.get('ProtocalId'1)
        device.c_name = request.form.get('Name')
        device.c_sensor_type = request.form.get('SensorType')
        device.n_create_time = time.mktime(datetime.now().timetuple())
        device.sensor_ip = request.form.get('SensorIp')
        device.sensor_port = request.form.get('SensorPort')
        db.session.commit()

        return redirect(url_for('device.device_list'))



请记住以下几点:

1、如果数据是通过 HTTP POST 方法提交的,请根据 form 的值创建表单。如果是通过 GET 方法提交的, 则相应的是 args 。

2、调用 validate() 函数来验证数据。如果验证通过,则 函数返回 True ,否则返回 False 。

3、通过 form.<NAME>.data 可以访问表单中单个值。

模板中的表单

现在我们来看看模板。把表单传递给模板后就可以轻松渲染它们了。看一看下面的示例 模板就可以知道有多轻松了。WTForms 替我们完成了一半表单生成工作。


{% block main_content %}
    <div class="row">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h4>新增设备</h4>
            </div>
            <div class="panel-body">
                <form action="{{url_for("device.device_add")}}" enctype="multipart/form-data" method="post" class="form-horizontal">
                    {{ form.csrf_token }}
                    <div class="form-group">
                    {{ form.csrf_token }}
                        {{ form.c_serial_num.label(class="control-label col-md-2") }}
                        <div class="col-md-2">
                            {{ form.c_serial_num(class="form-control") }}
                        </div>
                        {% if form.c_serial_num.errors %}
                            {% for msg in form.c_serial_num.errors %}
                                <p class="help-block" style="color:red;font-size: 10px"><i>{{ msg }}</i></p>
                            {% endfor %}
                        {% endif %}

                    </div>
                    <div class="form-group">
                        {{ form.n_protocol_id.label(class="control-label col-md-2") }}
                        <div class="col-md-2">
                            {{ form.n_protocol_id(class_="form-control") }}
                        </div>
                    </div>
                    <div class="form-group">
                        {{ form.c_name.label(class="control-label col-md-2") }}
                        <div class="col-md-2">
                            {{ form.c_name(class="form-control") }}
                        </div>
                    </div>

                    <div class="form-group">
                        {{ form.c_sensor_type.label(class="control-label col-md-2") }}
                        <div class="col-md-2">
                            {{ form.c_sensor_type(class="form-control") }}
                        </div>
                    </div>
                    <div class="form-group">
                        {{ form.sensor_ip.label(class="control-label col-md-2") }}
                        <div class="col-md-2">
                             {{ form.sensor_ip(class="form-control") }}
                        </div>
                        {% if form.sensor_ip.errors %}
                            {% for msg in form.sensor_ip.errors %}
                                <p class="help-block" style="color:red;font-size: 10px"><i>{{ msg }}</i></p>
                            {% endfor %}
                        {% endif %}
                    </div>
                    <div class="form-group">
                        {{ form.sensor_port.label(class="control-label col-md-2") }}
                        <div class="col-md-2">
                            {{ form.sensor_port(class="form-control") }}
                        </div>
                    </div>
                    <div class="col-md-offset-2">
                        <input type="submit" class="btn btn-primary" value="提交">
                        <input type="reset" class="btn btn-default" value="重置">
                    </div>
                </form>
            </div>
            <div class="panel-footer">
            </div>
        </div>
    </div>
{% endblock %}


五、通过 jQuery 使用 AJAX

jQuery 是一个小型的 javascript 库,通常用于简化 DOM 和 JavaScript 的使 用。它是一个非常好的工具,可以通过在服务端和客户端交换 JSON 来使网络应用更 具有动态性。

JSON 是一种非常轻巧的传输格式,非常类似于 Python 原语(数字、字符串、字典 和列表)。JSON 被广泛支持,易于解析。JSON 在几年之前开始流行,在网络应用 中迅速取代了 XML 。


载入 jQuery

为了使用 jQuery ,你必须先把它下载下来,放在应用的静态目录中,并确保它被载 入。理想情况下你有一个用于所有页面的布局模板。在这个模板的 <body> 的底 部添加一个 script 语句来载入 jQuery :

<script type=text/javascript src="{{
url_for('static', filename='jquery.js') }}"></script>

另一个方法是使用 Google 的 AJAX 库 API 来载入 jQuery:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script color: rgb(78, 154, 6);"> url_for('static', filename='jquery.js') }}">\x3C/script>')</script>

在这种方式中,应用会先尝试从 Google 下载 jQuery ,如果失败则会调用静态目录 中的备用 jQuery 。这样做的好处是如果用户已经去过使用与 Google 相同版本的 jQuery 的网站后,访问你的网站时,页面可能会更快地载入,因为浏览器已经缓存 了 jQuery 。

我的网站在哪里?

我的网站在哪里?如果你的应用还在开发中,那么答案很简单:它在本机的某个端口 上,且在服务器的根路径下。但是如果以后要把应用移到其他位置(例如 http://example.com/myapp )上呢?在服务端,这个问题不成为问题,可以使 用 url_for() 函数来得到答案。但是如果我们使用 jQuery ,那么就 不能硬码应用的路径,只能使用动态路径。怎么办?

一个简单的方法是在页面中添加一个 script 标记,设置一个全局变量来表示应用的 根路径。示例:

<script type=text/javascript>
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>

在 Flask 0.10 版本以前版本中,使用 |safe 是有必要的,是为了使 Jinja 不 要转义 JSON 编码的字符串。通常这样做不是必须的,但是在 script 内部我们 需要这么做。

进一步说明

在 HTML 中, script 标记是用于声明 CDATA 的,也就是说声明的内 容不会被解析。<script> 与 </script> 之间的内容都会被作为脚 本处理。这也意味着在 script 标记之间不会存在任何 </ 。在这里 |tojson 会正确处理问题,并为你转义斜杠( {{ "</script>"|tojson|safe }} 会被渲染为 "<\/script>" )。

在 Flask 0.10 版本中更进了一步,把所有 HTML 标记都用 unicode 转义 了,这样使 Flask 自动把 HTML 转换为安全标记。

JSON 视图函数

现在让我们来创建一个服务端函数,这个函数接收两个 URL 参数(两个需要相加的 数字),然后向应用返回一个 JSON 对象。下面这个例子是非常不实用的,因为一般 会在客户端完成类似工作,但这个例子可以简单明了地展示如何使用 jQuery 和 Flask:

from flask import Flask, jsonify, render_template, request
app = Flask(__name__)

@app.route('/_add_numbers')
def add_numbers():
    a = request.args.get('a'0, type=int)
    b = request.args.get('b'0, type=int)
    return jsonify(result=a + b)

@app.route('/')
def index():
    return render_template('index.html')
{% endblock %}


正如你所见,我还添加了一个 index 方法来渲染模板。这个模板会按前文所述载 入 jQuery 。模板中有一个用于两个数字相加的表单和一个触发服务端函数的链接。

注意,这里我们使用了 get() 方法。它不会调用失败。如果字典的键不存在,就会返回一个缺省值(这里是 0 )。更进一步它还会把值转换为指定的格式(这里是 int )。在脚本( API 、 JavaScript 等)触发的代码中使用它特别方便,因为在这种情况下不需要特殊的错 误报告。

HTML

你的 index.html 模板要么继承一个已经载入 jQuery 和设置好 $SCRIPT_ROOT 变 量的 layout.html 模板,要么在模板开头就做好那两件事。下面就是应用 的 HTML 示例( index.html )。注意,我们把脚本直接放入了 HTML 中。通常更好的方式是放在独立的脚本文件中:

<script type=text/javascript>
  $(function({
    $('a#calculate').bind('click'function({
      $.getJSON($SCRIPT_ROOT + '/_add_numbers', {
        a: $('input[name="a"]').val(),
        b: $('input[name="b"]').val()
      }, function(data{
        $("#result").text(data.result);
      });
      return false;
    });
  });
</script>
<h1>jQuery Example</h1>
<p><input type=text size=5 name=a> +
   <input type=text size=5 name=b> =
   <span id=result>?</span>
<p><a href=# id=calculate>calculate server side</a>


这里不讲述 jQuery 运行详细情况,仅对上例作一个简单说明:

1、$(function() { ... }) 定义浏览器在页面的基本部分载入完成后立即执行 的代码。

2、$('selector') 选择一个元素供你操作。

3、element.bind('event', func) 定义一个用户点击元素时运行的函数。如果 函数返回 false ,那么缺省行为就不会起作用(本例为转向 # URL )。

4、$.getJSON(url, data, func) 向 url 发送一个 GET 请求,并把 data 对象的内容作为查询参数。一旦有数据返回,它将调用指定的函数,并把 返回值作为函数的参数。注意,我们可以在这里使用先前定义的 $SCRIPT_ROOT 变量。


六、Flask之MethodView

Flask 为 RESTful APIs 提供了一种简易的实现方式,可以针对不同的HTTP方法提供不同的函数。使用也很简单,只需要继承 flask.views.MethodView 然后只需要重写需要的方法:GET POST PUT 等。在请求url的时候,就可以根据请求的方法自动跳转到重写的方法了


class DeviceDelView(MethodView):
    def get(self,id):
        if session.get('admin'Noneis None:
            return redirect(url_for('sign.login'))
        else:
            DeviceList.query.filter(DeviceList.n_id == id).delete()
            db.session.commit()
            return redirect(url_for('device.device_list'))




03


Flask项目部署


一、uwsgi的安装和配置 

uWSGI是一个由python实现的web容器,可以兼容性比较好地发布Django,Flask等pythonweb框架的应用。因为本质上来说uwsgi是python的一个模块,所以可以用pip install uwsgi直接来安装它。


[uwsgi]

socket = 127.0.0.1:5051

pythonpath = /home/wyz/flask

module = managewsgi-file =  /home/wt/flask_prj/manage.py

callable = app

processes = 4

threads = 2

daemonize = /home/wt/flask_prj/server.log



pythonpath指出了项目的目录,module指出了项目启动脚本的名字而紧接着的wsgi-file指出了真正的脚本的文件名。callable指出的是具体执行.run方法的那个实体的名字,一般而言都是app=Flask(__name__)的所以这里是app。processes和threads指出了启动uwsgi服务器之后,服务器会打开几个并行的进程,每个进程会开几条线程来等待处理请求,显然这个数字应该合理,太小会使得处理性能不好而太大则会给服务器本身带来太大负担。daemonize项的出现表示把uwsgi服务器作为后台进程启动,项的值指向一个文件表明后台中的所有输出都重定向到这个日志中去。


二、nginx的安装和配置

nginx常用命令:

nginx  启动nginx

nginx -s stop/reload  停止nginx/重载配置文件

nginx -v  查看版本

nginx -t  测试配置文件是否有语法上的错误等

安装完成后默认的nginx的配置文件位于/etc/nginx/conf.d/default.conf

server {

        listen       80;         //默认的web访问端口

        server_name  xxxxxx;     //服务器名

        #charset koi8-r;

        access_log   /home/wt/flask_prj/logs/access.log;    //服务器接收的请求日志,logs目录若不存在需要创建,否则nginx报错

        error_log   /home/wt/flask_prj/logs/error.log;         //错误日志

        location / {

            include        uwsgi_params;     //这里是导入的uwsgi配置

            uwsgi_pass     127.0.0.1:5051;   //需要和uwsgi的配置文件里socket项的地址

                                             //相同,否则无法让uwsgi接收到请求。

            uwsgi_param UWSGI_CHDIR  /home/wt/flask_prj;     //项目根目录

            uwsgi_param UWSGI_SCRIPT manage:app;     //启动项目的主程序(在本地上运行

                                                     //这个主程序可以在flask内置的

                                                     //服务器上访问你的项目)

}

}

三、 开启nginx

cd /var/log/nginx

mkdir log

nginx -t -c /etc/nginx/nginx.conf

systemctl start nginx.service




以上是关于IoT之Web--Flask的主要内容,如果未能解决你的问题,请参考以下文章

面向面试编程代码片段之GC

“云中论道”之——使用开源技术和Azure公有云服务快速搭建云端IoT解决方案(下)

物联网(IoT)开发使用 Arduino 和 Python在 Bluemix 上开发一个 IoT 应用程序之控制LED灯开关

鸿蒙之连接IoT云平台(华为云)

IOT之蓝牙安全 1) 从入门到放弃

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情