主頁 > 後端開發 > Python-Flask

Python-Flask

2020-10-07 23:36:10 後端開發

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 type has been garbage collected.

5 db的方法總結

方法 說明
.create_all() 創建所有表
.drop_all() 洗掉所有表
.init_app(app) 初始化應用程式

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/162035.html

標籤:Python

上一篇:利用Python獲取檔案型別

下一篇:[python第一課]初識python

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more