Flask Tornado Django 对比
2019-03-20 本文已影响0人
xxxQinli
安装
虚拟环境
- python3.7 -m venv venv_name 会在目录中产生一个venv_name的文件夹
- source venv_name/bin/activate - 激活
- deactivate - 退出
- pip3 freeze命令查看虚拟环境中安装了哪些包
pip3批量安装
- 创建txt文件,在txt文件中写入要批量安装的文件
- pipe install -r requirement.txt
Django
- pip3 install django==2.1.7
Flask
- pip3 install flask
Tornado
- pip3 install tornado==4.5
启动
Django
- django-admin startproject 项目名
- python3 manage.py startapp 应用名
- 修改端口: python3 manage.py runserver 端口号
- 修改 IP 和端口: python3 manage.py runserver IP:端口
Flask
app.run()函数的参数
- from flask import Flask
- app = Flask(name)
- app.run(host=host, port=port, debug=True)
- python3 manage.py
Manager管理启动命令
- from flask_script import Manager
- manage = Manager(app)
- manage.run()
- python3.7 hello.py runserver -p 端口号 -h 主机名 -d 打开调试
Tornado
- from tornado import ioloop
- import tornado.web
- 返回一个app应用
def make_app():
return tornado.web.Application(handlers=[
(r'/路由地址', 对应方法),
], template_path=模版地址, static_path=静态文件地址)
- app = make_app()
- app.listen(端口号)
- ioloop.IOLoop.current().start()
- python3 manage.py
自定义参数
- from tornado.options import define, options, parse_command_line
- 定义默认启动的端口port为8000 define('port', default=8000, type=int)
- parse_command_line()
- app.listen(options.port)
- python xxx.py --port=端口号
路由定义
Djando
- 在urls.py文件中
- 导包from django.contrib import admin
- 导包from django.urls import path
- urlpatterns = [path('路由地址/', 路由方法),path('路由地址/', 路由方法),..]
- 设置路由地址admin/,路由方法admin.site.urls为管理页面
路由URL规则
- from django.urls import path, re_path
path
- <转换器:参数名>
- 转换器: int,str,uuid,path
re_path
- /(\d+)/(\w+)/
- /(?P<参数名>\d+)/
拆分urls.py文件
- 新建app文件夹
- 在setting中的INSTALLED_APPS添加app
- 在对应的app文件夹中,新建urls.py
- 导入 from django.urls import path, re_path
- 在根urls文件里导包, from django.urls import include
- Django2.0以下写法
- path('goods/', include('goods.urls')) 当前缀为goods的路径,从goods.urls的文件中寻找路由
- 例如goods.urls中有/hello/的路由,则要写成goods/hello/
- Django2.0以上写法
Flask
@app.route('/路由地址/<类型:参数名>/')
def 路由方法():
....
设置参数
- <类型:参数名>
- <int:id> 整型
- <string:name> 字符串
- <name> 不加类型,为字符串
- <float:num> 浮点数
使用蓝图
- pip3 install flask-blueprint
- from flask import Blueprint
- blue = Blueprint('first', name)
- @blue.route('/')
- from app.views import blue
- app.register_blueprint(blueprint=blue)
Tornado
- import tornado.web
- 在make_app方法的return tornado.web.Application()的handlers=[]参数中
- (r'/路由名/', 路由方法)
设置参数
位置参数
- 定义: tornado.web.Application(handlers=[('/days/(d+)/(d+)/), DasyHandler])
- def get(self, month, day)
关键字参数
- 定义: tornado.web.Application(handlers=[(r'/days2/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})])
- def get(self, month, day, year)
路由视图
Djando
- from django.shortcuts import render
- from django.http import HttpResponse
- 定义路由视图:def 路由视图名(request): return HttpResponse('')
判断提交类型
- if request.method == 'GET':
- if request.method == 'POST':
- 若不判断,则默认GET
- GET 只用于获取数据
- POST 只用于创建数据
- PUT 修改某个对象的全部属性
- PATCH 修改某个对象的部分属性
- DELETE 删除数据
获取返回的参数
接收get传递参数
接收post传递参数
Flask
- @blue.route('/') def hello(): return
判断提交类型
- from flask import request
- if request.method == 'GET':
- if request.method == 'POST':
直接返回字符串
- return 'hello world'
渲染HTML页面
- from flask import render_template
- render_template('index.html')
跳转页面
- from flask import redirect
跳转到路由地址
- return redirect('/login/')
跳转到路由视图
- url_for('蓝图的第一个参数.跳转的函数名',传参=值)
- return redirect(url_for('first.s_id', id=1))
生成响应对象
- from flask import make_response
- res = make_response('响应内容', 响应状态码) 创建响应对象
- return make_response('Hello World', 200)
- return make_response('<font>Hello World</font>', 200)
获取返回的参数
接收get传递参数
- from flask import request
- 变量名 = request.args.get(name)
- name为input标签的name属性
接收post传递参数
- from flask import request
- 变量名 = request.form.get(name)
- name为input标签的name属性
设置响应状态码
- res = make_response('响应内容', 响应状态码) 创建响应对象
Tornado
- 在views.py页面中
- import tornado.web
- class 路由视图名(tornado.web.RequestHandler):
判断提交类型
- def get(self): def post(self): def put(self): def patch(self): def delete(self):
直接返回字符串
- self.write('hello world')
渲染HTML页面
- self.render('index.html', 传参=值)
跳转页面
- self.redirect('路由地址')
切入点函数
- def initialize():实例化初始内容,在调用行为方法之前将自动调用
- def prepare():在调用行为方法之前将自动调用
- def on_finish():在调用行为结束时自动调用
获取返回的参数
接收get传递参数
- self.get_argument(name)/self.get_arguments(name)
获取请求URL中的参数
- self.get_query_argument(name)/ self.get_query_arguments(name)
接收post传递参数
- self.get_argument(name)/self.get_arguments(name)
- self.get_body_argument(name)/self.get_body_arguments(name)
设置响应状态码
- self.set_status(200)
cookie参数
关于cookie
- 产生场景:由于HTTP无状态协议,无法保持登陆状态,使用cookie,保存一个令牌(标识符)用于标识用户的登陆状态
- 产生令牌:在登陆时向cookie中设置
- cookie不能跨浏览器,参数有字节限制,不能无限的存储参数
Django
Flask
设置cookie参数
- 生成响应内容 res = make_reponse()
- 设置cookie值 res.set_cookie(key, value, max_age) max_age为cookie存在时间毫秒
res = make_response(render_template('index.html'))
# 给index.html添加cookie
# 设置set_cookie('token', )
res.set_cookie('token', '12345678', max_age=3000)
return res
获取cookie参数
- token = request.cookies.get('token')
删除cookie数据
- res.delete_cookie('token')
Tornado
普通cookie
设置cookie参数
- 设置cookie,其中的expire参数表示过期时间,到了过期时间,自动删除
- self.set_cookie('token', '123456', expires_days=1)
获取cookie参数
- self.get_cookie('token')
删除cookie数据
- self.clear_cookie('token')
- self.clear_all_cookies()
secure cookie
session参数
关于session
- Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
Django
Flask
第一种: session数据存储在客户端
- Flask采用'secure cookie'方式保存session,即session数据是使用base64编码后保存在客户端的cookie中。也就是说无须依赖第三方数据库保存session数据。
- 先导入session
- from flask import session
- 对session中键值对加密,设置secret_key的加密复杂程度,使session的value加密
- app.secret_key = '1234567890'
- 向session会话中设置键值对
- session['login_status'] = 1
- 判断session是否有登陆标识
- if 'login_status' in session:
第二种: session数据存储在服务器端
- 步骤1: 当客户端发送请求到服务端的时候,服务端会校验请求中cookie参数中的sessionid值,如果cookie中不存在sessionid则认为客户端访问服务端时,是发起了一个新的会话。
- 步骤2: 如果是新的会话,则服务端会传递给客户端一个cookie,并在cookie中存储一个新的sessionid值,并将相关数据保存在session中。
- 步骤3: 客户端下次再发送请求的时候,请求上下文对象会携带cookie,通过校验cookie中的sessionid值,即可判断是否是同一会话。
- 步骤4: 如果校验会话是同一会话,则可以从session中获取到之前保存的数据。
- 访问者的标识问题服务器需要识别来自同一访问者的请求。这主要是通过浏览器的cookie实现的。 访问者在第一次访问服务器时,服务器在其cookie中设置一个唯一的ID号——会话ID(session)。 这样,访问者后续对服务器的访问头中将自动包含该信息,服务器通过这个ID号,即可区 隔不同的访问者。
将session数据存储在数据库
- 安装flask-session,是flask框架的session组件
- pip3 install flask-session
- from flask_session import Session
- 安装python的redis库
- pip3 install redis
- 配置Session
- app.config['SESSION_TYPE'] = 'redis'; app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port=6379)
- 对session中键值对加密,设置secret_key的加密复杂程度,使session的value加密
- app.secret_key = '1234567890'
- Session(app)
读取session数据
- value = session['login_status']
删除session数据
- del session['login_status']
Tornado
数据库模型
Django
导入
- from django.db import models
数据库简单配置与迁移
- 修改 settings.py 文件中 DATABASE 的数据: NAME,USER,PASSWORD,HOST,PORT,OPTIONS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dj',
'USER': 'root',
'PASSWORD': '7890267648',
'HOST':'127.0.0.1',
'PORT':3306
}
}
- 在setting表中的INSTALLED_APPS添加'app名'
- 生成迁移文件: python manage.py makemigrations
- 执行迁移文件: python manage.py migrate
- 当第一次迁移 django 默认提供的表时,直接执行 migrate 即可,若要迁移新建的表或者修改表,则先生成迁移文件,再执行迁移文件。
模型定义
字段定义
- IntegerField: 整型字段
- CharField: 字符串
- BooleanField: 布尔值
- DateTimeField: 年月日时分秒字段
- DateField: 年月日
- TimeField: 时间戳
- ImageFiled: 图片
- FloatFiled: 浮点数
- DecimalField: 浮点数,指定了长度的浮点数
- TextField: 存文本,texteare
- AutoField: 自增字段,不用定义
约束定义
- max_length: 最大长度
- min_length: 最小长度
- unique: 唯一
- null: 是否为空
- default: 默认值
- auto_now_add 和 auto_now: 互斥,当调 save()的时候才会生成
模型操作
- from django.db import models
- Student.objects
增
- 对象.save()
- stu = Student() stu.save()
- create(字段=值,字段 2=值 2...)
- Student.objects.create(s_name='小花',age=20)
删
- 对象.delete()
- Student.objects.filter(s_name = '小小花').delete()
- filter().delete()
- stu = Student.objects.filter(s_name = '小明').first() stu.delete()
改
- 修改的对象.save()
- stu.age = 50 stu.save()
- filter().update(字段=值,字段 2=值 2...)
- Student.objects.filter(s_name='小花').update(age=40)
查
查询所有
- Student.objects.all()
筛选
- filter(字段=值)
- 查询结果类型为 QuerySet,取其中的数据,.first(), last(),[下标]
- .get(字段=值)
- .get()直接获取到对象 .filter()获取 QuerySet 对象,要使用.first() last()来获取对象 .filter().first() 相当于 .get
- get(条件),查询条件必须查找到结果,如果查询不到结果,则报错'DoesNotExist',若查询到了多个,也会报错
筛选不满足条件的
- exclude(),筛选出不满足条件的
统计多少条数据
- Student.objects.all().count()
排序
- Student.objects.all().order_by('id')
- 若要降序,则在字段前家-号('-id')
获取对象
- Student.objects.all().values('s_name', 'age')
- 序列化,将对象的属性转化为字典格式数据
模糊查询
- contains 字段包含关键字 字段__contains = '值'
- startswith 字段开头包含关键字 字段__startswith = '值'
- endswith 字段结尾包含的关键字 字段__endswith = '值'
查询结果在某一范围
- id__in=[1,2,3,4,5,6,7]
- Student.objects.filter(id__in=[1,2,3,4,5]).all()
- Student.objects.filter(pk__in=[1,2,3,4,5]).all() pk 代表主键
大于 gt,大于等于 gte,小于 lt,小于等于 lte
- Student.objects.filter(ag__gt=23).all()
聚合
- 导包 from django.models import Avg,Sum,Count,Max,Min
-Student.objects.all().aggragate(Avg('age'))
字段值大于另一个字段值
- 导包 from django.models import F
- Student.objects.filter(yuwen__gt=F('math') + 10)
Q() & Q()且 ,Q() | Q() 或,~Q() 非
- 且 Student.objects.filter(s_name__contains='花', age=23).all()
- 或 Student.objects.filter(Q(s_name__contains='花') | Q(age=23)).all()
- 非 Student.objects.filter(~ Q(s_name__contains='花')
模型关联关系
一对一
class A():
id = modules.IntegerFiled()
class B():
aa = mldels.OneToOneField(A,, on_delete=models.CASCADE, null=True,related_name='cc')
- OneToOneField(关联模型)
- 模型定义
- 关联名 = models.OneToOneField(关联的表名, related_name = '关系名', on_delete=models.CASCADE, null=True)
- 已知 A 对象 a 查询 B 对象
- a.b 当 related_name 没定义时
- a.cc 当 related_name = 'cc'时
- 已知 B 对象 b 查询 A 对象 b.aa
- 一对一:定义在关联的两个模型中的任意一方都可以
一对多
class A():
id
class B():
aa = models.ForeignKey(A, on_delete=models.CASCADE, null=True,related_name='cc')
- ForeignKey(关联模型)
- 模型定义
- aa = ForeignKey(A)
- 已知 A 对象 a,查询 B 对象
- a.b_set 当 related_name 没定义时
- a.cc 当 related_name = 'cc'时
- 已知 B 对象 b 查询 A 对象 b.aa
- 一对多:定义在'多'的一方
多对多
- course = models.ManyToManyField(Course要进行关联的表的名字,related_name='stu') 会自动生成中间文件,中间文件的表名为course
- 查询id为1的学生课程信息
- stu = Student.objects.get(pk=1)
- 学生查询课程
- stu.course
- 课程查询学生
- cou = Course.objects(pk=1)
- 当 related_name 没定义时 cou.student_set.all()
- 当 related_name 定义时 cou.stu.all()
- 增加中间表信息
- stu = Student.objects.filter(s_name = '小明').first()
- cou1 = Course.objects.get(c_name='日语')
- stu.course.add(cou1)
- 删除中间表信息
- stu.course.remove(cou1)
- on_delete参数
- models.CASCADE 表示: 主键所在行的数据被删,外键所在行的数据也会被删
- models.PROTECT 表示: 主键有对应的外键数据时,不让删除主键的数据
- models.SET_NULL 表示: 主键删除,外键置空
- 注意: ManyToManyFiled定义的字段定义在任何一个关联模型中都可以
Flask
使用flask-sqlalchemy操作数据库
- 安装pymysql与flask_sqlalchemy
- from flask_sqlalchemy import SQLAlchemy
- db = SQLAlchemy()
- 定义id主键,自增字,类型要大写
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
配置数据库连接信息
- dialect+driver://username:password@host:port/database
- app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:7890267648@localhost:3306/flask9'
- app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
- db.init_app(app)
迁移模型
- 第一次迁移模型时候才有用 db.create_all()
删除表
- db.drop_all()
增删改查
- 新增
# 新增学生数据
stu = Student()
stu.s_name = '小明'
stu.age = 20
# 准备向数据库flask9中的student表中插入数据
db.session.add(stu)
# 提交操作
db.session.commit()
return '创建数据成功'
- 批量增加
stu_list = []
for i in range(10):
stu = Student()
stu.s_name = '小明' + str(i)
stu.age = i
stu_list.append(stu)
db.session.add_all(stu_list)
db.session.commit()
return '数据添加完成'
-
删除 db.session.delete(stu)
-
修改
- stus = Student.query.filter_by(s_name='小张2')[0]
- stus.age = 100
-
查询
- 筛选出来的都为列表,就算只有一个数据,也是列表中存放一个数据,所以要用下标或者.first去取
- stu = Student.query.filter_by(s_name='小张0').all()[0] # .all为列表
- stu = Student.query.filter_by(s_name='小张0').first() - 筛选出的第一个
-
查询所有学生信息
- all()结果为列表,列表中的元素为查询的学生对象
- stus = Student.query.all()
- print(stus)
-
查询id=1的学生信息
- stu = Student.query.filter(Student.id == 1).first()
- get():查询主键所在行的信息
- stu = Student.query.get(21)
-
排序 order_by()
- stus = Student.query.order_by( -Student.id ).all()
-
offset limit
- stus = Student.query.offset(1).limit(3).all()
- 分页 stus = Student.query.offset((page-1)*3).limit(3).all()
- stus[(page-1)3:page3]
-
模糊查询like % _
- 包含
- stus = Student.query.filter(Student.s_name.contains('小张')).all()
- like
- stus = Student.query.filter(Student.s_name.like('%小张%')).all()
- stus = Student.query.filter(Student.s_name.like('_小张')).all()
- stus = Student.query.filter(Student.s_name.like('小张_')).all()
- 以什么什么开头
- stus = Student.query.filter(Student.s_name.stratswith('小张_')).all()
- 以什么什么结束
- stus = Student.query.filter(Student.s_name.endswith('小张_')).all()
- 包含
-
大于 gt lt 大于等于gt 小于等于le
- stus = Student.query.filter(Student.age.ge(4)).all()
- stus = Student.query.filter(Student.age >= 9).all()
-
where id in [1,2,3,4,5,6,7]
- stus = Student.query.filter(Student.id.in_([1,2,3,3,5,6,7])).all()
-
not in
- stus = Student.query.filter(Student.id.notin_([1,2,3,3,5,6,7])).all()
-
多条件查询
- stus = Student.query.filter(Student.age == 2).filter(Student.s_name.like('小%')).all()
- stus = Student.query.filter(Student.age == 2, Student.s_name.like('小%')).all()
-
and or 条件查询
- stus = Student.query.filter(Student.age == 2 or Student.s_name.like('小%')).all()
-
and_, or_ not_
- from sqlalchemy import and_, not_, or
- stus = Student.query.filter(and_(Student.age == 2, Student.s_name.like('小%'))).all()
一对多关系
- 添加外键
- 定义的关联班级表id的外键g_id
- g_id = db.Column(db.Integer, db.ForeignKey('grade.id'), nullable=True)
- 若是新建表,则会自动生成外键,若是已有的表,则要手动在数据库中加外键。
- 关联关系的定义:stus = db.relationship('Student', backref= 'p')。班级查询学生:班级对象.stus,为列表,返回班级对象对应的学生对象。学生查询班级:学生对象.backref,为列表,返回学生对象对应的班级对象。
- 有一个外键就要写一个关系
多对多关系
- 定义
- stus = db.relationship('Student', secondary=c_s, backref='cou', lazy='dynamic')
- 学生查询课程:学生对象.backref
- 课程查询学生:课程对象.stus
- 添加和删除stu.cou.append(cou1)、stu.cou.remove(cou2)
- 中间表
- c_s = db.Table('c_s',db.Column('s_id', db.Integer, db.ForeignKey('student.id')),db.Column('c_id', db.Integer, db.ForeignKey('course.id')))
Tornado
安装SQLAlchemy
- pip3 install SQLAlchemy
配置连接
- 新建conn.py文件
- # 连接数据库格式# mysql+pymysql://root:123456@127.0.0.1:3306/tornado
- from sqlalchemy import create_engine
- # 创建引擎,建立连接 engine = create_engine(db_url)
- from sqlalchemy.ext.declarative import declarative_base
- # 模型与数据库表进行关联的基类,模型必须继承于Base
- Base = declarative_base(bind=engine)
- from sqlalchemy.orm import sessionmaker
- # 创建session会话
- DbSession = sessionmaker(bind=engine)
- session = DbSession()
建立模型
- 新建models.py文件
- from sqlalchemy import Column, Integer, String
- from utils.conn import Base
- 创建表和粘贴表的方法
- def create_db():Base.metadata.create_all()
- def drop_db():Base.metadata.drop_all()
- 创建数据模型
# 要继承Base
class Student(Base):
# 主键自增的int类型的id主键
id = Column(Integer, primary_key=True, autoincrement=True)
# 定义不能为空的唯一的姓名字段
s_name = Column(String(10), unique=True, nullable=False)
s_age = Column(Integer, default=18)
使用模型
- 在views.py文件中
- 导入创建表和删除表方法
- from app.models import create_db, Student,drop_db
- 导入session
- from utils.conn import session
创建单条数据
- stu = Student()
- session.add(stu)
- session.commit()
创建多条数据
- session.add_all(stus)
- session.commit()
删除数据
- session.delete(stu)
- session.commit()
- 或者 session.query(表).filter(筛选条件).delete()
查询数据
- stus = session.query(Student).filter_by(s_name == '小明').all() 返回列表
- filter_by 使用字段名s_name
- stus = session.query(Student).filter(Student.s_name == '小明').all() # 返回列表
- filter 使用类名.字段名
模版
Django
- 在setting.py中的TEMPLATES参数设置templates文件夹路径
- 在setting.py中的STATIC_URL参数设置static文件夹路径
- 父模板: 用于挖坑 {% block name %} {% endblock %}
- 子模板: 负责继承父模板后,进行填坑
标签: {% 标签 %}
- {% extends '父模板' %}
- {% block title %} {% block %}
- {% if 条件 %} {% else %} {% endif %}
- {% ifequal 变量 值 %} {% endifequal %}
- {% for i in [] %} {% endfor %}
变量: {{ 变量名 }}
- {{ forloop.counter }}
- {{ forloop.counter0 }}
- {{ stu.course.all }}: 通过学生查询所有的课程信息
- {{ stu.course.下标 }}
过滤器
- 定义: 使用管道符 ‘|’
Flask
jinja2
- 语法{% %}
- 在模版页面中
<title>
{% block title %}
{% endblock %}
</title>
- 在使用模版的页面中
{% extends 'base.html' %}
{% block title %}
学生列表页面
{% endblock %}
继承模版填充内容
- {{ super() }},不能忘加括号
{% block js %}
{{ super() }}
<script src="123.js"></script>
{% endblock %}
引用外部文件写法
{% block css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css')}}">
{% endblock %
后端向前端传入变量
- stus_score = [90, 89, 100, 99, 87, 67];return render_template('stu.html', scores = stus_score)
前端解析变量
- 解析变量使用{{变量名}} <p> {{ scores}} </p>
- 解析标签,extends,block,for
- {{ loop.index }} 从1开始
- {{ loop.index0 }} 从0开始
- {{ loop.revindex }} 反向开始
- {{ loop.revindex0 }} 反向d到0结束
- {{ loop.first }} 第一次循环,返回true
- {{ loop.last }} 最后一次循环,返回true
过滤器
- content_h2 = '<font>hello world!</font>'
- {{ content_h2 | safe }} 加载样式
- {{ 'Python' | length }} 长度
- {{ 'Python' | upper }} 大写
- {{ 'Python' | lower }} 小写
- {{ 'python' | upper | length }} 长度
- {{ 'Python' | reverse }} 反序
- {{ content_h2 | striptags }} 取消样式