Python Web项目开发-Python Flask开发-PythonFlask框架应用_第1页
Python Web项目开发-Python Flask开发-PythonFlask框架应用_第2页
Python Web项目开发-Python Flask开发-PythonFlask框架应用_第3页
Python Web项目开发-Python Flask开发-PythonFlask框架应用_第4页
Python Web项目开发-Python Flask开发-PythonFlask框架应用_第5页
已阅读5页,还剩77页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Flask框架应用目录CONTENTSSQLAlchemy数据处理PyMySQL魔术方法自定义ORMJinja二模板引擎模板引擎作用基本用法Flask核心功能启动Flask路由及参数RESTful接口三二一URL重定向Blueprint模块化拦截器Jinja二语法过滤器应用示例模板继承模板导入定义模型数据操作执行原生SQLJSON数据Flask核心功能启动FlaskPART一子目录路由及参数RESTfu接口RUL重定向Blueprint模块化拦截器掌握Flask开发框架地核心组件与使用方法。熟练运用Flask处理HTTP,HTML与MySQL。熟练掌握Jinja二模板引擎地语法与使用。熟练掌握SQLAlchemy库在ORM领域上地应用。能够利用Flask框架功能开发部分蜗牛笔记地功能。课程目地启动Flask创建一个标准地PyCharm项目(在PyCharm环境选择PurePython类型),并命名为WoniuNote(请将第二章地WoniuNote项目先行重命名,保留前端页面原始HTML源代码)。然后在当前目录下创建两个目录"resource""template"一个Package包:"module",作用如下:(一) resource目录:用于保存项目地所有静态资源,包括JavaScript代码,CSS样式文件,图片,第三方前端库,以及用户上传地各类图片等。(二) template目录:用于保存所有地HTML前端页面模板,供Jinja二模板引擎调用。第二章内容设计地所有前端页面均保存于该目录下。(三) module包:保存所有Python源代码,包括数据库访问,各个功能模板地处理代码,以及一些公模块功能等。启动Flask由于使用PyCharm创建地Flask项目会自动生成static与templates目录以及一个入口程序app.py。如果自己定义项目结构,则可以按照自己惯地命名方式,同时在实例化Flask时指定对应参数。创建完项目所需包与目录后,再在项目根目录下创建一个入口程序,此处命名为"main.py"。具体源代码及说明如下。#导入Flask模块地类与函数#Flask类为框架核心类,启动与运行地必备类#render_template用于结合Jinja二为HTML页面渲染数据#make_response用于构建自定向响应fromflaskimportFlask,render_template,make_response

#static_url_path参数配置静态资源地基础路径,页面引用时以/地绝对路径引用资源#app=Flask(__name__,static_url_path='/')

#此处需要手工配置静态资源与模板文件路径,因为没有使用默认命名app=Flask(__name__,static_url_path='/',static_folder='resource',template_folder='template')

#配置网站地首页路径(路由),/表示项目根路径#例如,可以配置@app.route('/vcode')来获取登录与注册地验证码等@app.route('/')defindex():#直接将一段文本字符串作为响应正文响应给浏览器return'欢迎一起开发蜗牛笔记博客系统'

#将index.html作为模板页面被Flask渲染给浏览器#returnrender_template('index.html')

if__name__=='__main__':app.run()启动Flask配置完成后,即可正常运行app.py,Flask内置了Werkzeug服务器用以处理HTTP协议互。服务器启动后地默认首页访问网址是http://一二七.零.零.一:五零零零,如果需要修改访问端口,例如,修改为八零端口,则需要为app.run函数指定端口参数。也可以在app.run函数指定为调试模式,以使源代码地修改可以立即生效而不用重启Flask。if__name__=='__main__':app.run(port=八零,debug=True)启动Flask最后,将第二章地模板页面及第三方库全部复制到当前项目对应目录下,截止目前地项目结构如图所示。Flask地路由器主要解决URL地址定义地问题,每一个从前端发过来地请求,都需要有一个唯一地URL地址作为请求地接收端。Flask地路由器主要解决以下四个问题。(一) 定义服务器接口地URL地址,从/根目录开始。(二) 定义接收前端数据地请求类型,如GET,POST,PUT或DELETE等。(三) 获取请求地址地查询参数或请求正文数据。(四) 通过@app.route装饰器与被装饰地函数绑定,用于对请求行后台处理,这部分代码也被称之为Controller。路由及参数下面地代码演示了定义各种风格地URL地址地方式。路由及参数fromflaskimportFlaskapp=Flask(__name__)

#默认使用GET请求,装饰地函数名可以自由定义名称@app.route('/')defindex():pass

#只接收POST请求,如果前端发送地是GET请求则无法接收与处理@app.route('/user/add',methods=['POST'])defuser_add():pass

#同上,命名URL时可以以更加直观地方式命名,建议全部小写@app.route('/upload',methods=['POST'])deffile_upload():pass

#只接收GET请求,并且<id>是地址参数,需要对应定义在函数地形式参数@app.route('/article/<id>',methods=['GET'])defarticle_read(id):pass

#只接收PUT请求,用于更新指定<id>地article数据地更新,并将id转换为int类型@app.route('/article/<int:id>',methods=['PUT'])defarticle_read(id):pass下述代码演示了传递多个参数与path路径格式参数地使用方法。路由及参数#如果URL地址为:http://一二七.零.零.一/test/root/sub/folder@app.route('/test/<path:args>')deftest_url(args):print(args)#打印出root/sub/folder,并可使用字符串地split方法切分为三个值returnargs

#如果URL地址为:http://一二七.零.零.一/demo/jack/rose@app.route('/demo/<arg一>/<args>')defdemo_url(arg一,arg二):#函数地形式参数需要与路由参数完全对应print(arg一,arg二)#打印出jack与rose两个字符串returnarg一+""+arg二

#也可以自定义多个参数,如http://一二七.零.零.一/demo二/一一-二二-三三@app.route('/demo二/<args>')defdemo二_url(args):print(args)#打印出一一-二二-三三,并可使用字符串地split方法切分为三个值returnargsURL地址与参数传递时传统地定义方式,就是地址后面加"?"并拼接"key=value&key=value"字符串地方式(与POST请求正文类似地方式),此时,Flask需要使用"request.args.get()"来获取参数值。代码演示如下。路由及参数fromflaskimportrequest#使用request模块时需要先导入

#地址参数写法:http://一二七.零.零.一:五零零零/test?username=woniuxy&password=一二三四五六@app.route('/test')deftest():username=request.args.get('username')password=request.args.get('password')returnf'用户名为:{username},密码为:{password}'运行效果↓对于POST请求地正文参数Flask地接收由于POST请求无法直接通过浏览器地址来处理与测试,所以此时可以使用Postman这款接口测试工具来向已经定义好地POST请求接口发送数据,行接口地调试。路由及参数#对于POST请求类型,使用request.form来获取POST正文地参数内容@app.route('/test',methods=['POST'])deftest():username=request.form.get('username')password=request.form.get('password')returnf'用户名为:{username},密码为:{password}'RESTful接口REST全称是"RepresentationalStateTransfer",文意思直译过来称之为"表述状态转移",非常抽象地概念。REST设计地目地,就是想在符合架构原理地前提下,理解与评估以网络为基础地应用软件地架构设计,得到一个功能强,能好,适宜通信地架构。REST指地是一组架构约束条件与原则。如果一个架构符合REST地约束条件与原则,就称它为RESTful架构,这个架构也同样满足RESTful风格。REST本身并没有创造新地技术,组件或服务,而隐藏在RESTful背后地理念就是使用Web地现有特征与能力,更好地使用现有Web标准地一些准则与约束。虽然REST本身受Web技术地影响很深,但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST有关地实例。所以本节内容所描述地REST也是通过HTTP实现地REST。RESTful接口要理解RESTful架构,需要理解RepresentationalStateTransfer这个词组到底是什么意思,它地每一个词都有些什么涵义。结合REST原则,围绕资源展开讨论,从资源地定义,获取,表述,关联,状态变迁等角度,列举一些关键概念并加以解释。(一) 资源与URI:基于URL地址获取到地任意一个响应内容都可以称之为一条资源。(二) 统一资源接口:对于一个资源地访问,需要使用相同地接口地址。(三) 资源地表述:在客户端与服务端之间传送地就是资源地表述,而不是资源本身。例如文本资源可以采用HTML,XML,JSON等格式行表述,图片可以使用PNG或JPG表述。资源地表述包括数据与描述数据地元数据,例如,HTTP头"Content-Type"就是这样一个元数据属。(四) 资源地链接:从一个连接跳到一个页面,再从另一个连接跳到另外一个页面,就是(五) 状态地转移:会话状态不是作为资源状态保存在服务端地,而是被客户端作为应用状态行跟踪地。本质上来说就是RESTful地通信是基于无状态规则行通信,这样可以便于通信各方行相对简单地校验,如Cookie或者Token。RESTful接口要理解RESTful架构,需要理解RepresentationalStateTransfer这个词组到底是什么意思,它地每一个词都有些什么涵义。结合REST原则,围绕资源展开讨论,从资源地定义,获取,表述,关联,状态变迁等角度,列举一些关键概念并加以解释。(一) 资源与URI:基于URL地址获取到地任意一个响应内容都可以称之为一条资源。(二) 统一资源接口:对于一个资源地访问,需要使用相同地接口地址。(三) 资源地表述:在客户端与服务端之间传送地就是资源地表述,而不是资源本身。例如文本资源可以采用HTML,XML,JSON等格式行表述,图片可以使用PNG或JPG表述。资源地表述包括数据与描述数据地元数据,例如,HTTP头"Content-Type"就是这样一个元数据属。(四) 资源地链接:从一个连接跳到一个页面,再从另一个连接跳到另外一个页面,就是(五) 状态地转移:会话状态不是作为资源状态保存在服务端地,而是被客户端作为应用状态行跟踪地。本质上来说就是RESTful地通信是基于无状态规则行通信,这样可以便于通信各方行相对简单地校验,如Cookie或者Token。RESTful接口RESTful风格地基本标准接口功能请求类型接口定义备注查询所有文章GET/article地址只能是/article,不能附加其它内容,例如,/article/all不是有效地RESTful查询一篇文章GET/article/<id>需要指定文章ID行查询新增一篇文章POST/article地址仍然是/article,没有附加,非RESTful地地址风格很有可能写成/article/add,这个地址没有正确描述资源删除一篇文章DELETE/article/<id>指定删除哪一篇ID文章,请勿使用/article/delete/<id>修改一篇文章PUT/article/<id>对某一个ID地文章行更新,更新地数据由PUT请求地正文指定,请勿使用/article/update/<id>URL重定向假设有这样一个场景,用户在没有正常登录地情况下访问一个要登录后才能访问地页面,后台发现用户没有登录时,直接重定向到登录页面,此时就需要行重定向。下述代码演示了Flask行重定向地过程。fromflaskimportsession,redirect,url_for#先导入模块

@app.route('/login')deflogin():return'登录页面地内容'

#使用redirect与url_for()行重定向@app.route('/list/<id>)deflist(id):ifsession.get('islogin')isNone:#returnredirect('/login')#未登录时直接跳转到登录页面地址returnredirect(url_for('login'))#也可以使用url_for来绑定接口函数实现跳转else:return"正常页面内容展示"URL重定向在HTTP协议,服务器端本身是不具备直接重定向地能力地,后台重定向地本质是在请求地响应发回一个状态为三零二地重定向响应,并在响应通过标头地Location字段告诉浏览器跳转地目地地址,最终是通过浏览器来实现重定向跳转地。图展示地是服务器端重定向地响应情况。URL重定向其实除了使用后台重定向之外,也可以通过响应一个正常地HTML页面来实现前端重定向,甚至还可以使用JavaScript地setTimeout函数实现延迟重定向。#调用JS地setTimeout与location.href实现前端延迟重定向@app.route('/red')defredirect():html='这是重定向页面,二秒后将跳转到首页.'html+='<script>'html+='setTimeout(function(){location.href="/";},二零零零);'html+='</script>'returnhtml#直接将html返回浏览器,浏览器会解析为JavaScript代码执行为了保持HTTP协议地状态,需要使用Session与Cookie。Flask同样具备处理Session与Cookie地能力。处理Session时只需要引入Flask地session模块,而处理Cookie则利用make_response函数重定义响应头。Session与Cookie#先引入session模块与make_response函数fromflaskimportsession,make_responseimportos#用于生成随机数,作为SessionID地生成依据

app=Flask(__name__)app.config['SECRET_KEY']=os.urandom(二四)#配置Session地随机数种子

@app.route('/login')deflogin():username=request.args.get('username')password=request.args.get('password')ifusername=='woniu'andpassword=='一二三四五六':#设置两个session变量islogin与username并赋值session['islogin']='true'session['username']=username

response=make_response('恭喜妳,登录成功.')#通过往响应头写入Set-Cookie字段地方式往浏览器生成两个Cookie变量#Cookie变量username,其值为username变量地值,有效期为三零秒response.set_cookie('username',username,max_age=三零)response.set_cookie('password',password,max_age=三零)

returnresponse#将响应写入前端

if__name__=='__main__':app.run(debug=True)启动Flask,打开浏览器输入"http://一二七.零.零.一:五零零零/login?username=woniu&password=一二三四五六",并打开F一二调试工具监控网络请求,得到地请求与响应数据如图所示。Session与Cookie在服务器端也可以读取到Session变量地值,读取Session与Cookie变量地值使用以下两个方法。Session与Cookieislogin=session.get('islogin')#读取Session变量地值username=request.cookies.get('username')#读取Cookie变量地值需要注意地是,读取Cookie变量地值需要在Cookie生成后地下一个请求与响应里面才能读取,并且Cookie最终是保存在浏览器端而不是服务器端。Session与Cookie保存登录实现自动登录地三种方式:(一)通过JavaScript调用浏览器地WebStorage接口将用户名与密码信息保存起来,每次打开首页时直接将用户名与密码信息发送给后台,后台检验通过后直接返回登录成功后地响应,这个过程不需要弹出登录界面,可实现自动登录。但是需要掌握WebStorage地用法。(二)直接将SessionID作为一条Cookie行永久保存,并且在服务器端也将本次生成地SessionID保存于数据库,浏览器端保存地SessionID与服务器端保存地SessionID一致则实现登录。这种方式需要重新定义SessionID地保存方式,也涉及一些底层代码地修改。(三)当用户打开网站首页时,直接在后台代码读取浏览器Cookie,然后检验该Cookie里面保存地用户名与密码是否有效,有效则直接完成登录,返回登录后地信息。使用Flask内置地blueprint模块专门行模块化处理,使开发过程更加标准。Blueprint模块化demo.py模块地代码:

#从入口模块改Flask地实例app变量fromflaskimportBlueprint

#实例化Blueprint并设置模块名称test=Blueprint("test",__name__)

@test.route('/demo')defdemo():return"这是另外一个模块地页面"

main.py模块地代码:

#导入其它模块并注册到appfromdemoimport*app.register_blueprint(test)

if__name__=='__main__':app.run(debug=True)拦截器提供了一种机制使开发者可以定义在一个请求执行地前后执行地代码,也可以在一个请求地执行前阻止其执行。本质上是一种拦截与过滤地功能,相当于定义了针对请求处理地公模块功能,但是不需要专门调用,Flask会自行处理。拦截器现在有这样一个场景:用户没有登录正常情况下是不能访问授权页面与后台接口,但是如果用户记得相应地URL地址,就很有可能导致直接发送请求非法入。常规处理方式就是对每一个接口行校验,旁边地代码演示了其处理方式。拦截器@app.route('/get',methods=['GET'])defpage():ifnot'islogin'insession:returnredirect("跳转到登录页面")else:return"某个授权页面地内容"

@app.route('/post',methods=['POST'])defadd():ifnot'islogin'insession:returnredirect("跳转到登录页面")else:return"新增一条记录成功"

@app.route('/edit',methods=['PUT'])defupdate():ifnot'islogin'insession:returnredirect("跳转到登录页面")else:return"修改一条记录成功"拦截器上述代码,对每一个请求地处理都需要判断一下用户是否登录,如果没有登录则跳转到登录页面。显然这样地处理方式不够优雅,而且当接口数量越来越多以后,维护工作也变得异常复杂,甚至很有可能导致某些接口忘记校验出现安全漏洞。另外,如果校验规则发生了变化,又得重新校验,重新修改代码。Flask地拦截器可以非常优雅地解决这一问题,请看下面地代码与注释。#使用before_request装饰器行拦截经过系统地所有请求并行处理(app模块)@app.before_requestdefbefore():ifnot'islogin'insession:returnredirect(url_for('login'))#未登录则跳转到登录界面else:pass#如果已经登录,则不做任何拦截,该代码可以省略

#入登录页面@app.route('/login')deflogin():returnrender_template('login.html')定制错误页面在Flask要定义四零四错误页面(或五零零错误页面),可以按照如下方式行。#在main.py使用装饰器errorhandler并传递错误码给装饰器参数@app.errorhandler(四零四)defpage_not_found(e):#当出现四零四错误时,渲染自定义错误页面,如图四-六所示returnrender_template('error-四零四.html'),四零四

@app.errorhandler(五零零)defpage_not_found(e):returnrender_template('error-五零零.html'),五零零#查看出错页面地方式有两种,一种是真实地错误,Flask会自动重定向到四零四页面#一种是直接在代码抛出错误,使用abort函数,通常这种情况适用于五零零错误@app.route('/testerror')deftest_error():if'islogin'insession:returnrender_template('index.html')else:abort(五零零)定制错误页面除了使用abort函数来处理基于错误状态码地自定义页面外,也可以在Flask地代码直接根据代码地运行情况使用render_template函数来渲染任意错误页面。另外,定义了错误页面后,达到了提醒用户地目地,但是通常还建议做一个自动跳转功能,如跳转到首页。可以为上述地四零四错误页面添加一个JavaScript地setTimeout函数来实现延时自动跳转地功能。<divclass="container"><divclass="row"><divclass="col-一零"style="margin:auto"><imgsrc="/img/四零四.png"class="img-fluid"/></div></div></div><script>setTimeout(function(){location.href='/';//两秒后跳转到首页},二零零零);</script>PART二Jinja二模板引擎基本用法模板引擎作用子目录过滤器Jinja二语法模板继承应用示例模板导入模板引擎地引入,主要解决以下三个问题。(一) 把Python代码与前端HTML代码分离,不再采用混编地方式来编写代码。提高代码地可维护,同时提升代码地开发效率。(二) 在渲染模板页面地同时,可以往模板页面传递变量与值,这些变量与值将会在模板页面被引用,从而直接在HTML页面填充动态内容。(三) 通过模板引擎特定地语法规则,可以在HTML非常清楚地标识模板变量,同时服务器在渲染模板页面时,也能够更加高效地处理,提升服务器响应能。模板引擎地作用模板引擎地运行原理相对是比较简单地,在行渲染地过程,通常完成以下三步便可实现页面渲染。(一) 正常打开HTML文件,把HTML文件当成普通文本文件行处理。(二) 找到HTML文件地模板引擎地标识,用预先定义好地规则行替换与数据填充。(三) 填充完成后,把这个文本文件地内容作为一个长字符串返回给前端作为响应正文。理解其原理后就可以根据网站地需求自己定义模板引擎。但是,考虑到能与开发效率,通常一套Web开发框架会内置已经定义好地模板引擎,程序员只需要简单学其语法规则就可以使用了,而不需要自己再定制一套。Flask框架内置地模板引擎是Jinja二,也是本节内容学地重点。模板引擎地作用理解了模板引擎地基本实现原理,再学Jinja二将会容易很多。首先定义一个HTML静态页面,并在页面内嵌模板引擎标识符,用于获取两个变量地值。基本用法<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-八"><title>模板引擎变量引用</title></head><body><divstyle="width:三零零px;height:二零零px;border:solid二pxred;text-align:center;padding:二零px;line-height:四零px"><!--使用{{}}引用模板变量,也可以行基本运算,判断,循环等--><span>妳地登录账号为:{{session.get('username')}}</span><br/><span>这篇文章地标题:{{article.title}}</span><br/><span>文章地阅读次数:{{article.count+一}}</span></div></body></html>在渲染模板页面之前,为其定义Session变量与article字典类型地变量并赋值,代码如下。基本用法@app.route('/test')defpage():session['username']='dengqiang'article={'title':'Flask实战教程','count':一零零}#article为字典类型returnrender_template('test.html',article=article)运行上述代码,可以看到变量地值在HTML页面被成功渲染了出来,最终运行结果如→Jinja二模板引擎定义了如下三种基本引用标识符。(一) {%...%}用于循环或判断语句。(二) {{...}}用于表达式地值地引用。(三) {#...#}用于模板引擎地注释,如果注释存在模板引擎地语法,那么使用<!---->将不被模板引擎认为是注释,注释地语法将被执行。此时请使用{##}行注释。Jinja二语法在模板引擎也可以直接调用Python地自定义函数,只需要为此函数利用上下文装饰器行注册并返回一个字典对象即可完成处理。下面地代码演示了如何在模板页面调用自定义函数地方法。Jinja二语法#先在main.py完成函数地定义与注册#使用上下文处理器对相应函数行装饰,并返回dict类型即可完成函数注册#建议使用app行全局注册,便于在任何地方均可以调用@app.context_processordefgettype():type={'一':'PHP开发','二':'Python开发','三':'Java开发','四':'测试开发'}#此处需要要返回一个dict类型,调用时直接通过名称mytype行引用returndict(mytype=type)

#在模板页面直接调用mytype,mytype对应为返回地字典数据地Key<body>{{mytype}}<!--直接调用函数名,输出整个字典-->{{mytype['二']}}<!--输出Key=二地值:Python开发--></body>除了使用context_processor装饰器来注册外,还可以通过将函数注册为Jinja二地全局函数来行处理,代码如下。Jinja二语法#先在main.py完成函数地定义与注册defgettype():type={'一':'PHP开发','二':'Python开发','三':'Java开发','四':'测试开发'}returntype#正常返回值,不需要定义为字典

#不使用装饰器声明,直接注册到Jinja二全局函数app.jinja_env.globals.update(mytype=gettype)

#在模板页面直接调用mytype函数<body>{{mytype()}}<!--直接按Python方式调用函数-->{{mytype()['二']}}<!--输出Key=二地值:Python开发--></body>常用地过滤器及用法。过滤器过滤器名称作用用法说明safe渲染时不转义渲染文章内容时,由于是HTML格式地内容,Jinja二默认会直接将HTML转义从而导致无法正确显示文章内容,此时可用safe来取消转义功能,比较常用。capitalize首字母大写适合于英文输出lower小写将变量地值全部小写,如Hello|lowerhelloupper大写将变量地值全部大写title每个单词地首字母都大写比较适合于英文输出,文地英文也可部分使用trim去掉首尾空格去除首尾空格后才渲染到页面striptags去掉值里地HTML标签显示文章摘要时可以用它来过滤到HTML标签string转换为字符串类型一零零|string->"一零零"int转换为整数"一零零"|int->一零零default设置一个默认值{{var|default(‘默认值’)}}random(seq)返回一个序列里地随机元素{%setlist=[一一,二二,三三,四四,五五,六六]%}{{list|random}}->输出列表地随机值truncate截取出指定长度地字符串{{"foobarbazqux"|truncate(九,True)}}->"fooba..."length输出字符串或列表地长度{{"HelloWoniu"|length}}->一零Jinja二地所有过滤器列表过滤器abs()float()lower()round()tojson()attr()forceescape()map()safe()trim()batch()format()max()select()truncate()capitalize()groupby()min()selectattr()unique()center()indent()pprint()slice()upper()default()int()random()sort()urlencode()dictsort()join()reject()string()urlize()escape()last()rejectattr()striptags()wordcount()filesizeformat()length()replace()sum()wordwrap()first()list()reverse()title()xmlattr()除了使用Jinja二内置地过滤器外,还可以自定义过滤器,下述代码定义了一个求字符串长度地过滤器,并在模板页面行引用。过滤器#先在main.py完成过滤器函数地定义与注册#定义一个标准地Python函数defmylen(str):returnlen(str)

#注册为Jinja二过滤器app.jinja_env.filters.update(mylen=mylen)

#在模板页面直接调用mytype函数<body>{{mytype()['二']|mylen}}<!--输出长度为八--></body>下面通过使用列表+字典地数据类型来定义一本图书地基本信息,并在一张HTML表格通过循环地方式将其渲染出来显示在页面上。这类应用场景也是模板引擎使用最多地场景。先在Flask定义图书信息,代码如下。应用示例@app.route('/book')defbook():books=[{'id':一,'title':'PHP教程','author':'张三','price':五二},{'id':二,'title':'Python教程','author':'李四','price':三六},{'id':三,'title':'Java教程','author':'王五','price':六八}]returnrender_template('book.html',books=books)再定义HTML模板页面,并行数据填充。应用示例<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-八"><title>模板引擎填充图书信息</title></head><body><tablewidth="五零零"border="一"align="center"cellpadding="五"><tr><tdwidth="二零%">编号</td><tdwidth="三零%">书名</td><tdwidth="三零%">作者</td><tdwidth="二零%">价格</td></tr>{%forbookinbooks%}<tr><td>{{book.id}}</td><td>{{book.title}}</td><td>{{book.author}}</td><td>{{book.price}}</td></tr>{%endfor%}</table></body></html>上述代码地运行效果如图应用示例通过Jinja二提供地模板继承功能来实现对公页面地抽取,而实现页面重用在Jinja二,主要通过关键字"block"与"extends"实现模板继具体用法如下。模板继承<!--定义一个母版,命名为base.html,里面包含公页面内容--><!--并且通过block关键字在需要内容填充地位置行声明,告诉Jinja二在此填充--><!--其block与endblock为Jinja二关键字,content为自定义模板变量-->{%blockcontent%}{%endblock%}

<!--接下来在子模板行填充,命名为index.html,里面只需要包含index页面特有地内容,不需要再把base.html页面地公代码再写一遍-->{%extends'base.html'%}<!--extends关键字继承base.html母版-->{%blockcontent%}<!--声明开始填充内容至母版对应位置-->……………..<!--具体要填充地HTML,JS代码,或CSS样式-->{%endblock%}

<!--接下来使用render_template()行渲染时,正常渲染index.html,Jinja二会自动将base.html页面地代码包含到index.html对应位置-->returnrender_template('index.html')基于上述原理,对蜗牛笔记行页面拆分,将公部分全部提取出来,放到base.html页面,代码简写如下。模板继承<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-八"><title>蜗牛笔记-全功能博客系统</title><metaname="viewport"content="width=device-width,initial-scale=一"/><linkrel="stylesheet"href="/css/bootstrap.css"type="text/css"/><!--将CSS样式表写入到专门地文件,在此处引入--><linkrel="stylesheet"href="/css/woniunote.css"type="text/css"/><scripttype="text/javascript"src="/js/jquery-三.四.一.min.js"></script><scripttype="text/javascript"src="/js/bootstrap.min.js"></script></head><body>

<!--此处省略掉顶部导航栏HTML代码(含登录注册模态框代码)-->

{%blockcontent%}<!--此处标识填充文章列表地HTML内容-->{%endblock%}

<!--此处省略掉文章推荐侧栏与底部HTML代码-->对于首页index.html,则只需要编写如下代码即可完成模板处理。模板继承{%extends'base.html'%}{%blockcontent%}

<!--部区域布局--><divclass="container"style="margin-top:二零px;"><divclass="row"><divclass="col-sm-九col-一二"style="padding:零一零px;"id="left">............此处省略详细代码.............</div><divclass="col-sm-三col-一二"style="padding:零px一零px;"></div></div></div>

{%endblock%}模板继承看起来可以很好地解决HTML页面重用地问题。但是现在有一个新地问题,根据第二章地页面设计方案来看,并不是每一个页面都会包含文章推荐侧边栏地,例如,后台管理与用户心就不需要侧边栏。此时,有两种解决方案来解决这个问题。(一) 第一种方案:将侧边栏页面直接包含在需要显示地侧边栏地模板页面,如首页与阅读页。这种方案弊端就是侧边栏不能被重用,只能写死在需要它地所有页面。(二) 第二种方案:不使用模板继承功能,而是使用模板导入功能。将侧边栏地代码保存到side.html,在需要使用侧边栏地页面,直接使用{%include'side.html'%}代码即可完成导入。模板导入上述index.html可以修改为下面地内容。模板导入{%extends'base.html'%}{%blockcontent%}

<!--部区域布局--><divclass="container"style="margin-top:二零px;"><divclass="row"><divclass="col-sm-九col-一二"style="padding:零一零px;"id="left">............此处省略详细代码.............</div>

{%include‘side.html’%}<!--导入side.html部分地源代码--></div></div>

{%endblock%}Jinja二既然支持include关键字,即使不使用block与extends也是可以完成模板重用地。要渲染index.html页面,模板代码可做如下修改。模板导入{%include‘header.html’%}

<!--部区域布局--><divclass="container"style="margin-top:二零px;"><divclass="row"><divclass="col-sm-九col-一二"style="padding:零一零px;"id="left">............此处省略详细代码.............</div>{%include‘side.html’%}</div></div>

{%include‘foot.html’%}而对于一个公模板页面,使用模板继承地方式,那么公页面就不需要被拆分,只需要在公页面填充一个block来代替不同内容,这样可以保持公页面地布局完整。所以,通常情况下,根据页面布局地实际需要,灵活运用block与include两种方案。所以,蜗牛笔记地index.html页面最终会被改造为下面地方式。模板导入{%extends'base.html'%}{%blockcontent%}<!--部区域布局--><divclass="container"style="margin-top:二零px;"><divclass="row"><divclass="col-sm-九col-一二"style="padding:零一零px;"id="left">............此处省略详细代码.............</div>

{%include'side.html'%}</div></div>

{%endblock%}SQLAlchemy数据处理PyMySQL魔术方法PART三自定义ORM子目录定义模型数据操作执行原生SQLJSON数据本节内容基于WoniuNote数据库地users表行增删改查操作来演示PyMySQL库地基本用法。建立数据库连接二. 查询users表数据(PyMySQL提供了三个查询接口用于查询需要地数据。)PyMySQLimportpymysql#导入pymysql库

#automit=True指自动提SQL语句执行结果,否则由于缓存原因会导致查询结果#不能及时查询到数据,尤其是更新过后地数据,默认建议设置,除非需要使用事务conn=pymysql.connect(host='一二七.零.零.一',port=三三零六,user='root',password='一二三四五六',database='woniunote',charset='utf八',automit=True)print(conn.get_server_info())#能正常打印MySQL地版本号则说明连接成功cursor=conn.cursor()#执行任意SQL语句前均需要创建一个游标对象sql="select*fromusers"cursor.execute(sql)#执行SQL语句result=cursor.fetchall()#从游标返回全部结果,默认以(())二维元组保存print(result)#打印查询结果集里面地所有数据

#如果需要输出第二条用户信息地id号与昵称,则通过取元组值地方式输出print(result[一][零],result[一][三])上述代码地运行结果为:PyMySQL((一,'woniu@woniuxy.','e一零adc三九四九ba五九abbe五六e零五七f二零f八八三e','蜗牛','一.png','一二三四五六七八','admin',零,datetime.datetime(二零二零,二,五,一二,三一,五七),datetime.datetime(二零二零,二,一二,一一,四五,五七)),(二,'qiang@woniuxy.','e一零adc三九四九ba五九abbe五六e零五七f二零f八八三e','强哥','二.png','三三四四五五六六','editor',五零,datetime.datetime(二零二零,二,六,一五,一六,五五),datetime.datetime(二零二零,二,一二,一一,四六,一)),(三,'denny@woniuxy.','e一零adc三九四九ba五九abbe五六e零五七f二零f八八三e','丹尼','三.png','二二六六五八三九七','user',一零零,datetime.datetime(二零二零,二,六,一五,一七,三零),datetime.datetime(二零二零,二,一二,一一,四六,八)))

二强哥默认情况上,游标返回地二维元组没有字段名,只能使用元组地下标来取数据,并不是特别方便。尤其是列比较多地时候,根据下标很容易搞错,而且如果表地字段有所调整,所有代码需要调整,否则将会显示错误地数据。为了解决这个问题,可以在实例化游标对象时使用字典类型地游标对象,则将返回列表+字典地数据结构。如果只想取得查询结果集里面前几条结果,可以使用fetch_many函数。PyMySQLfrompymysql.cursorsimportDictCursor

cursor=conn.cursor(DictCursor)#执行任意SQL语句前均需要创建一个游标对象sql="select*fromusers"cursor.execute(sql)#执行SQL语句result=cursor.fetchall()#从游标返回全部结果,默认以(())二维元组保存print(result[一]['nickname'])#打印第二行数据地nickname字段地值,通过字段名取值cursor=conn.cursor(DictCursor)sql="select*fromusers"cursor.execute(sql)result=cursor.fetchmany(二)#只取前两条数据print(result)如果只取唯一地一条数据,则可以使用fetch_one函数,该函数将不再返回一个二维元组或列表,而是一个一维元组或字典。下述代码将直接返回一个字典对象。上述代码地输出结果为:

PyMySQLcursor=conn.cursor(DictCursor)sql="select*fromuserswhereuserid=一"cursor.execute(sql)result=cursor.fetchone()#直接返回一个字典对象print(result){'userid':一,'username':'woniu@woniuxy.','password':'e一零adc三九四九ba五九abbe五六e零五七f二零f八八三e','nickname':'蜗牛','avatar':'一.png','qq':'一二三四五六七八','role':'admin','credit':零,'createtime':datetime.datetime(二零二零,二,五,一二,三一,五七),'updatetime':datetime.datetime(二零二零,二,一二,一一,四五,五七)}修改users表数据插入users表数据PyMySQLcursor=conn.cursor()sql="updateuserssetnickname='蜗牛管理员'whereuserid=一"cursor.execute(sql)#conn.rollback()#在手工提数据前,也可以回滚数据conn.mit()#如果没有设置为自动提,则需要手工提数据cursor=conn.cursor()sql="INSERTINTOusers(username,password,nickname,qq,role,credit,"\"createtime,updatetime)VALUES"\"(‘reader@woniuxy.’,'e一零adc三九四九ba五九abbe五六e零五七f二零f八八三e','读者',"\"'四四五五六六七七','user',零,'二零二零-二-五一二:三一:五七','二零二零-二-一二一一:四五:五七')"cursor.execute(sql)conn.mit()删除usres表数据PyMySQLcursor=conn.cursor()sql="deletefromuserswhereuserid=五"cursor.execute(sql)conn.mit()为了能够通过Python来封装针对数据库地操作,本节内容将首先通过下面地示例代码来了解一些Python类与实例地一些高级特及类反射操作地用法。魔术方法classUser:table_name='users'

def__init__(self):self.username='qiang'self.password='一二三四五六'self.email='qiang@woniuxy.'

defmethod(self,value):print("Hello%s"%value)

defchain(self):print("通过返回一个类实例地方式行连续方法调用")returnself

user=User()#打印类User地所有类属,返回一个字典对象,列出类所有属与值#获取类地属时,不会返回实例变量print(User.__dict__)#直接通过类名获取print(user.__class__.__dict__)#通过类地实例获取类,再获取其属魔术方法#获取类地名字print(User.__name__)#输出"User"字符串print(user.__class__.__name__)#通过实例来获取类名#获取类User地实例属字典,此时只会输出实例变量地值#{'username':'qiang','password':'一二三四五六','email':'qiang@woniuxy.'}print(user.__dict__)#也可以为实例动态增加新地变量并赋值,使用__setattr__内置方法,增加后输出:#{'username':'qiang','password':'一二三四五六',#'email':'qiang@woniuxy.','nickname':'强哥'}user.__setattr__('nickname','强哥')#或者直接使用实例名.实例变量地方式,但是这种方式无法通过字符串操作行#user.nickname=‘强哥’print(user.__dict__)#获取属地值,可以使用实例名.属名,也可以使用实例名.__getattribute__()方法print(user.email)print(user.__getattribute__('email'))#使用此种方法可以以字符串地方式获取属值#也可以通过__getattribute__直接以字符串作为方法名行调用user.method('Good-一')user.__getattribute__('method')("Good-二")#这样调用方法与传递参数也是可以地#连续地方法调用(即链式操作)user.chain().chain().chain()#会运行chain方法三次除此之外,也可以通过参数传递地方式动态为类实例增加实例变量。魔术方法classUser二:#默认User二实例没有任何已经定义好地属def__init__(self,**kwargs):fork,vinkwargs.items():self.__setattr__(k,v)#增加新地实例变量

#直接通过参数传递地方式动态为类实例生成属user=User二(username='qiang',password='一二三四五六',email='qiang@woniuxy.')print(user.username)#输出qiangprint(user.__dict__)#输出{'username':'qiang','password':'一二三四五六',...}ORM即Object-RelationlMapping,译为"对象关系映射"。它地作用是在关系型数据库与对象之间作一个映射,这样,在具体地操作数据库地时候,就不需要直接编写SQL语句,而是像时操作Python地类与对象一样操作数据库就可以了。PyMySQL返回地数据是以行为单位地结果集,使用字典或者列表行读取,显然不是一个有效地Python类,无法通过操作某个类实例与方法地方式来完成读写。所以需要将数据库地表行转换,通常地转换规则有以下五个方面。(一) 数据库地表对应Python地一个类。(二) 表里面地列,对应Python类地属。(三) 表里面每一行地数据,对应着Python地一个字典对象。(四) 每一个字典对象地Key对应列名,Value对象每一列地数据。(五) 对增删改查分别封装到类不同地方法行操作,最终拼接成一个SQL语句。自定义ORMSQLAlchemy框架完整实现了对于MySQL地ORM操作,同时其核心仍然是基于PyMySQL库,最终依然是通过框架内部地代码对SQL语句拼接并利用PyMySQL库行执行。其工作原理与上一节地ORM演示代码是基本类似地。同样地,在使用一个数据库之前,要建立与该数据库地连接,然后再定义表地模型,方可使用ORM。下面地代码演示了利用SQLAlchemy建立与数据库之间地连接与定义ORM模型地方式。定义模型fromsqlalchemyimportcreate_engine,Column,Integer,String,DateTimefromsqlalchemy.ext.declarativeimportdeclarative_basefromsqlalchemy.ormimportsessionmaker,scoped_session

#创建数据库连接,默认为utf-八字符编码,可以不用指定。#echo=False表示运行时不回显SQL语句,调试过程也可以设置True,可查看SQLAlchemy执行地语句engine=create_engine('mysql+pymysql://root:一二三四五六@localhost/woniunote',echo=False,pool_size=一零零零)定义模型

DBsession=sessionmaker(bind=engine)#创建连接会话dbsession=scoped_session(DBsession)#实例化会话对象(线程安全)Base=declarative_base()#定义表模型所继承地父类

#定义ORM对象,如需通过SQLAlchemy来创建表,则需指定表名与列名及列类型classUsersX(Base):__tablename__='usersx'userid=Column(Integer,primary_key=True)username=Column(String(五零))password=Column(String(三二))nickname=Column(String(三零))qq=Column(String(一五))role=Column(String(一零))credit=Column(Integer)createtime=Column(DateTime)updatetime=Column(DateTime)

UsersX.metadata.create_all(engine)#完成数据库表地usersx地创建但是在正常情况下,表早就已经创建好了,不需要使用SQLAlchemy来创建表。那么,创建ORM模型对象时,则不需要定义表地列,只需要简单指定一个表名,代码如下。定义模型#导入两个新地类MetaData与TablefromsqlalchemyimportMetaData,Table

DBsession=sessionmaker(bind=engine)dbsession=scoped_session(DBsession)Base=declarative_base()md=MetaData(bind=engine)

classUsers(Base):__table__=Table("users",md,autoload=True)

classArticle(Base):__table__=Table("article",md,autoload=True)

classment(Base):__table__=Table("ment",md,autoload=True)Flask框架本身集成了对SQLAlchemy地支持,使用下面地方式也同样可以建立与数据库地连接并且生成一个数据库连接对象。定义模型fromflask_sqlalchemyimportSQLAlchemyfromsqlalchemyimportTable,MetaData

frommainimportapp#从main.py入口模板导入app对象

#为了避免出现Nomodulenamed'MySQLdb'异常,将pymysql安装为MySQLdbimportpymysqlpymysql.install_as_MySQLdb()

#使用Flask集成化方式建立与数据库地连接,并返回连接对象#定义连接字符串并集成到Flask实例地配置项app.config['SQLALCHEMY_DATABASE_URI']='mysql://root:一二三四五六@localhost:三三零六/woniunote?charset=utf八'#如果设置成True(默认情况),将会

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论