1 session和cookie
Session和Cookie的结合使用,一般有两种存储方式:
第一种: session数据存储在客户端: Flask采用'secure cookie'方式保存session,即session数据是使用base64编码后保存在客户端的cookie中。也就是说无须依赖第三方数据库保存session数据。
第二种: session数据存储在服务端,分为以下三步骤:
步骤1: 当客户端发送请求到服务端的时候,服务端会校验请求中cookie参数中的sessionid值,如果cookie中不存在sessionid则认为客户端访问服务端时,是发起了一个新的会话。
步骤2: 如果是新的会话,则服务端会传递给客户端一个cookie,并在cookie中存储一个新的sessionid值,并将相关数据保存在session中。
步骤3: 客户端下次再发送请求的时候,请求上下文对象会携带cookie,通过校验cookie中的sessionid值,即可判断是否是同一会话。
步骤4: 如果校验会话是同一会话,则可以从session中获取到之前保存的数据。
访问者的标识问题服务器需要识别来自同一访问者的请求。这主要是通过浏览器的cookie实现的。 访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。
1.1. Cookie
概念:
a)客户端会话技术,浏览器的会话技术
b)数据全部存储在客户端中
c)存储使用的键值对结构进行存储
特性:
支持过期时间
默认会自动携带本网站的cookie
不能跨域名
不能跨浏览器
创建:
Cookie是通过服务器创建的Response来创建的
设置:set_cookie('key', value, max_ages='', expires='')
删除, 有三种删除方式
1\. 直接清空浏览器的cookie
2\. delete_cookie('key') 直接使用delete_cookie函数
3\. set_cookie('key','',expires=0) 重新设置key的值为空,过期时间为0
获取:
在每次请求中,url都会向服务器传递Request,在request中可以获取到cookie的信息
request.cookies.get('name')
例子1,设置cookie:
import datetime
@blue.route('/setcookie/')
def set_cookie():
temp = render_template('index.html')
response = make_response(temp)
outdate=datetime.datetime.today() + datetime.timedelta(days=30)
# 设置cookie中的name的存在时长,设置为30天才过期
response.set_cookie('name','cocoococo',expires=outdate)
return response
例子2,删除cookie中的值
@blue.route('/setcookie/')
def set_cookie():
temp = render_template('index.html')
response = make_response(temp)
# 第一种方式,通过set_cookie去删除
response.set_cookie('name','',expires=0)
# 第二种方式,del_cookie删除
response.del_cookie('name')
return response
例子3,获取cookie中的值
@blue.route('/getcookie/')
def get_cookie():
name=request.cookies.get('name')
return name
1.2. 将session数据存储在数据库
flask-session是flask框架的session组件
该组件则将支持session保存到多个地方
如:
redis:保存数据的一种工具,五大类型。非关系型数据库
memcached
mongodb
sqlalchmey:那数据存到数据库表里面
1.2.1 安装
pip install flask-session
如果指定存session的类型为redis的话,需要安装redis
pip install redis
1.2.2 语法
设置session:
session['key'] = value
读取session:
result = session['key'] :如果内容不存在,将会报异常
result = session.get('key') :如果内容不存在,将返回None
删除session:
session.pop('key')
清空session中所有数据:
session.clear()
1.2.2 使用
我们在初始化文件中创建一个方法,通过调用该方法来获取到Flask的app对象
def create_app():
app = Flask(__name__)
# SECRET_KEY 秘钥
app.config['SECRET_KEY'] = 'secret_key'
# session类型为redis
app.config['SESSION_TYPE'] = 'redis'
# 添加前缀
app.config['SESSION_KEY_PREFIX'] = 'flask'
# 加载app的第一种方式
se = Session()
se.init_app(app=app)
#加载app的第二种方式
Session(app=app)
app.register_blueprint(blueprint=blue)
return app
1.2.3 案例
定义一个登陆的方法,post请求获取到username,直接写入到redis中,并且在页面中展示出redis中的username
a)需要先启动redis,开启redis-server,使用redis-cli进入客户端
b)定义方法
@blue.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
username = session.get('username')
return render_template('login.html', username=username)
else:
username = request.form.get('username')
session['username'] = username
return redirect(url_for('first.login'))
c)定义模板
<body>
<h3>欢迎:{{ username }}</h3>
<form action="" method="POST">
用户名:<input type="text" name="username" placeholder="请输入你的名字">
<input type="submit" value="提交">
</form>
</body>
d)redis中数据
[图片上传失败...(image-9503e7-1539087770774)]
注意:我们在定义app.config的时候指定了SESSION_KEY_PREFIX为flask,表示存在session中的key都会加一个前缀名flask
e) cookie和session的联系
[图片上传失败...(image-c21ba6-1539087770774)]
访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。然后根据不同的访问者来获取其中保存的value值信息。
2. jinja2
Flask中使用jinja2模板引擎
jinja2是由Flask作者开发,模仿Django的模板引擎
优点:
速度快,被广泛使用
HTML设计和后端python分离
非常灵活,快速和安全
提供了控制,继承等高级功能
3. 模板语法
3.1 模板语法主要分为两种:变量和标签
模板中的变量:{{ var }}
视图传递给模板的数据
前面定义出来的数据
变量不存在,默认忽略
模板中的标签:{% tag %}
控制逻辑
使用外部表达式
创建变量
宏定义
3.2 结构标签:
block
{% block xxx %}
{% endblock %}
块操作
父模板挖坑,子模板填坑
extends
{% extends ‘xxx.html’ %}
继承以后保留块中的内容
{{ super() }}
挖坑继承体现的化整为零的操作
macro
{% macro hello(name) %}
{{ name }}
{% endmacro %}
宏定义,可以在模板中定义函数,在其他地方调用
宏定义可导入(函数)
{% from 'xxx' import xxx %}
例子1:
在index.html中定义macro标签,定义一个方法,然后去调用方法,结果是展示商品的id和商品名称
{% macro show_goods(id, name) %}
商品id:{{ id }}
商品名称:{{ name }}
{% endmacro %}
{{ show_goods('1', '娃哈哈') }}
<br>
{{ show_goods('2', '雪碧') }}
例子2:
在index.html页面中定义一个say()方法,然后解析该方法:
{% macro say() %}
<h3>今天天气气温回升</h3>
<h3>适合去游泳</h3>
<h3>适合去郊游</h3>
{% endmacro %}
{{ say() }}
例子3:
创建function.html文档并定义一个方法:
{% macro create_user(name) %}
创建了一个用户:{{ name }}
{% endmacro %}
在index.html中引入function.html中定义的方法
{% from 'functions.html' import create_user %}
{{ create_user('小花') }}
3.3 循环
{% for item in cols %}
aa
{% else %}
bb
{% endfor %}
也可以获取循环信息loop
loop.first
loop.last
loop.index
loop.revindex
例子:
在视图(models.py)中定义一个视图函数:
@stu.route('/scores/')
def scores():
scores_list = [21,34,32,67,89,43,22,13]
content_h2 = '<h2>今天你们真帅</h2>'
content_h3 = ' <h3>今天你们真帅</h3> '
return render_template('scores.html',
scores=scores_list,
content_h2=content_h2,
content_h3=content_h3)
(该视图函数,在下面讲解的过滤器中任然使用其返回的content_h2等参数)
首先: 在页面中进行解析scores的列表。题目要求:第一个成绩展示为红色,最后一个成绩展示为绿色,其他的不变
<ul>
{% for score in scores %}
{% if loop.first %}
<li style="color:red;">{{ loop.revindex }}:{{ loop.index }}:{{ score }}</li>
{% elif loop.last %}
<li style="color:green;">{{ loop.revindex }}:{{ loop.index }}:{{ score }}</li>
{% else %}
<li> {{ loop.revindex }}:{{ loop.index }}:{{ score }}</li>
{% endif %}
{% endfor %}
</ul>
3.4 过滤器
语法:
{{ 变量|过滤器|过滤器... }}
capitalize 单词首字母大写
lower 单词变为小写
upper 单词变为大写
title
trim 去掉字符串的前后的空格
reverse 单词反转
format
striptags 渲染之前,将值中标签去掉
safe 讲样式渲染到页面中
default
last 最后一个字母
first
length
sum
sort
例子:
<ul>
<li>{{ content_h2 }}</li>
<li>{{ content_h2|safe }}</li>
<li>{{ content_h2|striptags }}</li>
<li>{{ content_h3 }}</li>
<li>{{ content_h3|length }}</li>
<li>{{ content_h3|trim|safe }}</li>
<li>{{ content_h3|trim|length }}</li>
</ul>
4. 定义模板
4.1 定义基础模板base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{% endblock %}
</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
{% block extCSS %}
{% endblock %}
</head>
<body>
{% block header %}
{% endblock %}
{% block content%}
{% endblock %}
{% block footer%}
{% endblock %}
{% block extJS %}
{% endblock %}
</body>
</html>
4.2 定义基础模板base_main.html
{% extends 'base.html' %}
{% block extCSS %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
{% endblock %}
5. 静态文件信息配置
django:
第一种方式:
{% load static %}
<link rel="stylesheet" href="{% static 'css/index.css' %}">
第二种方式:
<link rel="stylesheet" href="/static/css/index.css">
flask:
第一种方式:
<link rel="stylesheet" href="/static/css/index.css">
第二种方式:
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
6. Flask模型
Flask默认并没有提供任何数据库操作的API
我们可以选择任何适合自己项目的数据库来使用
Flask中可以自己的选择数据,用原生语句实现功能,也可以选择ORM(SQLAlchemy,MongoEngine)
SQLAlchemy是一个很强大的关系型数据库框架,支持多种数据库后台。SQLAlchemy提供了高层ORM,也提供了使用数据库原生SQL的低层功能。
ORM:
将对对象的操作转换为原生SQL
优点
易用性,可以有效减少重复SQL
性能损耗少
设计灵活,可以轻松实现复杂查询
移植性好
针对于Flask的支持,官网地址
pip install flask-sqlalchemy
安装驱动
pip install pymysql
7. 定义模型
使用SQLALchemy的对象去创建字段
其中tablename指定创建的数据库的名称
创建models.py文件,其中定义模型
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Student(db.Model):
s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
s_name = db.Column(db.String(16), unique=True)
s_age = db.Column(db.Integer, default=1)
__tablename__ = "student"
其中:
Integer表示创建的s_id字段的类型为整形,
primary_key表示是否为主键
String表示该字段为字符串
unique表示该字段唯一
default表示默认值
autoincrement表示是否自增
8. 创建数据表
在视图函数中我们引入models.py中定义的db
from App.models import db
@blue.route("/createdb/")
def create_db():
db.create_all()
return "创建成功"
@blue.route('/dropdb/')
def drop_db():
db.drop_all()
return '删除成功'
其中: db.create_all()表示创建定义模型中对应到数据库中的表
db.drop_all()表示删除数据库中的所有的表
9. 初始化SQLALchemy
在定义的init.py文件中使用SQLALchemy去整合一个或多个Flask的应用
有两种方式:
第一种:
from flask_sqlalchemy import SQLALchemy
app = Flask(__name__)
db = SQLAlchemy(app)
第二种:
from App.models import db
def create_app():
app = Flask(__name__)
db.init_app(app)
return app
10. 配置数据库的访问地址
数据库连接的格式:
dialect+driver://username:password@host:port/database
dialect数据库实现
driver数据库的驱动
例子: 访问mysql数据库,驱动为pymysql,用户为root,密码为123456,数据库的地址为本地,端口为3306,数据库名称HelloFlask
设置如下: "mysql+pymysql://root:123456@localhost:3306/HelloFlask"
在初始化init.py文件中如下配置:
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@localhost:3306/HelloFlask"
11.向表中添加数据
db.session.add(user)
db.session.commit()
12.加密解密
加密
generate_password_hash(密码)
解密
check_password_hash(数据库加密数据,前端上传数据)
manage.py
import redis
from flask import Flask
from flask_script import Manager
from flask_session import Session
from uers.models import db
from uers.views import uesrs_blue
app=Flask(__name__)
# 设置前缀users,使用url_prefix
app.register_blueprint(blueprint=uesrs_blue,url_prefix='/users')
# session配置
app.config['SESSION_TYPE']='redis'
app.config['SESSION_REDIS']=redis.Redis(host='127.0.0.1',port=6379)
# 设置秘钥SECRET_KEY
app.config['SECRET_KEY']='123'
# 数据库的配置
app.config['SQLALCHEMY_DATABASE_URI']='mysql+pymysql://root:123456@127.0.0.1:3306/flask5'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
# 获取Session对象,并初始化app
se=Session()
se.init_app(app)
# 绑定app和db
db.init_app(app)
manager=Manager(app=app)
if __name__=='__main__':
manager.run()
users->models.py
from flask_sqlalchemy import SQLAlchemy
db=SQLAlchemy()
class User(db.Model):
id=db.Column(db.Integer,primary_key=True,autoincrement=True)
username=db.Column(db.String(10),unique=True,nullable=False)
password=db.Column(db.String(128),nullable=False)
__tablename__='user'
users->views.py
from flask import Blueprint, render_template, request,\
redirect, session, url_for
from werkzeug.security import generate_password_hash, check_password_hash
from uers.models import db, User
from utils.functions import is_login
uesrs_blue=Blueprint('users',__name__)
@uesrs_blue.route('/login/',methods=['GET','POST'])
def login():
if request.method=='GET':
return render_template('login.html')
if request.method=='POST':
username=request.form.get('username')
password=request.form.get('password')
# 校验用户不能为空
if not all([username,password]):
return render_template('login.html')
user=User.query.filter_by(username=username).first()
if user:
pwd=user.password
if check_password_hash(pwd,password):
session['login_status'] = 1
return redirect(url_for('users.index'))
else:
return render_template('login.html')
@uesrs_blue.route('/index/',methods=['GET','POST'])
@is_login
def index():
return render_template('index.html')
@uesrs_blue.route('/session_delete/')
def session_delete():
session.clear()
return render_template('login.html')
@uesrs_blue.route('/scores/',methods=['GET'])
def scores():
# django 中的render
# render('scores',{'stu_scores':stu_scores,'content_h2':content_h2})
stu_scores=[80,56,31,89,76,34]
content_h2='<h2> hello python </h2>'
return render_template('scores.html',
stu_scores=stu_scores,
content_h2=content_h2)
@uesrs_blue.route('/create_db/')
def create_db():
db.create_all()
return '创建成功'
@uesrs_blue.route('/register/',methods=['GET','POST'])
def register():
if request.method=='GET':
return render_template('register.html')
if request.method=='POST':
username=request.form.get('username')
password=request.form.get('password')
if not all([username,password]):
return render_template('register.html')
# 保存注册信息
pwd=generate_password_hash(password)
user=User()
user.username=username
user.password=pwd
# 保存
db.session.add(user)
db.session.commit()
return redirect(url_for('users.login'))
templates->base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{% endblock %}
</title>
{% block css %}
{% endblock %}
{% block js %}
{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
templates->base_main.html
{% extends 'base.html' %}
{% block js %}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"type="text/javascript"></script>
{% endblock %}
templates->register.html
{% extends 'base_main.html' %}
{% block title %}
注册
{% endblock %}
{% block content %}
<form action="" method="post">
姓名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="注册">
</form>
{% endblock %}
templates->login.html
{% extends 'base_main.html' %}
{% block title %}
登录
{% endblock %}
{% block js %}
{{ super() }}
<!--第一种:引入js-->
<!--<script src="/static/js/style.js"></script>-->
{# 第二中:Django中{% static 'xxx.js' %} #}
<script src="{{ url_for('static',filename='js/style.js')}}"></script>
{% endblock %}
{% block content %}
<form action="" method="post">
姓名:<input type="text"name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
{% endblock %}
templates->index.html
{% extends 'base_main.html' %}
{% block title %}
首页
{% endblock %}
{% block js %}
{{ super() }}
{% endblock %}
{% block content %}
lala
{% endblock %}
templates->functions.html
<!--宏定义,定义函数,无参情况-->
{% macro hello() %}
<p>你好,兄弟</p>
{% endmacro %}
<!--宏定义:有参情况-->
{% macro say(name,age) %}
<p>你好,{{name}},{{age}}</p>
{% endmacro %}
templates->scores.html
{% extends 'base_main.html' %}
{% block title %}
成绩
{% endblock %}
{% block content %}
<!--宏定义,定义函数,无参情况-->
{% from 'functions.html' import hello , say %}
{{ hello() }}
<!--宏定义:有参情况-->
{{ say('小李',15) }}
{# {{ say('小李','18','男','address:天祥广场') }} #}
{{ content_h2 | safe }}
{{ content_h2 | safe | length }}
<!--去掉p标签-->
{{ content_h2 | striptags}}
{{ content_h2 | striptags | capitalize | title | lower |upper}}
{{ stu_scores }}<br>
{% for score in stu_scores %}
<!--jinja2中没有ifequal,loop不能在for循环外使用,只能在for循环中使用-->
<p {% if loop.first %} style="color:red"{% endif %}>
序号:{{ loop.index0 }}
成绩:{{ score }}
循环:{{loop.first}}
</p>
{% endfor %}
{% endblock %}