接着之前的python进行学习,决定还是从更加实用点的项目相关的知识去学习,这样也可以去更好的实践当中去锻炼自己的能力,所以就选择了flask这个python中的轻量模块去学习。
一、python中虚拟环境的使用 python是一个非常常见的编程语言,在python使用过程中,我们总是需要使用到很多需要自己安装下载的库,所以一般一个python编程人员的电脑上都有着许许多多的python库,而在我们去开发一些网站,程序相关的时候,我们并不需要去使用到我们所有的库,并且可能需要去使用某些特定版本的库,可这又应该怎么办?这个时候,虚拟环境也就因此诞生了。
在使用Python语言时,通过pip(pip3)来安装第三方包,但是由于pip的特性,系统中只能安装每个包的一个版本。但是在实际项目开发中,不同项目可能需要第三方包的不同版本,迫使我们需要根据实际需求不断进行更新或卸载相应的包,而如果我们直接使用本地的Python环境,会导致整体的开发环境相当混乱而不易管理,这时候我们就需要开辟一个独立干净的空间进行开发和部署,虚拟环境就孕育而生。
下面就直接去记录一下一些虚拟环境的常见使用方法,最流行的就是Virtualenv这个虚拟环境的配置工具
安装:
1 pip install virtualenv virtualenvwrapper-win //windows环境下
查看虚拟环境有哪些
创建一个新的虚拟环境
去使用一个虚拟环境
注意:虚拟环境文件默认在C盘中用户下中的env中
二、数据库中的数据迁移操作 数据库首先先初始化连接设置
1 2 3 db_uri = 'mysql+pymysql://root:password@localhost:3306/bookdb' app.config['SQLALCHEMY_DATABASE_URI' ] = db_uri app.config['SQLALCHEMY_TRACK_MODIFICATIONS' ] = False
其次就是模块类的创建,也就相当于数据库中的列
1 2 3 4 5 6 7 class User (db.Model): __tablename__ = 'tb_user' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(30 ), unique=True , index=True ) age = db.Column(db.Integer, default=1 ) sex = db.Column(db.Boolean, default=True ) salary = db.Column(db.Float, default=10000 , nullable=False )
为什么要做数据迁移呢?在我的理解中这就相当于一个空白数据库的初始化,将创建的类中的列添加到数据库之中,其实就是相当于我们去创建各个字段的过程。
下面就是用到的命令
1 2 3 4 flask db init //创建迁移文件夹migrates,只调用一次 flask db migrate //生成迁移文件 flask db upgrade //执行迁移文件中的升级 flask db downgrade //执行迁移文件中的降级
三、flask框架中的一些基本操作以及示例 1、最基础的单文件框架 文件结构:
static: 静态文件文件夹,用于存放一些像是css,js,image,font等的文件。
templates:用于存放html网站文件
app.py:(主项目文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from flask import Flaskapp = Flask(__name__) @app.route('/' ) def hello_world (): return 'Hello World!' @app.route('/index/' ) def index (): return 'hello index!' if __name__ == '__main__' : app.run()
app运行的时候一些可选的参数:
参数
说明
默认值
debug
代码更新是否自动重启
False
theaded
是否开启多线程
False
port
指定端口
5000
host
指定主机(设置0.0.0.0可以通过本地IP访问)
127.0.0.1
一般项目开发时都会开启debug模式
2、简单的模板渲染
示例:
app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from flask import Flask, render_templateapp = Flask(__name__) @app.route('/' ) def hello_world (): return 'hello world' @app.route('/index/' ) def index (): return render_template('index.html' , name="法外狂徒张三" ) if __name__ == '__main__' : app.run(debug=True )
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <link rel ="stylesheet" href ="../static/index.css" > </head > <body > <h2 > index</h2 > <hr > <h3 > name: {{ name }}</h3 > </body > </html >
可以利用render_template去将后端的一些数据返回到前端,然后再使用去使用传递过来的参数
3、对文件结构进行进行拆分 文件结构:
将除了主文件的其他文件都移入一个单独创建的App文件夹当中。
init.py:初始化的文件,在这里我们将app的创建函数放入其中,待需要使用时导入即可
models.py:模板文件,后边主要用于数据库方面的操作
views.py:视图函数的存放,也就是存放各种路由的地方
为了方便视图函数的使用,使用蓝图去绑定
1 app.register_blueprint(blueprint=blue)
将app绑定在blue上,这样之后路由就可以这样去使用了
1 2 @blue.route('/' ) @blue.route('/home/' )
4、简易登录界面的实现 ①cookie的一些设置 1 2 3 response = redirect('/home/' ) response.set_cookie('user' , username, max_age=3600 *24 *7 )
可以直接通过set__cookie去设置一个json格式的cookie
②session的一些设置 1 app.config['SECRET_KEY' ] = 'admin'
将app的配置文件中的SECRET_KEY修改为不空的值,并且将这个作为密匙去加密
1 2 3 session['user' ] = username session.permanent = True
③实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import datetimefrom flask import Blueprint, redirect, render_template, request, sessionfrom .models import *blue = Blueprint('user' , __name__) @blue.route('/' ) @blue.route('/home/' ) def index (): username = session.get('user' ) return 'home.html' , username @blue.route('/login/' , methods=['GET' , 'POST' ] ) def login (): if request.method == 'GET' : return render_template('login.html' ) elif request.method == 'POST' : pass username = request.form.get('username' ) password = request.form.get('password' ) if username == 'admin' and password == '123456' : response = redirect('/home/' ) session['user' ] = username session.permanent = True return response else : return '用户名或密码错误!' @blue.route('/logout/' ) def logout (): response = redirect('/home/' ) session.pop('user' ) return response
在自己设计一个前段登入即可
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 登录</title > </head > <body > <h2 > 登录</h2 > <hr > <form action ="" method ="post" > <p > 用户名:<input type ="text" name ="username" > </p > <p > 密码:<input type ="password" name ="password" > </p > <p > <button > 提交</button > </p > </form > </body > </html >
5、路由参数
string 接收如何没有斜杠(‘/‘)的字符串(默认)
int 接收整型
float 接收浮点型
path 接收路径,可接收斜线(‘/‘)
uuid 只接受uuid字符串,唯一码,一种生成规律(uuid是一段特定的字符串,基本无法伪造)
any 可以同时指定多种路径,进行限定
1 2 3 4 @blue.route('/any/<any(apple, orange, banana):fruit>/' ) def get_any (fruit ) print (type (fruit)) return str (fruit)
any表示限定只能在给定的之中选择
6、路由请求方法的指定 1 @blue.route('/login/' , methods=['GET' , 'POST' ] )
设置该路由可以由GET和POST这两种请求方法区访问
一些最常见的请求方式
GET
POST
PUT
DELETE
四、flask中四个全局变量 **
变量
上下文
描述
current_app
应用上下文
相当与在主程序中激活的实例化app(app=Flask(__name__)
)
g
应用上下文
一次性函数,处理请求的临时变量。只在一个请求中被应用,下个请求开始时会自动重置
request
请求上下文
请求对象。存放了客户端发来的HTTP信息
session
请求上下文
记录用户和服务器之间的会话的。在服务器端记录需要记住的信息。(和cookie对应,cookies是记录在客户端的)
request中的一些用法:
url 完整请求地址
base_url 去掉GET参数的URL
host_url 只有主机和端口号的URL
path 路由中的路径
method 请求方式
remote_addr 请求的客户端地址
args GET请求参数
form POST请求参数
files 文件上传
headers 请求头
cookies 请求头中的cookie
五、钩子函数装饰器
函数
描述
before_first_request
处理第一次请求之前
before_request
在每次请求之前,通常利用这个处理一些变量,实现反爬
app.after_request
注册一个函数,如果没有未处理的异常抛出,每次请求结束后运行
app.teardown_request
有异常也会运行,每次请求结束后。当APP上下文被移除后执行的函数,可以进行数据库的提交和回滚
使用示例:
1 2 3 @blue.before_request def before (): print ('before request' )
六、Flask的一些插件的使用 1、flask-caching 安装
1 pip install flask-caching
初始化
1 2 3 cache = Cache(config={ 'CACHE_TYPE' : 'simple' })
添加缓存
1 2 3 4 5 6 7 8 @blue.route('/' ) @cache.cached(timeout=20 ) def index (): print ('index2' ) print (g.star) time.sleep(5 ) return 'index2'
对反爬的相关应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @blue.before_request def before (): print ('before request' ) print (request.path) print (request.method) print (request.remote_addr) print (request.user_agent) if 'python' in request.user_agent.string: return '您正在使用python爬虫,再见!' ip = request.remote_addr if cache.get(ip): return '小伙子,别爬了!' else : cache.set (ip, 'value' , timeout=1 )
七、前后端分离模式
将原本的views拆分为urls和apis。
urls.py
1 2 3 4 5 6 7 8 9 from .exts import apifrom .apis import *api.add_resource(HelloResource, '/hello/' ) api.add_resource(UserResource, '/user/' , endpoint='id' ) api.add_resource(User2Resource, '/user2/' ) api.add_resource(User4Resource, '/user4/' )
apis.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 from flask import jsonifyfrom flask_restful import Resource, fields, marshal_with, reqparsefrom .models import *class HelloResource (Resource ): def get (self ): return 'get请求' def post (self ): return jsonify({'methods' : 'post请求' }) ret_field = { 'status' : fields.Integer, 'msg' : fields.String, 'data' : fields.String, 'like' : fields.String(default='ball' ), 'data2' : fields.String(attribute='data' ) } class UserResource (Resource ): @marshal_with(ret_field ) def get (self ): return { 'status' : 1 , 'msg' : 'ok' , 'data' : '千峰教育Python' } user_fields = { 'id' : fields.Integer, 'name' : fields.String, 'age' : fields.Integer, 'url' : fields.Url(endpoint='id' , absolute=True ) } ret_field2 = { 'status' : fields.Integer, 'msg' : fields.String, 'data' : fields.Nested(user_fields) } class User2Resource (Resource ): @marshal_with(ret_field2 ) def get (self ): user = User.query.first() return { 'status' : 1 , 'msg' : 'ok' , 'data' : user } parser = reqparse.RequestParser() parser.add_argument('name' , type =str , required=True , help ='name是必须的' ) parser.add_argument('age' , type =int , action='append' ) class User4Resource (Resource ): def get (self ): args = parser.parse_args() name = args.get('name' ) age = args.get('age' ) return {'name' : name, 'age' : age}
八、数据表之间的操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 from .exts import dbclass Grade (db.Model): __tablename__ = 'grade' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(30 ), unique=True , index=True ) students = db.relationship('Student' , backref='grade' , lazy=True ) class Student (db.Model): __tablename__ = 'student' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(30 ), unique=True ) age = db.Column(db.Integer) gradeid = db.Column(db.Integer, db.ForeignKey(Grade.id )) collect = db.Table( 'collects' , db.Column('user_id' , db.Integer, db.ForeignKey('usermodel.id' ), primary_key=True ), db.Column('movie_id' , db.Integer, db.ForeignKey('movie.id' ), primary_key=True ) ) class UserModel (db.Model): __tablename__ = 'usermodel' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(30 )) age = db.Column(db.Integer) class Movie (db.Model): __tablename__ = 'movie' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(30 )) age = db.Column(db.Integer) users = db.relationship('UserModel' , backref='movies' , lazy='dynamic' , secondary=collect)
lazy属性
dynamic: 会返回一个query对象(查询集),可以继续使用其他查询方法,如all()
select: 首次访问到属性的时候,就会加载该属性的数据
joined: 在对关联的两个表进行join操作,从而获取到所有相关的对象
True: 返回一个可用的列表对象,同select
九、前端页面渲染的一些函数操作
函数
说明
capitalize
首字母大写
upper
全部大写
lower
全部小写
title
每个单词首字母大写
trim
去掉两边的空白
striptags
去掉所有的HTML标签
safe
即删除标签,又保留标签功能
我们就可以在html页面中去使用
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 模板</title > </head > <body > <h1 > hello {{ name|capitalize }} ! </h1 > </body > </html >
这样就可以去便利我们对参数的使用
十、实践操作–个人简易博客的搭建 1、更加清晰分明的文件结构:
由于需要有前端显示和后端管理两个层面,所以将视图文件,模板文件,以及静态文件分为两个,并且专门创建了一个文件夹去存放,这样就能去更清晰的去文件分类。
2、代码编写中的一些知识学习 1)装饰器的使用 对登录界面使用装饰器进行简化,并且解决flask中使用两次同个装饰器的报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 from functools import wrapsdef login_required (fn ): @wraps(fn ) def inner (*args, **kwargs ): user_id = request.cookies.get('user_id' , None ) if user_id: user = AdminUserModel.query.get(user_id) request.user = user return fn(*args, **kwargs) else : return redirect('/admin/login/' ) return inner
2)前端js代码的修改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $(function ( ) { $("#main table tbody tr td a" ).click (function ( ) { var that = $(this ); var id = that.attr ("aid" ); if (event.srcElement .outerText == "删除" ) { if (window .confirm ("此操作不可逆,是否确认?" )) { $.post ('/admin/delarticle/' , {'id' : id}, function (data ){ console .log (data.msg ) if (data.code == 200 ){ location.reload () } else { alert (data.msg ) } }) } } }); });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @admin.route('/admin/delarticle/' , methods=['GET' , 'POST' ] ) @login_required def admin_del_article (): if request.method == 'POST' : id = request.form.get('id' ) article = ArticleModel.query.get(id ) try : db.session.delete(article) db.session.commit() except Exception as e: print ('e:' , e) db.session.rollback() return jsonify({'code' : 500 , 'msg' : '删除失败!' }) return jsonify({'code' : 200 , 'msg' : '删除成功!' }) return jsonify({'code' : 400 , 'msg' : '请求方式错误!' })
进行前后端的数据交互,通过判断请求方法区决定不同的操作对于路由而言,从而去实现不同的功能,这里就是去利用了json数据与前端的交互,从而完成了页面相应和后台数据的关联。