简介
flask框架是用python语言编写的一款小而精的web开发框架,最近学习用到了该框架,实现了网站服务器基本的地址路由、数据库连接、脚本管理、登录管理等功能。
路由
地址路由是web服务器的最基本功能,flask使用修饰器的方法实现。修饰器个人理解就是一个嵌套函数,将被修饰的函数作为参数传入修饰器函数中,修饰器函数可以在需要时回调被修饰的函数。flask框架已经写好了地址路由的函数route(url),在处理好web请求后,框架再回调用户定义的函数作为响应。
作为服务响应,flask框架使用jinja引擎实现网页模板的渲染,开发时调用render_template函数。render_template是一种支持模板式网页解释渲染的方法,其第一个参数为html文件名,第二个参数为HTML文件中需要使用的变量。flask会到项目下的templates文件夹查找该HTML文件,HTML文件中以{{variable}} {%expression%}的jinja代码会被flask解释执行。利用jinja语法,可以实现网页间类似于面向对象的继承性。实现时,可以用基类网页表示多个网页相同的部分,不同的部分留有接口{%block xx%}{%endblock%},让继承它的子类网页来实现{%block xx%} xx_content {%endblock%}。例如:
python
def index():
images = Image.query.order_by("id desc").limit(10).all()
return render_template("index.html", images = images)
base.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{%block title%}{%endblock%}</title>
<link rel="stylesheet" href="{%block css%}{%endblock%}">
</head>
<body>
<%block content%><%endblock%>
</body>
index.html
<%extends "base.html"%>
<%block content%>
{%for image in images%}
...
{%endfor%}
<%endblock%>
数据库
数据库管理使用flask_sqlalchemy,该扩展使用orm模式对关系型数据库进行管理。orm模式是一种使用描述对象与数据库映射的元数据,将程序中的对象自动持久化到关系数据库中的技术。使用时先提供数据库的账号、密码、数据库地址、数据库名建立连接并生成一个SQLAlchemy类的数据库管理对象。以mysql为例,连接的url如下:
'mysql://user\_name:user\_password@database\_ip:database\_port/database\_name'
建立连接后,使用SQLAlchemy.create_all方法来创建程序中与orm对象对应的表,使用SQLAlchemy.drop_all()方法来删除数据库中的表。
生成的SQLAlchemy对象包含一个session对话属性来与数据库对话,执行一些表的增加、删除行等操作。例如,通过add方法可以在数据库表中增加某个orm对象对应的行。不管何种操作,在session对话结束后使用commit方法提交更改。
orm对象的属性与表中的字段对应,创建时需要指明字段类型,是否是主键,是否自动增长,是否有外键等特性。外键用来表示这张表的字段值引用于另一张表的unique字段,当引用的字段没有该值时,此表的字段值无效。如果定义了外键,两张表之间需要关联,这种关联在对象中通过SQLAlchemy.relationship(“Object_name”)建立。外键值在引用的表中必须是唯一的,而在本表中该值可以是重复的,即本表的多行可以拥有相同的外键值,是一种一对一或多对一的关系。
以外,orm对象在创建时需要继承SQLAlchemy.Model类,该类中有query属性,可以对表中的行与字段进行操作。query.get(“id”)可以以主键为参数获取对象,query.filter(“condition_exp”)可以根据条件查询获得对象,first()可以获得第一个对象,order(“order_rule”)可以进行排序,等等。
脚本管理
脚本管理功能可以通过命令方式启动某个函数,从而对服务器进行管理和维护,并将脚本与系统分开。flask通过flask_script扩展提供脚本管理的功能。使用时只要从该模块中导入Manage类,然后传入当前的Flask应用对象生成一个Manage类对象,调用该对象的run方法后,就可以接收命令行中的选项作为函数名参数调用。作为可以被脚本调用的函数,需要在函数前用修饰器“@manager.command”或“@manager.option”修饰,前者用来修饰无参数的函数,后者可以用来修饰有参数的函数。如以下例子所示:
manage.python
@manager.command
def print_hello():
print "hello";
if __name__ == "__main__":
manager.run()
print "manage is run"
命令行
python manage.py print_hello
hello
登录
flask提供的登录功能包括查询用户是否已登录,是否激活,是否可以匿名登录,以及通过id访问当前用户对象。因此,为了实现以上功能,在用户类的定义中要实现is_authenticated, is_active, is_anonymous, get_id方法,可简单实现如下:
def __repr__(self):
return "<User %d %s>" %( self.id, self.userName)
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return self.id
定义了这些方法后,就可以使用从flask_login模块导入的LoginManager类来对登录进行管理。使用时,先以flask实例对象为参数传入LoginManager类的构造函数,生成login_manager对象。然后定义获取用户对象的方法,并用“@login_manager.user_loader”修饰器修饰。接下来,在处理登录的过程中使用login_user(User)函数可用于告知flask该用户已登录。在退出登录时使用logout_user()函数退出当前用户的登录。如果某个页面是需要登录用户才能访问的,可以用“@login_required”修饰器修饰处理路由的函数。此时用户在跳转到该页面时,flask会检查当前用户是否登录,如果没有登录过则访问失败。另外,可以使用LoginManager.login_view来设置登录的地址,这样就会跳转到登录页面使用户登录。此时访问的url中会带有next路径参数,代表用户原本请求访问的路径,服务器可以在用户登录成功后通过该参数将用户重新带到之前需要访问的页面。当用户登录后,可以通过current_user变量直接访问当前用户实例。示例代码如下:
登录设置
login_manager = LoginManager(app)
login_manager.login_view = "/regloginpage/" #设置登录地址,当访问需要登录的页面而未登录时自动跳转
定义flask获取用户实例的方法
@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
登录与登出
login_user(User)
logout_user()
设置登录用户才能访问的页面
@Flask.route("/profile/{int:user_id}")
@login_required
def profile():
...
访问当前登录用户的id
current_user.id
flash闪存与本地文件访问
def put_msg(msg):
flash(msg, category="factory")
def get_msg():
get_flashed_messages(category_filter=["factory"])
flask提供本地文件的访问功能,方法是send_from_directory(file_path, file_name)file_path是本地存储文件的路径,file_name是文件名。该方法一般与从request中获取的文件对象的save方法使用,即file.save(file_path)。示例代码如下:
@Flask.route("/upload/", method ={"post"})
def upload_file():
file = request.files["file_key"]
file.save("C:/")
@Flask.route("/get_file/{string:file_path}/{string:file_name}")
def get_file(file_path, file_name):
return send_from_directory(file_path, file_name)