欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flask,web框架学习_持续更新

程序员文章站 2022-07-02 19:21:44
...

《Flask web 开发:基于python》

Always believe that something wonderful is about to happen

第1章 安装

1.1virtualenv模块安装

#在shell窗口
easy_install virtualenv #即可
或者
pip isntall virtualenv 
#查看版本
virtualenv --version

1.2创建虚拟环境venv 及**

virtualenv venv#venv为名字,可随意改,但常用venv
#在目录下
venv\scripts\activate #注意是反斜杠,是地址

1.3在虚拟环境中作业

后面的所有安装模块及操作,都在虚拟环境中执行!虚拟环境就是一个独立的环境,将我们所有的文件都集中在一个世界里面,尤其是在有很多不同办法的应用、模块时,你会知道的虚拟环境的友好。

第2章 程序结构

2.1程序初始化

import flask import Flask
app=Flask(__name__)
#在Flask类中创建一个叫做app的实例,__name__是程序主模块或包的名字,在这里作为变量

2.2路由和视图函数

路由:一个处理url和函数之间映射关系的程序。

装饰器/修饰器:一个用来接受函数的函数,并且返回一个函数,如@app.route(),修饰器的惯用做法,就是将函数注册为事件的处理程序。

@app.route("/") #此处装饰器就是将index()函数绑定一个url
def index():
    return"Hello world!"
#把index()函数注册为根目录的处理程序


@app.route("/user/<name>")
def index():
    return"hello,%s" %name
#“动态路由”

2.3启动程序

实例的.run()方法来启动程序,调试模式的话则设置debug=True:

if __name__="main":
    app.run(debug=True)
    #app.debug=True
    #app.run()
#调试模式下,浏览器在脚本修改代码后并保存的同时,自动重新载入,并且在发生错误的时候,提供一个调节器

以上三步骤就可以构造一个功能最简单的flask程序了,重点是理解修饰器的实质含义

2.5请求响应循环

介绍flask的设计理念

1.程序和请求上下文:flask使用上下文临时将某些对象变成全局可访问——一个线程中的全局访问,不会干扰其他线程的。

from flask import request

@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your browser is %s</p>' % user_agent
#request变量,这是一个请求上下文。只有当请求被推送之后,request才会有意义,才可以使用request,否则就会报错,因为缺少上下文。

程序上下文和请求上下文:有种“环境”意味,解释见上面。

2.请求调度:就是找到某个请求的视图函数。

3.请求钩子:为了避开视图函数使用重复代码,加一个“钩子”,这个钩子关联到一个函数,在请求发送到视图函数之前、之后调用

4.响应:响应就是flask调用视图函数后,返回的一个值或者一个页面,也有在重定向响应中,返回的是一个新的地址链接。

第3章 模板

3.1 jinja2模板引擎

在安装jinja2后,将其应用到程序中,jinja2有一套自己的模板和语法,其主要作用是控制HTML响应、显示效果以及程序和页面之间信息交互。

1渲染模板:在通用骨架(即模板)中,传入我们个性的信息,最终展现出来。

from flask import Flask,render_template#导入render_template函数
#...
@app.route("/user/<name>")
def user(name):
    return render_template("user.html",name=name)
#传入name变量实际值到user.html中
#render_template()函数第一个变量第模板名字,第二个是键值对

2变量
模块中占位符{{name}}表示一个变量,告诉模板引擎,这个位置的值从渲染模板时,使用的数据中获取并替代。

3控制结构:即是jinja2提供的,用来改变渲染流程,有以下几种。

1条件控制语句

{% if user %}
    Hello,{{user}}!
{% else %}
    Hello,stranger!
{% endif %}

2将多次重复的代码写成一个单独的文件中,例如common.html,使用包含命令,传到其他文件中,避免重复

{% include "common.html"%}
#...

3继承,也是重复使用代码,先建立基模板,再建立衍生模板,基模板的某些元素可以在衍生模板中更改。

#基模板base.html
#只有block标签定义的元素才可在衍生模板总更改
</html>
<head>
    {% block head %}
    <title>{% block title %}{% endblock%} - my application</title>
    {% endblock%}
</head>
<body>
    {% block body%}
    {% endblock%}
</body>
</html>

#衍生模板
{% extends "base.html"%}#继承基模板
{% block title %}Index{% endblock %}#直接加入需要更改的元素
{% block head %}
    {{super()}} #head元素中仅仅改了title,所以剩下的还是照旧,就super()来获取原来的内容   
{% endblock%}
{% block body %}
    Hello,World!
{% endblock %}

从以上三种控制方法,得出以下结论

  • 控制结构基本形式{% xxx %},条件控制,则是比常见的if从句多一个结尾{% endif %}
  • 控制结构语句都是有始有终,不管是{% endif %}还是{% endblock%}
  • 继承方法中,block标签是更改元素的关键。

    3.2使用flask-bootstrap集成Twitter-BootStrap

    BootStrap是一个Twitter开发的开源框架,为了在程序中集成BootStrap,我们使用一个叫做flask-bootstap的扩展。

通过初始化化bootstrap=BootStrap(app),即可将基模板为我所用,利用模板继承{% extends "bootstrap/base.html"%},就可以定义出统一页面了。

3.3自定义错误页面

常见的错误有路由错误404和未处理的异常500,flask允许我们基于模板自定义错误页面:

@app.errorhandler(404)
    def page_not_found(e):
        return render_template("404.html"),404

@app.errorhandler(500)
    deg internal_serner_error(e):
        return render_template("500.html"),500

Thinking

这里代码与前几章定义index.html代码进行对比,有显著的差别,index.html使用@app.route("/")修饰器注册,而这里是好像是一个“类”,@app.errorhandler(404),代表是访问的路由不存在这类情况,即可响应404.html,不涉及到"路由"功能。

3.4链接

用的最多就是导航条了,链接到的有相对地址绝对地址,常用的是url_for()函数,最简单的用法是将函数名(不带后缀类型)作为参数,生成对应的url。还有,用此函数生成动态地址,将动态部分作为关键字参数传入。

url_for("index")#注意不是index.html,这里不需要后缀
url_for("index",_external=True)#返回绝对地址
url_for("user",name=name)#生成动态url链接

3.5 静态文件

常见静态文件有HTML中使用的图片、JavaScript源代码文件和CSS。

{#定义收藏夹图标#}
{% block head %}
{{ super() }}{#保存定义块的原始内容#}
<link rel="shortcut icon " href="{{ url_for("static",filename = "favicon.ico")}}" type="image/x-icon">
<link rel="icon"  href="{{ url_for("static",filename = "favicon.ico")}}" type="image/x-icon">
{% endblock %}

3.6使用flask-moment本地化时间和日期

网络上有个统一的时间,UTC,universal time coordinated协调世界时间,我们将之转为本机时间。flask-moment依靠着moment.js和jquery.js库来实现功能。二者都是在HTML引入这两个库。

要实现显示时间功能,1引入时间变量datetime,2在基模板中引入moment.js库,3在衍生模板中渲染时间戳。时间渲染详见momentjs介绍

步骤

#安装模块及初始化
pip install flask-moment#安装模块

from flask.ext.moment import Moment
moment=Moment(app)#初始化flask-moment

#template/base.html,引入moment.js库
{# 本地化时间 之引入moment.js,jquery在bootstrap中自动引入了 #}
% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

#hello.py   加入datetime变量
from datetime import datetime
@app.route()
    def index():
        return render_template("index.html",current_time=datetime.utcnow())

#template/index.html,使用flask-moment渲染时间戳
<p>The local data and time is {{ moment(current_time).format("LLLL") }}.</p>{#时间显示#}
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
<p>{{ moment.lang("ch") }}</p>#涉及到语言

第4章 web表单

什么叫做表单?每个web表单都由一个继承自Form的类表示。通俗来说,一个表单经过渲染为HTML后,就是我们看到网页上有交互功能、能键入信息的框框。
flask-wtf 扩展非常适用于表单处理

4.1表单类

新建一个表单,WTForms支持的HTML标准字段及內建的验证函数种类很多,见书本列表。

from flask_wtf import Form
from wtforms import StringField,SubmitField,PasswordField
from wtforms.validators import Required,Length,Email#验证函数
def NameForm(Form):
    name=StringField("your name:",validators=[Required])#名字非空,这是一个字段
    password=PasswordField("your password",validators=[Required(),Length(6)])#非空、长度不少于6
    submit=SubmitField("Submit")

4.2把表单渲染为HTML格式

利用Bootstrap中预先定义好的表单样式渲染整个Flask-WTF表单

{% import "bootstrap/wtf.html" as wtf %}{#此处是导入模块来渲染表单,就是将表单渲染为HTML样式#}
{% wtf.quick_form(form)%}

4.3在视图函数中处理表单

表单中的数据,传递给视图函数,显示具有个性的消息

@app.route("/",methods=["GET","POST"])
    def index():
        name=None #自定义变量name,通过赋值None来获得
        form=NameFlaskForm()#form是对象、实例
        if form.valitate_on_submit(): #form.valitate_on_submit()要是填写正确,返回True.
            name=form.name.data #name在前面自定义时,是在if外面
            form.name.data=""
return render_template("index.html",name=name,form=form)#index.html中的变量name,form接受传入的参数值,name=name,前面的name是变量。

4.4重定向和会话

重定向就是为了避免提交表单刷洗,会提醒再次确认的现象发生

会话session主要是用在重定向中,在请求之间,记住数据,session["name"]=form.name.data,name=session.get("name")可以看出来,session好比一个字典,用来储存我们的信息。

4.5Flash消息

请求完成后,有时候需要让用户知道了状态的变化,比如显示确认、警告或者错误from flask import flash。书中例子是在视图函数总添加提醒出来的条件,在模板文件中设置提醒样式即可。

第5章 数据库

使用Flask-SQLAlchemy管理数据库,在学习之余结合《SQL必知必会》终于理解了这一章节,这一张内容大致如下:

  • 关系型数据库的特点,创建模型、关系
  • 在shell中使用flask-SQLAlchemy管理数据库的基本操作,创建表、行及删除行、查询
  • 进阶为在视图函数中操作数据库,将视图函数填入的form.name.data加入数据库,进而针对不对访问对象有不同的欢迎信息
  • 对数据库的迁移migrate、更新和维护等。

5.1常用语法
初始化及简单配置一个SQLite数据库:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app=Flask(__name__)

basedir=os.path.dirname(os.path.abspath(__file__))
#获得当前文件的绝对目录,想一下file为啥不能改为name?_file__ 是用来获得模块所在的路径,固有的“属性”吧
app.config["SQLALCHEMY_DATABASE_URI"]="sqlite:///"+os.path.join(basedir,"shiyanku.sqlite")
#config[]字典配置URI,而不是URL,os.path.join(a,b)用来组合路径的,文件名为shiyanku.sqlite
app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"]=True
#自动提交数据库
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]=True
#书中无本此语句,自己根据运行bug添加

db=SQLAlchemy(app)
#SQLAlchemy实例

5.2定义模型:模型就是持久化实体,模型一般是python的类,类中的属性就是数据表的列。

class Role(db.Model):
    __table__="roles" #模型中数据表的名称
    id=db.Column (db.Integer,primary_key=True) #id列,为主键
    name=db.Column(db.String(64),unique=True)#name列,有唯一性
    
    def __repr__(self): #定义一个方法,返回具有可读性的字符串表示模型,具体解释详见下面链接
        return "Role:%r" %self.name
  • __repr__不管有没有print 在前面,都会显示我们想要的内容,而不是内存地
  • 注意列Colume(类型,选项)的类型和选项来定义列属性,比如整数/字符串,主键/唯一性等等

__repr__和__str__方法区别

5.3关系
关系型数据库就是利用关系将不同表中的行联系起来,为我所用。

“一个角色role可以多个用户user,但是一个用户只能是一个角色”一对多的关系如下:

class Role(db.Model):
#...
users = db.relationship("User",backref="role",lazy="dynamic")#lazy指明如何加载相关记录
#backref反向引用,这一语句意思:一个users列表,是一个相同role角色的列表。

class User(db.Model):
#...
role_id=db.Column(db.Integer,db.ForeignKey("roles.id"))、
#建立role_id外键列,用来“一个用户只能一个角色”来映射roles的id列

5.4数据库操作

上面讲数据库完成了配置,下面就是在Pythonshell中操作数据库

创建表

在pythonshell中输入:
>>>from hello import db #hello为hello.py
>>>db.create_all() 
#这个时候就在目录下新建一个xxx.sqlite文件,xxx是在配置中指定的。
#删除表格:db.drop_all()

新建行

>>>from hello import Role,User
>>>admin_role=Role(name="admin")
>>>user_role=Role(name="user")
>>>user_lengqian=User(username="lengqian",role="user_role")
>#...
#role="user_role",role在关系中赋予User模型role属性(backref="role")(小九九:role这个统一赋值的话,在后期就可以容易的列出同一个角色的用户列表...)
#注意,这些数据只是存在python中还未写入数据库
>>>db.session.add(admin_role)
>>>db.session.add(user_lengqian)
#或者简写如下:
>>>db.session.add_all([admin_role,user_lengqian])

再次确认提交:
>>>db.session.commit()

修改、删除行

   #修改为直接修改重命名
>>>admin_role.name="adminstrator"
>>>db.session.add(admin_role)
>>>db.session.commit()
 #删除行
>>>db.session.delete(admin_role)
>>>db.sessiom.commit()

查询行:

Flask-SQLAlchemy为每个模型类都提供了query对象?(方法吧)。配合使用过滤器进行精确筛选

常见过滤器,有filter_by(条件),filter(),order_by(条件)按条件进行排序返回一个新查询

常见查询执行函数,有all(),first(),count(),get()等

User.query.all()
#返回的是一个列表[<User u"lengqian">,<...>]

User.query.filter_by(username="lengqian").all()
#返回的是一个列表[<User u"lengqian">]
#filter为等值过滤器,username="lengqian"就是匹配条件

5.5在视图函数中操作数据库

现在是简单的在视图函数中操作数据库,比如将用户submit的用户名与已有的数据库模型User表users进行对比,最后展示具有个性的页面。

借助书本例子,关系到数据库的操作有添加查询、添加,重点在于理解session["known"],个人认为,session作为会话,权当做一个类,known是一个实例,在这里是给known赋值,session["known"]=True,只是在这么一个视图函数的特殊环境中,才有session参与进来

#书本例子
@app.route("/",methods=["GET","POST"])
def index():    
    form = NameForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.name.data).first()
        if user is None:
            user = User(username=form.name.data)
            db.session.add(user)
            session["known"]=False
        else:
            session["known"]=True
        session["name"]=form.name.data
        form.name.data="" #无此行代码时,submit后刷新总是有提醒,个人理解:post提交一次后,form.name.data就被清空了
        return redirect(url_for("index"))#redirect函数,重定向响应
    return render_template("index.html",form=form,current_time=datetime.utcnow(),name=session.get("name"),known=session.get("known",False))

5.6集成python shell

就是将数据库的数据自动提交给shell中,利用的是flask-script的Shell命令,这个导入只是将我们程序当中的数据库实例、模型注册到shell中,这样的话,就不用我们一个个手动在shell中添加进去了。

以上只是书本描述的一部分作用,更重要的是,将下面文件运行python c:/..../manage.py shell后,会出现如下代码。这些待选项就是我们用来操作的方法:

    ============== RESTART: C:\Users\Administrator\flasky\hello.py ==============
usage: hello.py [-?] {shell,db,runserver} ...

positional arguments:
  {shell,db,runserver}
    shell               Runs a Python shell inside Flask application context.
    db                  Perform database migrations
    runserver           Runs the Flask development server i.e. app.run()

optional arguments:
  -?, --help            show this help message and exit

有以下几种操作:

python c:/../../manage.py shell #启动python shell
python c:/../../manage.py db init #数据库的初始化,可建造xxx.sqlite
python c:/..../manage.py db upgrade #更新数据库
python c:/..../manage.py runserver #启动运行

讲了这么多操作,注意点在于加入路径操作才会有满意的结果,否则出现了not package或者toplevel等等各种错误

若想要把对象添加到列表中,可以为shell命令注册一个make_context函数,

from flask_script import Shell

def make_shell_context():
    return dict(app=app,db=db,User=User,Role=Role)
manager.add_command("shell",Shell(make_context=make_shell_context))#以后再解释。

if __name__== "__main__"
manager.run()   #集成python shell和原来程序有点不同,就是将app.run()改为manager.run()这时候运行如书中一样,运行之后出现一个类似普通shell的界面

如上面所示,make_shell_context()函数注册了程序、数据库实例和模型到shell中,可以如下查看:

>>>python hello.py shell
>>>app
<Flask "app">

>>>python hello.py db
>>>...#数据库库的路径

若没有这样配置的话,要自己手动导入对象才能往下操作

>>>hello
>>>from hello import app
>>>app
>>><Flask "app">

5.7使用Flask-Migrate实现数据库迁移

能够跟踪数据库模式的变化,增量式地 把变化应用到数据中

hello.py:配置flask-migrate

from flask_migrate import Migrate,MigrateCommand
#...
migrate=Migrate(app,db)
manager.add_command("db",MigrateCommand)

步骤:暂时放下来,后期再在项目中补充,挺难的,尤其是脚本的创立(之所以这样,是在前面的pythonshell中失败了)
更新:将上述代码加入程序当中即可

  • 在shell中,利用init子命令建立迁移仓库(venv)...python hello.py db init 即可创建迁移仓库
  • 实际操作,更新数据库(venv)...python hello.py db upgrade 即可

第6章 电子邮件

6.1测试邮件

本章内容见电子邮件QQ邮箱.md(9/17 2017)

单一的邮件测试成功,详见电子邮件QQ邮箱.mdqq.py,问题主要是在于变量的设置和POP3/SMTP授权码的最新属性。

6.2在程序中集成发送邮件
在程序中集中发送、异步发送电子邮件失败。详见hello2.py,暂时放一放吧#0908
更新:上述错误主要在于授权码没有更新,更新后测试成功单独见“QQ邮箱.md”

6.3异步发送电子邮件

第7章 大型程序的结构

本章节最大的问题是“单个文件hello.py转为为大型程序”的描述不足,在结合git checkout的条件下,将作者代码和自己的对比,一点点转化过来。大概能理解和正常运行了。

7.1项目结构

在这里,使用包和模块组织大型程序的方式,其最大作用是,将功能分开,有利于后期的维护等。

7.2配置选项

在实例生产中,我们需要程序在不同的环境中运行,比如测试、开发和生产,这个时候就需要不同的配置了,于是config.py产生了。config字典中,注册不同的配置环境,一般情况还要注册一个默认的配置,如:"default":DevelopmentConfig

7.3程序包

  • 使用工厂函数:就是为了创建,在不同配置环境下的程序实例,用来测试程序运行效果。比如,在开发、生产和测试中。
  • 在蓝本中实现程序功能:让路由先休眠来定义路由简单,使拆分开的可以嵌套一起并运行-使得各个模块可以单独编辑维护

7.4x

  • 启动脚本:由manage.py来启动脚本
  • 需求文件:需求文件生成,requirements.txt用来记录所有依赖包及其版本号在shellpip freeze >requirements.txt;新生成虚拟环境的完全副本:pip install -r requirements.txt即可。文件名自己定义,一般是常用requirements.txt
  • 单元测试:就是实实在在的将程序可能出现的错误写出来,进行测试。常用标准库中的unittest包编写。

8.4使用flak-login认证用户

第九章 用户角色

用户角色,代表着用户的权限,比如发表文章、评论、关注等,在这里使用的是权限的组合,再根据用户的角色分配不同的权限。具有通用性。10/26/2017 11:24:29 PM

9.1角色在数据库中的表示

在角色模型中,添加permissions字段,其值是一个整数,表示位标志。各操作对应一个位位置,能操作就是1;增加的default列(db.Boolean),则是用来区分是否为管理员。

文中的《程序的权限》表格,并不是flask的设计理念,而是我们的自己添加的属性。我们构建一个8位的二进制数字,是位标志,不同的值代表着不同的权限(数字和权限附加到一起了)。代表不同权限的值相加(权限的相加)结果,就可以代表着不同的用户了,例如,普通用户和管理员肯定不同的。而后,我们构建函数来实现数字和角色的演变。

实际操作:

在models.py中建立"权限常量"???

class Permission:
    FOLLOEW = 0x01
    ...

第12章 关注者

12.1 再论数据库关系

  1. 数据库使用关系建立记录之间的联系。一对多关系是最常见的关系类型,实现这种关系时,我们要在"多"这一侧键入一个外键,指向"一"这一侧联结的记录(用户是多,角色才是一,"多个用户配一个角色",每个用户通过外键和唯一的角色联系起来)。
  2. 大部分的其他关系类型都可以从一对多类型中衍生。一对一关系类型是简化版的一对多关系--限制"多"这一侧最多只能有一个记录即可。

    12.1.1 多对多关系

  3. 需要添加第三张表-关联表。多对多关系就可以分解成原表和关联表之间的两个一对多关系。例如,在student和class两个模型之间,我们建立一个关联表,表中的每一行都表示一个学生注册的一个课程。学生和关联表是一对多关系(一个学生多个课程选择),课程也是如此(一个课程多个学生选择)。
  4. 于是,我们在,也就是关联表中建立外键,产生联系。注意关联表只是一个table,不是模型。SQLAlchemy会自动接管这个表。
  5. 多对多关系仍旧使用定义一对多关系的db.relationship()方法定义,不同的是,必须把secondary参数设为关联表。多对多关系可以在任何一个类中定义,backref参数会处理好关系的另一端。注意其中的回引backref=db.backref("studetns",lazy='dynamic')。
  6. 12.1.2 自引用关系

问题汇总

1.程序启动问题

程序启动有以下三种方法:

  1. 在shell中交互式输入一条条命令(-Shell就是一个命令行解释器,它的作用是解释执行用户的命令,用户输入一条命令,Shell就解释执行一条,这种方式称为交互式(Interactive))
  2. 在shell中批处理直接执行脚本(批处理(Batch),用户事先写一 个Shell脚本(Script),其中有很多条命令)
  3. 在python自带的IDLE中执行脚本

2.程序小问题

  1. 第九章《用户角色》中,在model.py中更改了角色内容,每次在manage.py shell之后,都要调用Role.insert_roles来更新数据库10/30/2017 3:20:34 PM
  2. 同样的,在很多章节中,修改数据库项目后,都要执行python managy.py db upgrade,进行数据更新

问题扩展:

1.一个脚本文件hello.py在IDLE中执行,报错no module named flask_stript,在shell中输入C:\Python27\Flask\hello.py还是报错(输入地址麻烦的话可以直接将文件丢进shell中执行),报错一样,在shell中键入python hell.py shell成功运行。

2.书本中多次涉及到“命名空间”,例如flask-bootstrap从flask.ext命名空间导入。
结合之前

3.网络请求只是还需要补充GET,POST等

4.蓝本的理解flask蓝本

5.工厂函数的理解:个人理解,工程函数def current_app(config_name):,主要是用来测试程序在不同配置环境下的运行效果。若是普通函数的话,程序实际在运行第一次的时候就随之建立了,但是在工厂函数中,先没有创建实例,而是在配置后,再创建实例了,多个配置config,就可以创建很多程序实例用来测试了。0912

6.current_app以及在异步发送邮件一章中的current_app.get_current_boject()辨识链接1

[email protected]和classmethod的区别:链接2

阿里云部署flask:阿里云部署flask+wsgi+nginx

flask GitHub优秀作品:刘志辉

问题汇总

  1. 关于pip模块的安装:

    1. 电脑系统更换SSD系统也全新安装之后,进行开发环境的安装,python2.7.13
    2. 第一步是安装virtualenv模块,在CMDshell窗口中使用pip isntall xxeasy_install xx,提示没有pip和easy_install命令
    3. 在python/scripts安装包中发现有pip和easy应用程序,将该目录添加至path即可使用。
    4. 提示,我百度的时候,大部分做法是下载pip.exe文件安装后再使用。
  2. 使用pip install -r requirements/common.txt安装依赖包,仅部分安装了,重试还是如此。后面将common.txt中明显不需要的模块删除后放至根目录下,安装成功。这算是一个bug吗?

  3. 基本步骤
    1. python安装及path配置,注意pip程序路径的配置 # 安装python时自带pip和easy_install应用程序,注意也需要添加至path
    2. 安装virtualenv模块 #使用pip install进行安装
    3. 安装虚拟环境并**
    4. 在虚拟环境中安装所需要的模块pip install -r requirements.txt即可。
    5. 开发运行环境配置完毕。3/8/2018 10:58:11 PM