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 = https://www.cnblogs.com/lczmx/p/{
"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框架,
點擊查看Flask-SQLAlchemy的API
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) | 初始化應用程式 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/162035.html
標籤:Python
上一篇:利用Python獲取檔案型別
