Python-Flask
flask是Python的一个轻型Web框架.
使用pycharm自动创建项目,也可以手动创建,以下是目录的结构:
├── app.py
├── static
└── templates
一 创建一个简单应用
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "hello world "
if __name__ == '__main__':
app.run()
其中,创建Flask实例时,可以自定配置主要有:
参数 | 说明 |
---|---|
static_path | 静态文件访问路径(不推荐使用,使用 static_url_path 代替) |
static_url_path | 指定静态文件的访问路径(默认为 / + static_folder) |
static_folder | 指定存放静态文件资源的文件夹的名称(默认为static) |
template_folder | 指定存放模板的文件夹的名称(默认为templates) |
二 路由规则
1 简单路由
@app.route('/')
def hello_world():
return "hello world "
2 带参数路由
2.1 不限定类型
@app.route("/user/<username>")
def user_info(username):
return "hello %s" % username
2.2 限定类型
@app.route("/user/<string:username>")
def user_info(username):
return "hello %s" % username
注:这里是string而不是str
其他的类型有
int 、 float、uuid、path
3 正则路由
在Flask中是不能直接使用re的,需要使用转换器。转换器的作用就是:过滤指定用户。
使用正则路由的步骤:
1、导入转换器基类(BaseConverter)
2、自定义转换器(需要继承BaseConverter)
3、把自定义的转换器添加到转换器字典中
4、在路由中使用转换器
代码:
from flask import Flask, render_template
# 第一步
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# 第二步
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 正则参数
self.regex = args[0]
# 第三步
app.url_map.converters['re'] = RegexConverter
# 第四步
@app.route("/user/<re('lcz.{2}'):username>")
def user_info(username):
return "hello %s" % username
@app.route('/')
def hello_world():
return render_template("index.html")
if __name__ == '__main__':
app.run()
系统自带的转换器
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'path': PathConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
其实很多都是参数路由的
4 限定请求方式
加上一个methods参数限定请求的方式,注意methods应该是一个list
from flask import request
@app.route("/user", methods=["get", "post"])
def user():
if request.method == "get":
return "user"
elif request.method == "post":
pass
else:
pass
注意request是需要从falsk中导入的
三 响应规则
1 HTML
1.1 HTML文本
@app.route("/user")
def user():
return "<h1>User Info</h1>"
1.2 HTML文件
from flask import render_template
@app.route('/')
def hello_world():
return render_template("index.html")
2 JSON
from flask import jsonify
@app.route("/user")
def user():
data = {
"user_name": "lczmx",
"age": "18"
}
return jsonify(data)
3 重定向
from flask import redirect
@app.route("/index")
def index():
return redirect("/")
4 跳转视图
from flask import redirect
from flask import url_for
@app.route("/user/<int:user_id>")
def user(user_id):
if user_id > 200:
return redirect(url_for("vip_user", user_id=user_id))
else:
return redirect(url_for("general_user"))
@app.route("/vip_user/<int:user_id>")
def vip_user(user_id):
return "vip_user %s" % user_id
@app.route("/general_user")
def general_user():
return "general_user"
5 状态码
@app.route('/')
def index():
return render_template("index.html"), 200
200就是状态码,可以是其他的
四 模板引擎
注:Flask的模板引擎是jinja2, 与django的很相似
1 变量
使用: {{ 变量名 }}
app.py中传参
@app.route('/')
def index():
return render_template("index.html", name="lczmx")
2 注释
使用: {# 多行注释 #}
3 过滤器
使用: {{ var|filter }}
3.1 常用的过滤器
设置默认值: {{ data.bool|default('我是默认值',boolean=True) }}
禁用转义:{{ '<em>hello</em>'
| safe }}
删除标签:{{ '<em>hello</em>'
| striptags }}
首字母大写:{{ 'hello' | capitalize }}
所有值小写:{{ 'HELLO' | lower }}
首字母大写:{{ 'hello world' | title }}
字符串反转:{{ 'hello' | reverse }}
字符串截断:{{ 'hello world' | truncate(5) }}
拼接成字符串: {{ [1,2,3,4] | join('') }}
字符的格式化: {{ '我叫%s '|format('lczmx') }}
获取长度:{{ [1,2,3,4,5,6] | length }}
列表求和:{{ [1,2,3,4,5,6] | sum }}
列表排序:>{{ [6,2,3,1,5,4] | sort }}
3.2 自定义过滤器
方法1: 通过Flask对象(app)的add_template_filter方法
def add_suffix(line):
return line + "--lczmx"
# 可以给过滤器器一个名字,如果没有,默认就是函数的名字
app.add_template_filter(add_suffix,'add_suffix')
方法2: 使用装饰器来实现
# 使用装饰器事项过滤器,
# 如果不传入参数,默认过滤器名字就是函数的名字
@app.template_filter()
def add_suffix(line):
return line + "--lczmx"
4 标签
4.1 if
{% if user_info.nid == 10 %}
<li>{{ user_info.msg1 }}</li>
{% elif user_info.nid == 20%}
<li>{{ user_info.msg2 }}</li>
{% else %}
<li>{{ user_info.msg3 }}</li>
{% endif %}
4.2 for
{% for name in name_list %}
<li>{{ name }}</li>
{% endfor %}
不能使用continue和break
注:
在for中有一个关键字叫loop,使用loop可以查看当前的迭代状态
下面列出我认为有用的loop方法
方法 | 说明 |
---|---|
index0 | 获取当前迭代的索引(从0开始) |
index | 获取当前迭代的索引(从1开始) |
first | 是否为第一次迭代 |
last | 是否为最后一次迭代 |
length | 要迭代的数据长度 |
nextitem | 下一项 |
previtem | 前一项 |
使用:
{% for k,v in data.items() %}
<li>{{ k }}--{{ v }}</li>
<li>{{ loop.index }}</li>
<li>{{ loop.index0 }}</li>
<li>{{ loop.first }}</li>
<li>{{ loop.last }}</li>
<li>{{ loop.length }}</li>
<li>{{ loop.nextitem}}</li>
<li>{{ loop.previtem}}</li>
<li>{{ loop.changed}}</li>
<li>{{ loop.cycle}}</li>
<li>{{ loop.revindex}}</li>
<li>{{ loop.revindex0}}</li>
{% endfor %}
5 导入
导入一个html文件,主要是导入一些可复用的.
{% include "layout.html" %}
6 继承
比如我现在有一个模板base.html需要在index.html中继承.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% block title %}
{% endblock %}
<div>
<h1>今天是个好日子</h1>
</div>
{% block content%}
<h3>我是内容</h3>
{% endblock %}
{% block js %}
<script src="../static/js/jquery.js"></script>
{% endblock %}
</body>
</html>
用block留出填充部分
{% extends "base.html" %}
{% block title %}
<h1>啦啦啦</h1>
{% endblock %}
{% block js %}
{% endblock %}
在另外一个html中就可以继承修改一些东西了
**注: block后面跟着名字
五 获取请求数据
注意: 需要先导入request
form flask import request
常用的request属性有:
属性名 | 说明 |
---|---|
request.scheme | 获取请求协议 |
request.method | 获取本次请求的请求方式(GET / POST) |
request.args | 获取以get请求方式提交的数据 |
request.form | 获取以post请求方式提交的数据 |
request.cookies | 获取cookies中的相关信息 |
request.headers | 获取请求信息头的相关信息 |
request.files | 获取上传的文件 |
request.path | 获取请求的资源具体路径(不带参数) |
request.full_path | 获取完整的请求资源具体路径(带参数) |
request.url | 获取完整的请求地址,从协议开始 |
request.files | 获取上传的文件用(save进行保存) |
注: arequest.args,request.form ,request.files 的返回值都是字典。
六 会话控制
1 cookie
Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie的key/value可以由服务器端自己定义。
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
Cookie基于域名安全,不同域名的Cookie是不能互相访问的
如访问huya.com时向浏览器中写了Cookie信息,使用同一浏览器访问 baidu.com时,无法访问到huya.com写的Cookie信息
浏览器的同源策略针对cookie也有限制作用.
当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息
使用场景: 登录状态, 浏览历史, 网站足迹
1.1 设置cookie
from flask import make_response
@app.route('/set_cookie')
def set_cookie():
res = make_response('set cookie page')
res.set_cookie('username', 'lczmx', max_age=604800) # max_age 是cookie有效期,单位为秒, 这里刚好设置一周时间
return res
1.2 获取cookie
from flask import request
@app.route('/vip')
def resp_cookie():
user_name = request.cookies.get('username')
if user_name == "lczmx":
return "vip"
else:
return "bye"
2 session
对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息
在服务器端进行状态保持的方案就是Session
注意: Session依赖于Cookie,而且flask中使用session,需要配置SECRET_KEY选项,否则报错.
2.1 配置SECRET_KEY
生成SECRET_KEY:
这里使用操作系统的密码随机生成器生成
命令行输入:
import os
os.urandom(24)
在config.py中:
CSRF_ENABLED = True
SECRET_KEY = 'SECRET_KEY粘贴在这里'
在app.py中
import config
app.config.from_object(config)
其实,所有的配置都应该放在config文件中,然后导入
2.2 设置session
from flask import session
@app.route('/set_session')
def set_session():
session['username'] = 'lczmx'
session['password'] = '123456'
return 'success'
一定要先配置SECRET_KEY
2.3 获取session
from flask import session, jsonify
@app.route('/login')
def login():
user_name = session.get('username')
pass_word = session.get('password')
if user_name == "lczmx" and pass_word == "123456":
return jsonify({"status": 1, "msg": "success"})
else:
return jsonify({"status": 0, "msg": "failed"})
七 Models
假如使用过django的orm,我相信一般人都不会直接写sql语句操作数据库了.那么Flask有没有一款orm呢?应该说是有吧.该ORM是SQLAlchemy, 首次发行于2006年2月,并迅速地在Python社区中最广泛使用的ORM工具之一,不亚于Django的ORM框架。
1 安装
默认安装Flask是不带flask-sqlalchemy,需要手动安装
pip install flask-sqlalchemy
# 如果连接的是 mysql 数据库,需要安装 flask-mysqldb
pip install flask-mysqldb
# 提示:如果flask-mysqldb安装不上,安装, pip install pymysql
2 配置
操作数据库需要先创建一个db对象,通常写在exts.py文件里。
exts.py
:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
flask项目一般将数据库配置写入config.py文件里面,配置在创建引擎前需写好,不要在程序运行时修改配置,如下
config.py
:
HOST = '127.0.0.1'
PORT = '3306' # 在mysql中使用show global variables like 'port';查看端口
DATABASE = 'test'
USERNAME = 'root'
PASSWORD = '123456'
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,
password=PASSWORD, host=HOST,
port=PORT, db=DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True
配置选项
名称 | 说明 |
---|---|
SQLALCHEMY_DATABASE_URI | 连接的数据库。示例:mysql://username:password@host/post/db?charset=utf-8 |
SQLALCHEMY_BINDS | 一个将会绑定多种数据库的字典 |
SQLALCHEMY_ECHO | 是否调试 |
SQLALCHEMY_POOL_SIZE | 数据库池的大小,默认值为5 |
SQLALCHEMY_POOL_TIMEOUT | 连接超时 |
SQLALCHEMY_POOL_RECYCLE | 自动回收连接的秒数 |
SQLALCHEMY_TRACK_MODIFICATIONS | 默认为True ,Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。(需要内存, 可以禁用) |
SQLALCHEMY_MAX_OVERFLOW | 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。 |
3 使用
在app.py
:
from flask import Flask
import config
from exts import db
app = Flask(__name__)
app.config.from_object(config) # 加载配置文件
db.init_app(app) # db绑定app
@app.route('/')
def index():
return "Index"
if __name__ == '__main__':
app.run()
4 ORM操作
4.1 表操作
4.1.1 创建一个表
# models文件中
from exts import db
class User(db.Model):
__tablename__ = "User" #设置表名
uid = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
在Flask-SQLAlchemy中,一张表其实就是一个类.跟Django的ORM非常相似.
注意: 类需要继承db.Model
完整示例
创建一个表的过程,有很多坑。所以这里详细记录一下
首先看一下tree图
.
├── app.py
├── config.py
├── exts.py
├── models.py
把应用分成4个py文件是为了以后好维护。
app.py
:
from flask import Flask
import config
from exts import db
from models import Article # 坑一
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app) # 坑二
@app.route('/')
def index():
db.create_all() # 坑三
return "Index"
if __name__ == '__main__':
app.run()
坑一: 你要创建那个表,你就得导入那个表所在的py文件(即使它在app.py中未被使用),否者不会创建表,且不会报错
坑二: app.config.from_object(config) 一定要在db.init_app(app)之前,否者会报错
坑三: db.create_all() 是创建一个表的命令(models中的类不会创建一个表,就好比django用命令行生成表一样),注意一定要在view函数里面写,不然不会创建表,且不会报错.建议在'/'中写,不会重复创建,可以在创建完后删除
config.py
:
HOST = '127.0.0.1'
PORT = '3306' # 在mysql中使用show global variables like 'port';查看端口
DATABASE = 'test'
USERNAME = 'root'
PASSWORD = '123456'
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,
password=PASSWORD, host=HOST,
port=PORT, db=DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True
跟之前的没有区别,为了方便写下了
exts.py
:
from flask_sqlalchemy import SQLAlchemy
#此时先不传入app
db = SQLAlchemy()
models.py
:
from exts import db
class Article2(db.Model):
__tablename__ = 'article'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
def __init__(self, title, content):
self.title = title
self.content = content
在这里就可以尽情写表了,注意在app.py中引入就行了
__init__中记得写上字段名,新增数据时要实例化
数据类型和约束条件在这里:
# class类中
# 使用db.Column(数据类型, 约束条件)创建,比如:
uid = db.Column(db.Integer, primary_key=True)
数据类型
数据类型 | 说明 |
---|---|
Integer | 整形(一般是32位) |
SmallInteger | 整形(一般是16位) |
BigInteger | 不限制精度的整数 |
Float | 浮点数 |
String | 字符串 |
Text | 文本 |
Unicode | Unicode 字符串 |
UnicodeText | Unicode 文本 |
Date | 日期(datetime.date) |
Time | 时间(datetime.time) |
DateTime | 日期+时间(datetime.datetime) |
Boolean | 布尔值 |
LargeBinary | 二进制文件 |
pickleType | pickle(Python的一个标准库)后的对象 |
约束条件
约束条件 | 说明 |
---|---|
primary_key | 如果设为 True,这列就是表的主键 |
autoincrement | 如果设为 True,主键自增长 |
unique | 如果设为 True,这列不允许出现重复的值 |
index | 如果设为 True,为这列创建索引,提升查询效率 |
nullable | 如果设为 True,这列允许使用空值;如果设为 False,这列不允许使用空值 |
default | 为这列定义默认值 |
doc | 字段说明 |
4.1.2 创建一条记录
我们现在已经有一张表了,假如我们需要向表中添加数据那么就需要用到
db.session.add(tab)
db.session.commit()
例子
注User表和render_template都要我们导入
@app.route("/register", methods=["get", "post"])
def register():
print(request.method)
if request.method == "GET":
return render_template("register.html")
else:
username = request.form.get("username")
password = request.form.get("password")
user_obj = User(username, password)
db.session.add(user_obj)
db.session.commit()
return "ok!"
特别提示: db.session.add()的参数是一个model对象(表的对象),传错的话会报错
对于一次性修改多个表的话,可以使用db.session.add_all(), 参数是由表对象组成的可迭代对象(如:[user_obj, article_obj])
4.2 一对多关系
4.2.1建立一对多关系
一对多关系就是字表db.ForeignKey和主表db.relationship一起建立
db.relationship很重要,后面查询时用到
from exts import db
class User(db.Model):
__tablename__ = 'user'
nid = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(16), nullable=False)
password = db.Column(db.String(32), nullable=False)
article = db.relationship("Article", backref="user")
def __init__(self, username, password):
self.username = username
self.password = password
def __repr__(self):
return "<User %s>" % self.username
class Article(db.Model):
__tablename__ = 'Article'
nid = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(36), nullable=False)
content = db.Column(db.Text, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.nid"))
def __init__(self, title):
self.title = title
self.content = content
def __repr__(self):
return "<User %s>" % self.title
坑: 一定要注意db.ForeignKey的参数是
表名.xxx
,而且表名是__tablename__,不是类名,大小写敏感,写错会报错
4.2.2 一对多——增
一对多增加条记录,会同时修改两个表,但只需要一次bd.session.add()和db.session.commit()
@app.route("/register", methods=["get", "post"])
def register():
print(request.method)
if request.method == "GET":
return render_template("register.html")
else:
username = request.form.get("username")
password = request.form.get("password")
title = request.form.get("title")
content = request.form.get("content")
user_obj = User(username, password)
art_obj = Article(title, content)
user_obj.article = [art_obj]
db.session.add(user_obj)
db.session.commit()
return "ok!"
注意: relationship字段接收的是一个list
4.2.3 一对多——删
步骤:
1、找到要删除的记录
2、db.session.delete(a)删除
3、db.session.commit()保存
@app.route('/delete_user')
def delete_user():
a = db.session.query(User).filter(User.nid == 2).first()
db.session.delete(a)
db.session.commit()
return "ok"
4.2.4 一对多——改
@app.route('/update_article')
def update_article():
# 方法一
a = db.session.query(Article).get(1) # 先查询出需要修改的条目
print(a)
Article.query.get(1)
a.title = '母猪产后护理' # 修改
db.session.commit()
# 方法二
# 直接查询出后修改,update采用字典修改{修要修改的列:'修改后的值'}
db.session.query(Article).filter(Article.nid == 3).update({Article.title: '母猪产后护理'})
db.session.commit()
return 'ok'
4.2.5 一对多——查
先看代码
@app.route('/get_article')
def get_article():
# 子查主
# 方法一
user1 = db.session.query(Article).filter(Article.title == '母猪产后护理').first().user
# 方法二
user2 = Article.query.filter(Article.title == '母猪产后护理').first().user
print("user1", user1.username)
print("user2", user2)
# 主查子
# 方法一
article1 = db.session.query(User).filter(User.username == 'lczmx').first().article
# 方法二
article2 = User.query.filter(User.username == 'lczmx').first().article
print(article1)
print(article2)
return "ok"
这里需要注意的是:
1、query.filter()后的数据是BaseQuery对象,不能通过‘.外键’的方式取值,要把其变为表的对象后才能‘.’
2、这里的跨表查询实际上用到的是主表的relationship
relationship的参数:backref
图示:
补充:
relationship的关键字参数cascade
值 | 说明 |
---|---|
save-update | 默认选。在添加一条数据的时候,会把其他和它相关联的数据都添加到数据库中。这种行为就是save-update属性影响的。 |
delete | 表示当删除某一个模型中的数据的时候,是否也删除掉使用relationship和它关联的数据。 |
delete-orphan | 表示当对一个ORM对象解除了父表中的关联对象的时候,自己便会被删除掉。当然如果表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对一上。并且还需要在子模型中的relationship中,增加一个single_parent=True的参数。 |
merge | 默认选项。当在使用session.merge,合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操作 |
expunge | 移除操作的时候,会将相关联的对象也进行移除。这个操作只是从session中移除,并不会真正的从数据库中删除。 |
all | 是对save-update,merge,refresh-expire,expunge,delete几种的填写 |
比如:
articles = relationship("Article",cascade="save-update,delete")
来源:点击这里
4.2.6 查(不跨表)
对于普通的单表查找知道在哪里写好,只好这这里记录了。
假如已经import了User表
命令 | 说明 |
---|---|
User.query.all() | 查询所有用户数据 |
User.query.count() | 查询有多少个用户 |
User.query.first() | 查询第1个用户 |
User.query.get(1) | 根据id查询 |
User.query.filter_by(id=1).all() | 根据id查询, 简单查询, 使用关键字实参的形式来设置字段名 |
User.query.filter(User.id == 1).all() | 根据id查询,复杂查询,使用恒等式等其他形式来设置条件 |
User.query.filter(User.name.startswith("l")).all() | 开头 |
User.query.filter(User.name.endswith("x")).all() | 结尾 |
User.query.filter(User.name.contains("lcz")).all() | 包含 |
User.query.filter(User.name.like("%cz%")).all() | 模糊查询,点击查看多关键字过滤查询 |
User.query.filter(not_(User.name == "lczmx")).all() | 取反查询 |
User.query.filter(User.name != "lczmx").all() | 取反查询 |
User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all() | 查询id为[1, 3, 5, 7, 9]的用户 |
User.query.group_by(User.role_id).all() | 分组查询 |
User.query.order_by(User.role_id).all() | 排序 |
.distinct() | 去重 |
.limit(n) | 取结果的前n个 |
.offset(n) | 跳过前n个 |
pn = User.query.paginate((page,per_page,False) | 分页查询,age:哪一页 per_page:每页多少条,False:查询不到不报错。pn.items 获取该页的数据 pn.page 获取当前的页码 pn.pages 获取总页数 |
这篇文章写得很好
注:和django的ORM一样.all()的是一个由query对象组成的列表,.get()和.first()以及.last()都是query对象
4.3 一对一关系
一对一需要设置relationship中的uselist=Flase,其他数据库操作一样。
4.4 多对多关系
如果你想要用多对多关系,你需要定义一个用于关系的辅助表。对于这个辅助表, 强烈建议 不 使用模型,而是采用一个实际的表
4.4.1 建立多对多关系
使用db.Table
student2course = db.Table('student2course', db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
db.Column('course_id', db.Integer, db.ForeignKey('course.id')))
class Student(db.Model):
__tablename__ = 'student'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(12))
course = db.relationship('Course', secondary=student2course)
def __init__(self, name):
self.name = name
def __repr__(self):
return "name:%r" % self.name
class Course(db.Model):
___tablename__ = 'course'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), unique=True)
def __init__(self, name):
self.name = name
def __repr__(self):
return "name:%r" % self.name
注:添加多对多的反向引用,必须使用secondary指定中间关联表
4.4.2 多对多——增
@app.route("/add")
def add():
stu1 = Student("卢来佛祖")
stu2 = Student("乔碧萝")
cou1 = Course("语文")
cou2 = Course("数学")
cou3 = Course("英语")
stu1.course = [cou1, cou2]
stu2.course = [cou1, cou2, cou3]
db.session.add_all([stu1, stu2])
db.session.commit()
return "ok"
可以发现,使用的就是一对多的方法,实际上多对多就是两个一对多
另一种情况
假如为已经在表中的数据添加关系
@app.route("/update")
def update():
cou1 = Course.query.filter_by(id=2).first()
stu1 = Student.query.get(1)
stu1.course.append(cou1)
db.session.add(stu1)
db.session.commit()
return "ok"
4.4.3 多对多——删
@app.route("/delete")
def delete():
cou = Course.query.get(2)
stu = Student.query.get(1)
stu.course.remove(cou)
db.session.commit()
return "ok"
假如使用Student.query.filter(Student.id == 2).first().course.remove(cou),这样一条代码写完的话会报错,不知道为什么
报错提示:sqlalchemy.orm.exc.ObjectDereferencedError: Can't emit change event for attribute 'Student.course' - parent object of typehas been garbage collected.
5 db的方法总结
方法 | 说明 |
---|---|
.create_all() | 创建所有表 |
.drop_all() | 删除所有表 |
.init_app(app) | 初始化应用程序 |