1. 程式人生 > >Python自動化開發學習12-MariaDB

Python自動化開發學習12-MariaDB

運行 fault 學員 2個 echo 一致性 並且 class類 相同

關系型數據庫

主流的關系型數據庫大概有下面這些:

  • Oracle : 甲骨文公司的企業級的數據庫
  • SQL Server : 微軟的
  • MySQL : 免費的數據庫,現在也屬於Oracle的旗下產品
  • MariaDB : 開源的數據庫,MySQL的一個分支
  • PostgreSQL : 也是開源的
  • SQLite : 一款輕量級的數據庫
  • DB2 : IBM的

RDBMS 術語

RDBMS(Relational Database Management System)即關系數據庫管理系統,在開始之前,先了解下RDBMS的一些術語:

  • 數據庫: 數據庫是一些關聯表的集合。.
  • 數據表: 表是數據的矩陣。在一個數據庫中的表看起來像一個簡單的電子表格。
  • 列: 一列(數據元素) 包含了相同的數據,例如郵政編碼的數據。
  • 行: 一行(=元組,或記錄)是一組相關的數據,例如一條用戶訂閱的數據。
  • 冗余: 存儲兩倍數據,冗余可以使系統速度更快。(表的規範化程度越高,表與表之間的關系就越多;查詢時可能經常需要在多個表之間進行連接查詢;而進行連接操作會降低查詢速度。例如,學生的信息存儲在student表中,院系信息存儲在department表中。通過student表中的dept_id字段與department表建立關聯關系。如果要查詢一個學生所在系的名稱,必須從student表中查找學生所在院系的編號(dept_id),然後根據這個編號去department查找系的名稱。如果經常需要進行這個操作時,連接查詢會浪費很多的時間。因此可以在student表中增加一個冗余字段dept_name,該字段用來存儲學生所在院系的名稱。這樣就不用每次都進行連接操作了。)
  • 主鍵: 主鍵是唯一的。一個數據表中只能包含一個主鍵。你可以使用主鍵來查詢數據。
  • 外鍵: 外鍵用於關聯兩個表。
  • 復合鍵: 復合鍵(組合鍵)將多個列作為一個索引鍵,一般用於復合索引。
  • 索引: 使用索引可快速訪問數據庫表中的特定信息。索引是對數據庫表中一列或多列的值進行排序的一種結構。類似於書籍的目錄。
  • 參照完整性: 參照的完整性要求關系中不允許引用不存在的實體。與實體完整性是關系模型必須滿足的完整性約束條件,目的是保證數據的一致性。

MariaDB安裝

直接使用yum來安裝:

$ yum groupinstall mariadb mariadb-client

開啟服務,以及開啟自啟動:

 $ systemctl start mariadb
 $ systemctl enable mariadb

安裝後建議運行一下安全加固:

$ mysql_secure_installation

幾個交互式的問答,可以設置root密碼。其他都可以選yes,主要是刪除匿名用戶登錄,關閉root賬號遠程登錄,刪除test庫。
如果需要被遠程訪問,還要開啟防火墻:

$ firewall-cmd --permanent --add-service=mysql
$ firewall-cmd --reload

配置文件

/etc/my.cnf 這個文件就是配置文件,一般情況下,你不需要修改該配置文件。
如果你的數據庫是單機運行的,那麽建議關閉聯網,具體就是添加一行配置:
在 [myslqd] 中加一行, skip-networking=1

基本操作

太具體的例子和語句就不一個一個試了,就在下面列出常用的操作和命令簡單的語法。太復雜的查詢語句還是在需要的時候再上網查吧。

賬號

登錄數據庫:

$ mysql [-u root] [-h localhost] -p[PASSWORD]

註意-p後面可以不跟密碼,這樣可以在之後的交互界面輸入密文的密碼。也可以在-p後面直接跟上明文的密碼,但是中間不要用空格
-u 缺省就是root登錄, -h 缺省就是登錄到localhost
用戶賬戶記錄在mysql庫的user表裏,權限在db表裏。
創建一個用戶,並且設置賬號權限:

> GRANT SELECT, UPDATE, DELETE, INSERT ON 庫名.表名 TO 用戶名@主機 INDENTIFIED BY ‘密碼‘ ;

也可以賦予完全的權限,比如創建一個admin賬號,賦予所有的權限:

> GRANT ALL PRIVILEGES ON *.* TO admin IDENTIFIED BY ‘admin123‘;

賬號權限有很多,最常用的就是增刪改查的操作,所有的權限可以看db表:

> USE mysql
> DESC db;

查看賬號權限:

> SHOW GRANTS [for 用戶名@主機];

查看有多少賬號:

> SELECT user, host FROM user;
> SELECT * FROM user \G;  # 或者查看全部,不過內容比較多,用\G參數按列打印

刪除賬戶:

DEOP USER 用戶名@主機;

最後註意,賬號的設置不會馬上生效。重啟一下服務最保險,或者:

> FLUSH PRIVILEGES;

命令

以下列出了使用Mysql數據庫過程中常用的命令:

  • USE 數據庫名 : 選擇要操作的Mysql數據庫,使用該命令後所有Mysql命令都只針對該數據庫。
  • SHOW DATABASES : 列出 MySQL 數據庫管理系統的數據庫列表。
  • SHOW TABLES : 顯示指定數據庫的所有表,使用該命令前需要使用 use命令來選擇要操作的數據庫。
  • SHOW COLUMNS FROM 表名 : 顯示數據表的屬性,屬性類型,主鍵信息 ,是否為 NULL,默認值等其他信息。
  • DESC 表名 : 同上,貌似一般都用這個
  • CREATE DATABASE 庫名 CHARSET "utf8" : 創建一個支持中文的數據庫
  • SHOW CREATE DATABASE 庫名 : 上面創建後,查看這個庫的字符編碼。默認是‘latin1‘。
  • DROP DATABASE 庫名 : 刪除數據庫
  • SHOW INDEX FROM 表名 : 顯示數據表的詳細索引信息,包括PRIMARY KEY(主鍵)。

操作

創建表,然後進行增刪改查的操作,簡單列一下:

  • 創建表 : CREATE TABLE 表名 (表結構,主鍵);
  • 插入數據 : INSERT INTO 表名 (字段名列表) VALUES (值的列表);
  • 查詢數據 : SELECT 字段名 FROM 表名;
  • 修改數據 : UPDATE 表名 SET 字段名1=值[, 字段名2=值 ...] [WHERE語句];
  • 刪除數據 : DELETE FROM 表名 [WHERE語句]; ,如果沒有WHERE,所有記錄都將被刪除

使用下面的語句,加到SELECT語句後面,設置查詢條件或者輸出額:

  • WHERE : 查詢的條件
  • OFFSET : 開始查詢數據的偏移量,可以不用每次都從頭開始查
  • LIMIT : 設定返回的記錄數
  • DESC : 查詢結果降序排列,默認是升序(ASC,ASC默認缺省,加不加都一樣)
  • GROUP BY : 將數據進行分組

ALTER命令,修改數據表名或者修改數據表字段使用的命令。

  • 刪除字段 : ALTER TABLE 表名 DROP 字段名;
  • 添加字段 : ALTER TABLE 表名 ADD 字段名 字段類型; ,新字段添加在表的末尾。
    • 加到開頭:最後再加上FIRST
    • 加到指定位置:最後加上AFTER 字段名,就是插入到指定字段名的後面。
    • FIRST 和 AFTER 關鍵字只作用於 ADD 子句。所以無法調整字段位置,或先 DROP 刪除然後再 ADD 添加並設置位置
  • 修改字段類型 : ALTER TABLE 表名 MODIFY 字段名 字段新類型;
    • ALTER TABLE 表名 MODIFY 字段名 BIGINT NOT NULL DEFAULT 100; , 設定字段類型為BIGINT,並且不能為空默認值100。如果不設置默認值,則自動設置該字段默認為 NULL。
  • 修改字段名及類型 : ALTER TABLE 表名 CHANGE 舊字段名 新字段名 字段新類型;
  • 修改字段默認值 : ALTER TABLE 表名 ALTER 字段名 SET DEFAULT 默認值;
  • 刪除字段默認值 : ALTER TABLE 表名 ALTER 字段名 DROP DEFAULT;
  • 修改表名 : ALTER TABLE 表名 RENAME TO 新表名;

ALTER 命令不只上面這些,還可以用來創建及刪除數據表的索引,先這樣吧。

外鍵關聯

先準備好數據,順便復習前面的內容:

> CREATE DATABASE week12 CHARSET utf8;  # 創建數據庫
> USE week12

按照下面的表,創建表格
學生信息表(student):

id name age
1 Adam 36
2 Bob 32
3 Clare 27
4 Dan 26
> CREATE TABLE student (
    -> id INT UNSIGNED AUTO_INCREMENT,
    -> name VARCHAR(20) NOT NULL,
    -> age TINYINT,
    -> PRIMARY KEY (id)
    -> );

然後插入數據:

> INSERT INTO student (name, age) VALUES (‘Adam‘, 36);

再創建下面的這張考勤表。考勤表中的 student_id 要和學生信息表這的 id 建立外鍵關聯。
考勤表(record):

day student_id checkin late level_early
2018-01-01 1 1 0 0
2018-01-01 2 1 0 0
2018-01-01 3 1 0 0
2018-01-02 1 1 0 0
2018-01-02 2 1 0 0
2018-01-02 3 1 0 0
> CREATE TABLE record(
    -> day DATE,
    -> student_id INT UNSIGNED,
    -> checkin BOOL,
    -> late BOOL,
    -> level_early BOOL,
    -> PRIMARY KEY (day, student_id),
    -> KEY fk_student_key (studeng_id),
    -> CONSTRAINT fk_student_key FOREIGN KEY (student_id) REFERENCES studnet (id)
    -> );

嘗試添加記錄:

> INSERT INTO record (day, student_id, checkin, late, level_early) VALUES (‘2018-1-2‘, 3, 1 ,0, 0);

record表的主鍵是 (day, student_id) ,這是一個復合主鍵。所以日期和id都可以重復出現,但是同一日期不下不能由相同的id。
無法在record表中插入在student表中不存在的student_id,這個叫外鍵約束
嘗試刪除記錄:

> DELETE FROM record WHERE day=‘2018-01-01‘ AND student_id=1;  # 這條沒問題
> DELETE FROM student WHERE name LIKE ‘Adam‘;  # 這條數據如果被關聯了,就無法刪除。

查詢表的外鍵關聯,通過查看建表的語句就能看到外鍵的SQL語句
> SHOW CREATE TABLE record;
然後被關聯的表可以用下面的語句查詢到關聯關系
> select * from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where REFERENCED_TABLE_NAME=‘student‘;

NULL 值處理

我們已經知道數據庫使用 SELECT 命令及 WHERE 子句來讀取數據表中的數據,但是當提供的查詢條件字段為 NULL 時,該命令可能就無法正常工作。
關於 NULL 的條件比較運算是比較特殊的。你不能使用 = NULL 或 != NULL 在列中查找 NULL 值 。用下面的 IS NULL 和 IS NOT NULL。NULL值與任何其它值的比較(即使是NULL)永遠返回false,即 NULL = NULL 也返回 false 。
為了處理這種情況,使用如下的三大運算符:

  • IS NULL : 當列的值是NULL,此運算符返回true。
  • IS NOT NULL : 當列的值不為NULL, 運算符返回true。
  • <=> : 比較操作符(不同於=運算符),當比較的的兩個值為NULL時返回true。

多表查詢

上面例子中的2個表,要輸出一張考勤表,但是考勤表中沒有name字段。想要name字段需要根據student_id到student表中查找對應的id獲取。這就需要多表聯合查詢

> SELECT * FROM record, student WHERE record.student_id = student.id;  # 也可以使用JOIN方法

或者也可以使用JOIN。另外只需要從student表中取到name字段,別的字段不需要。SELECT * 也可以修改一下:

> SELECT record.*, student.name FROM record, student WHERE record.student_id = student.id;
> SELECT record.*, student.name FROM record JOIN student ON record.student_id = student.id;

上面的2句一樣。
另外JOIN其實分4種類:

  • INNER JOIN(內連接,或等值連接):獲取兩個表中字段匹配關系的記錄。默認缺省 INNER 就是這個。
  • LEFT JOIN(左連接):獲取左表所有記錄,即使右表沒有對應匹配的記錄。
  • RIGHT JOIN(右連接): 與 LEFT JOIN 相反,用於獲取右表所有記錄,即使左表沒有對應匹配的記錄。
  • FULL JOIN : 沒有這句命令,不直接支持,但是可以實現

技術分享圖片技術分享圖片技術分享圖片

多表聯查的2張表不需要有外鍵關聯。由於上面建立的2張表建立了外鍵關聯,record表中的student_id一定是在student表中的,所以上面 JOIN 語句使用 LEFT 是不會有更多記錄的。但是使用 RIGHT,會把record表中沒有記錄的student的name也生成一條記錄。

SELECT record.*,student.name FROM record RIGHT JOIN student ON record.student_id = student.id;

間接實現FULL JOIN的方法就是做 LEFT JOIN 和 RIGHT JOIN 各做一次,然後把結果拼起來就是了:

> SELECT record.*,student.name FROM record LEFT JOIN student ON record.student_id = student.id 
    -> UNION
    -> SELECT record.*,student.name FROM record RIGHT JOIN student ON record.student_id = student.id;

FULL JOIN 知道就行了,因為貌似也沒啥用。

事務

事務主要用於處理操作量大,復雜度高的數據。比如說,在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務。再比如上面的例子,你如果要刪除一個學生,還需要先刪除這個學生的考勤記錄,這就是2個步驟。我們希望這2個步驟可以都完成。如果完成了考勤記錄的刪除,但是之後刪除學生的時候出現了問題,那麽可以會退到整個刪除過程之前的狀態,既恢復之前刪除的考勤記錄。直白一點,就是一列的操作,所有的步驟要麽都成功,要麽一個都不執行。

  • 只有使用了Innodb數據庫引擎的數據庫或表才支持事務,默認使用的數據庫引擎就是Innodb。
  • 事務處理可以用來維護數據庫的完整性,保證成批的SQL語句要麽全部執行,要麽全部不執行
  • 事務用來管理 INSERT, UPDATE, DELETE 語句。沒有 SELECT 因為並不會對表進行修改

一般來說,事務需要滿足4個條件(ACID):

  1. 原子性 : 一組事務,要麽成功;要麽撤回。
  2. 穩定性 : 有非法數據(外鍵約束之類),事務撤回。
  3. 隔離性 : 事務獨立運行。一個事務處理後的結果,影響了其他事務,那麽其他事務會撤回。事務的100%隔離,需要犧牲速度。
  4. 可靠性 : 軟、硬件崩潰後,InnoDB數據表驅動會利用日誌文件重構修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 選項決定什麽時候吧事務保存到日誌裏。

操作起來很簡單:

> BEGIN;  # 聲明開始一個事務
> INSERT INTO student (name, age) VALUES (‘Frank‘, 18);  # 執行一些操作,這裏就插入一條記錄
> ROLLBACK;  # 回滾,如果數據是不會寫入的,回到初始得狀態
> COMMIT; # 提交,如果數據沒有問題就執行提交而不是回滾

另外如果步驟比較多還可以設置多個臨時保存點,可以進行回滾:
保存點(Savepoint) : 事務集中的一個臨時占位符,可進行回滾。

> SAVEPOINT delete1;  # 設置保存點
> ROLLBACK TO delete1;  # 回滾到保存點

索引

索引的建立對於數據庫的高效運行是很重要的,索引可以大大提高數據的檢索速度。
索引分單列索引和組合索引。單列索引,即一個索引只包含單個列,一個表可以有多個單列索引,但這不是組合索引。組合索引,即一個索包含多個列。
上面是使用索引的好處,但過多的使用索引將會造成濫用。因此索引也會有它的缺點:雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,不僅要保存數據,還要保存一下索引文件。建立索引會占用磁盤空間的索引文件。
查看索引:

> SHOW INDEX FROM student [\G];  # 看不清楚,就加上\G

即時還沒有創建過索引,但是依然能查看到索引信息。因為默認已經對主鍵做了索引了。
創建索引:

> CREATE INDEX index_name ON student(name(5));  # 創建單列索引,長度可以缺省
> CREATE INDEX index_name_age ON student (name,age);  # 創建聯合索引,這裏缺省了長度

索引也是一張表,所以要取一個索引名(‘index_name’)。然後要指定一下長度(例子中是5,也可以缺省)。如果是CHAR,VARCHAR類型,長度可以小於字段實際長度(或者不寫);如果是BLOB和TEXT類型,必須指定長度。
刪除索引:

> DROP INDEX index_name ON student;  # 刪除索引

用ALTER添加、刪除索引:

> ALTER TABLE student ADD INDEX index_name(name);  # 這裏就缺省了長度,也可以加上
> ALTER TABLE student DROP INDEX index_name;  # 刪除索引

另外,在創建表的時候也可以指定索引。

唯一索引

它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。要創建唯一索引,只需要加上UNIQUE這個關鍵字就好了:

> CREATE UNIQUE INDEX index_name ON student(name(10));  # 加上UNIQUE
> ALTER TABLE student ADD UNIQUE [INDEX] index_name(name(10));  # 這裏可以缺省INDES

PyMySql 模塊

這是一個第三方庫,需要安裝。使用的時候基本都是用源生SQL語句來操作數據庫。
連接查詢數據庫:

import pymysql

conn = pymysql.connect(host=‘192.168.246.134‘, port=3306,
                       user=‘operator‘, passwd=‘operator123‘,
                       db=‘week12‘)  # 創建連接
cursor = conn.cursor()  # 創建遊標
effect_row = cursor.execute(‘SELECT * FROM student‘)  # 執行SQL語句
print(effect_row)  # 返回值是受影響的行數
print(cursor.fetchone())  # 獲取1條
print(cursor.fetchmany(2))  # 獲取多條
print(cursor.fetchall())  # 獲取所有
cursor.close()  # 關閉遊標
conn.close()  # 關閉連接

這裏執行SQL命令的方法excute,有2個參數。第一個是SQL語句的字符串。第二個參數上面是缺省的。
插入數據:

import pymysql

conn = pymysql.connect(host=‘192.168.246.134‘, port=3306,
                       user=‘operator‘, passwd=‘operator123‘,
                       db=‘week12‘)  # 創建連接
cursor = conn.cursor()  # 創建遊標
effect_row = cursor.execute("INSERT INTO student (name, age) "
                            "VALUES (‘Gina‘, 20)")  # 執行SQL語句
effect_row = cursor.execute("INSERT INTO student (name, age) VALUES (%s, %s)",
                            (‘Helena‘, 21))  # 變量可以作為第二個參數寫成一個元組
print(effect_row)  # 一次插入1行,所以返回值是1
conn.commit()  # 必須提交,默認都是事務操作
cursor.close()  # 關閉遊標
conn.close()  # 關閉連接

這裏註意,默認所有的修改操作都是事務,所以執行後得提交,否則不會生效。
還可以一次插入多條數據,用 executemany 執行多條:

import pymysql

conn = pymysql.connect(host=‘192.168.246.134‘, port=3306,
                       user=‘operator‘, passwd=‘operator123‘,
                       db=‘week12‘)  # 創建連接
cursor = conn.cursor()  # 創建遊標
student_list = [(‘Ivy‘, 21), (‘Jimmy‘, 22), (‘Kane‘, 23)]  # 數據的列表
effect_row = cursor.executemany("INSERT INTO student (name, age) VALUES (%s, %s)",
                                student_list)  # 把列表直接作為第二個參數
print(effect_row)  # 一次插入3行,所以返回值是3
conn.commit()  # 必須提交,默認都是事務操作
cursor.close()  # 關閉遊標
conn.close()  # 關閉連接

SQLAlchemy 模塊

現在已經可以使用SQL語句通過python來操作數據庫了。但是我並不是專業的DBA,使用SQL語句並不熟練(復雜點的語句可能寫出來,根本不能執行)。我還需要更高級的封裝。

ORM介紹

全稱object relational mapping,就是對象映射關系程序,簡單來說我們類似python這種面向對象的程序來說一切皆對象,但是我們使用的數據庫卻都是關系型的,為了保證一致的使用習慣,通過orm將編程語言的對象模型和數據庫的關系模型建立映射關系,這樣我們在使用編程語言對數據庫進行操作的時候可以直接使用編程語言的對象模型進行操作就可以了,而不用直接使用sql語言。
ORM的優點:

  • 隱藏了數據訪問細節,“封閉”的通用數據庫交互是ORM的核心。他使得我們與通用數據庫交互變得簡單易行,並且完全不用考慮該死的SQL語句。快速開發,由此而來。
  • ORM使我們構造固化數據結構變得簡單易行。

ORM的缺點:

  • 無可避免的,自動化意味著映射和關聯管理,代價是犧牲性能(早期,這是所有不喜歡ORM人的共同點)。現在的各種ORM框架都在嘗試使用各種方法來減輕這塊(LazyLoad,Cache),效果還是很顯著的。

SQLAlchemy 操作數據庫

首先,這也是一個第三方庫,使用前需要安裝。
在Python中,最有名的ORM框架是SQLAlchemy。該框架建立在數據庫API之上,使用關系對象映射進行數據庫操作,簡言之便是:將對象轉換成SQL,然後使用數據API執行SQL並獲取執行結果。
SQLAlchemy本身無法操作數據庫,其必須通過pymsql等第三方插件,根據配置文件的不同調用不同的數據庫API,從而實現對數據庫的操作,如:
mysql 通過 PyMySQL`mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]`
br/>`mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]`
br/>`mssql+pymssql://<username>:<password>@<freetds_name>/?charset=utf8`
br/>`oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]`
如此,我們只要通過ORM,就可以操作任何他支持的數據庫了。並且可以把數據庫當做我們的數據對象來處理,而不需要了解數據庫本身的語句。

創建表

創建一張表:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)  # 這裏設置了echo參數,顯示中間過程和SQL語句

Base = declarative_base()  # 生成orm基類
class User(Base):
    __tablename__ = ‘user‘  # 表名
    id = Column(Integer, primary_key=True)
    name = Column(String(32))
    password = Column(String(64))

Base.metadata.create_all(engine)  # 創建表結構,這裏是通過父類來調用子類

註意一下賬號權限,root賬號默認是只能本地登錄了,最好也不要開放給遠程。確保你使用的賬號有遠程登錄的權限(如果你不是本地登錄操作的話)。另外確保你的賬號有創建表的權限(一般操作用的賬號只分配增刪改查的權限就好了)。
設置了echo參數,會打印很多額外的信息,使用的時候可以關閉去掉這個參數。
創建成功後重復運行不會再創建或者覆蓋,也不會報錯。

插入數據

要插入數據,前面創建表的整段代碼都要抄下來。先是連接數據庫,然後是聲明表結構一句都不能少。除了最後一句create_all可以不寫(寫上也沒事,這句是創建表,但是表已經存在的情況下,不會創建也不會報錯)
暫時不要用中文,使用中文的方法在最後
插入數據:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)  # 這裏設置了echo參數,顯示中間過程和SQL語句
Base = declarative_base()  # 生成orm基類
class User(Base):
    __tablename__ = ‘user‘  # 表名
    id = Column(Integer, primary_key=True)
    name = Column(String(32))
    password = Column(String(64))
Base.metadata.create_all(engine)  # 可寫可不寫。寫上,如果該表不存在就創建

# 上面是連接數據庫和聲明表結構
# 下面是插入數據
Session_class = sessionmaker(bind=engine)  # 創建與數據庫的會話session class ,註意,這裏返回給session的是個class,不是實例
session = Session_class()  # 生成session實例

user_obj = User(name="Jerry", password="jerrypass")  # 生成你要創建的數據對象
session.add(user_obj)  # 把要創建的數據對象添加到這個session裏, 一會統一創建

session.commit()  # 現此才統一提交,創建數據

上面的例子中,先生成一個Session實例,然後通過操作這個實例來插入數據。增刪改查的操作,都是同個這個Session來完成的。
這裏用的是 add(obj) ,還可以使用 add_all(list) ,來加入一組數據。參數是列表,列表中的每一個元素是需要插入的一條數據。

查詢數據

查詢數據:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker

engine = create_engine("mysql+pymysql://operator:[email protected]/week12",
                       encoding=‘utf-8‘, echo=False)  # 這裏設置了echo參數,顯示中間過程和SQL語句
Base = declarative_base()  # 生成orm基類
# 這裏我把表換成了之前創建的student表,裏面有之前創建的數據
# id字段的主鍵必須要聲明,否則會報錯。
class Student(Base):
    __tablename__ = ‘student‘  # 表名
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer)
Base.metadata.create_all(engine)  # 可寫可不寫。寫上,如果該表不存在就創建
Session_class = sessionmaker(bind=engine)  # 創建與數據庫的會話session class ,註意,這裏返回給session的是個class,不是實例
session = Session_class()  # 生成session實例
# 上面都一樣
data = session.query(Student).filter_by(id=3).all()  # 如果去掉filter_by括號中的內容,就是查詢所有
print(data)  # 目前打印結果只是一個對象

上面例子中使用了 .all() 輸出所有,也可以使用 .first() 只輸出一條。
查詢條件有 ‘filter_by()‘ 和 ‘filter()‘ 。如果參數為空,就是查詢所有,2種語法沒有差別。
詳細講一下 filter() ,使用SQL表達式。
單個條件, filter(Student.name == ‘Bob‘)
多個條件,用逗號隔開或者寫多個filter串起來,都是AND的意思 : filter(Student.id &lt; 6, Student.age &gt; 30) 或者 filter(Student.id &lt; 6).filter(Student.age &gt; 30)
多個條件使用OR:

from sqlalchemy import or_  # 需要導入這個
filter(or_(Student.id == 1, Student.age < 20))

使用in匹配 : filter(Student.name.in_([‘Bob‘, ‘Eve‘]))filter(~Student.name.in_([‘Bob‘, ‘Eve‘])) 前面加個~是not in。
使用like匹配 : filter(Student.name.like(‘%y‘))
最終極的辦法就是用原生SQL的語法了:

from sqlalchemy import text  # 需要導入這個
data = session.query(User).filter(text("id > 1 and name Like ‘%m%‘")).all()  # 然後就按原生的語法那麽寫

filter_by() : 使用關鍵參數表達式。filter_by(name=‘Bob‘)filter_by(name=‘Bob‘, id=3)。貌似查不了多條,只能用等於,沒細講。
差不多了,更多的情況,用到了再查吧。

打印查詢結果

上面打印出來得只是對象,並不是表的內容。既然有對象了,只需要用操作對象的方法就好了。
查詢到的記錄數量,可以通過 len(data) 獲取到。要打印結果需要重構類的 __repr__ 方法。

class Student(Base):
    __tablename__ = ‘student‘  # 表名
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer)
    def __repr__(self):
        return "<Student(id=‘%s‘, name=‘%s‘, age=‘%s‘)>" % (
            self.id, self.name, self.age)
data = session.query(Student).filter(Student.name.like(‘%y‘)).all()  # 如果去掉filter_by括號中的內容,就是查詢所有
print(len(data))  # 返回的記錄數量
print(data)  # 打印所有的數據

還可以用取對象屬性的方法打印出表的內容,

data = session.query(Student).filter(Student.name.like(‘Bob‘)).all()  # 用all返回所有,data就是所有對象的列表
print(type(data), len(data))  # 返回的記錄數量,這裏的data類型是列表,data[0]才是對象
print(data[0].id, data[0].name, data[0].age)  # 如果是多條,我們可以寫for循環
data = session.query(Student).filter(Student.name.like(‘Bob‘)).first()  # 用first只返回第一條。data就是對象
print(type(data))  # 這裏data是對象,沒有len
print(data.id, data.name, data.age)  # 直接打印data的屬性
print(data.__dict__)  # 既然是對象,我們可以打印它所有的屬性值

修改數據

修改數據:
直接操作對象,給對象賦值就完成了數據的修改,最後調用commit()寫入。

data = session.query(Student).filter(Student.name.like(‘Dan‘)).first()  # 用first只返回第一條。data就是對象
print(data.id, data.name, data.age)  # 打印看看
data.name = ‘Dennis‘  # 這裏就當做普通對象一樣操作
data.age += 1  # 我們來加1歲
session.commit()  # 最後要提交才生效

上面的方法只能改1條,你用個all(),然後也能修改多條。
另外還可以用update修改,用法如下:

data = session.query(Student).filter(Student.name == ‘Dennis‘).update(
    {Student.name: ‘David‘, Student.age: Student.age+1})  # update用字典的形式賦值
session.commit()
print(data)  # 返回值是修改的記錄數

刪除數據

直接像上面的update方法那樣,調用一個delete方法。因為是刪除,所以delete()就好了,不需要參數

data = session.query(Student).filter(Student.name == ‘David‘).delete()  # 直接改成delete即可
session.commit()
print(data)  # 返回值是修改的記錄數

回滾

模塊所有的修改操作都是通過事務來執行的,之前每次操作完成後,都需要加上commit()執行一下提交。在提交之前,也可以使用rollback()執行回滾。通過自增id的變化,印證了是通過事務來實現的。

data = session.query(Student).filter().all()  # 其實用降序排列,取第一條就可以了。現在降序還不會
print(data[len(data)-1].id)  # 這個是最後一條記錄的id
obj = Student(name=‘Dan‘, age=29)
session.add(obj)  # 插入數據
data = session.query(Student).filter(Student.name == ‘Dan‘).first()
print(data.id, data.name)  # 註意這個id,所以這個id已經生成並且並用掉了
session.rollback()  # 不提交而是回滾
data = session.query(Student).filter(Student.name == ‘Dan‘).first()
print(data)  # 此時為None,添加的數據沒了
obj = Student(name=‘Dennis‘, age=30)
session.add(obj)  # 再插入數據
session.commit()  # 這次提交
data = Session.query(Student).filter().all()
print(data[len(data)-1].id, data[len(data)-1].name)  # 再看看新記錄的id

SQLAlchemy 進階操作

試完了增刪改查的基本操作後,看看一些別的操作。

分組和統計

統計用 count

data = session.query(Student).filter(Student.name.like(‘%y‘)).all()  # 用all返回所有,data就是所有對象的列表
print(data, len(data))
count = session.query(Student).filter(Student.name.like(‘%y‘)).count()  # 使用count方法實現統計
print(count)  # 這個還是滿足條件的記錄的 數量,意義貌似不大

分組用 group_by

from sqlalchemy import func  # 這裏的統計需要導入這個
data = session.query(func.count(Student.age), Student.age, func.sum(Student.age)).group_by(Student.age).all()
print(data)  # query裏的參數,就是輸出的元祖的每一個元素,其中func.count是記錄的數量。上面還試了一個sum

對query參數的理解

到這裏對query有了新的認識。query裏的參數,就是輸出的內容。之前的參數都是類名,結果就是一個對象。這裏直接把屬性和方法放到query中,就直接獲取到屬性和方法的值了。

class Student(Base):
    __tablename__ = ‘student‘  # 表名
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer)
# 上面我沒有重構__repr__方法
data = session.query(Student.id, Student.name, Student.age).filter().first()  # 用all的區別就是所有的元素再組成一個列表
print(data)  # 現在data的內容就是query中定義的,是一個元祖

對表的class類的理解

這裏主要是class裏的每個字段的類型,
任何時候主鍵的聲明都不能缺省
創建表的時候需要詳細的寫明類型包括大小,
查詢的時候只需要聲明主鍵,類型可以缺省,全部用 Column()
插入表的時候,也要寫明類型,否則ORM不知道這個字段是數字還是字符串。但是不寫大小是可以的
總結,所以創建表的時候對類的要求是最嚴的。實際使用的時候,在創建表的時候把類定義好(即使表已經存在也定義一下),其他操作的時候直接import這個類就好了。如果不是使用本系統的表,而是使用其他系統的表,那就只是查詢,只需要知道字段名就好了,用 Column()

多表查詢

下面是SQL中的JOIN語句,這裏SELECT * 就好了,我們可以用代碼實現輸出內容的篩選。
&gt; SELECT * FROM record JOIN student ON record.student_id = student.id;
對應的python代碼:

class Student(Base):
    __tablename__ = ‘student‘  # 表名
    id = Column(primary_key=True)  # 只要聲明你需要的字段名,主鍵必須聲明
    name = Column()  # 字段類型可以不要,我們不是創建表
    # age = Column()  # 不需要的字段也可以不要
class Record(Base):
    __tablename__ = ‘record‘
    day = Column(primary_key=True)
    student_id = Column(primary_key=True)
    checkin = Column
    late = Column
    level_early = Column
    def __repr__(self):
        return "<Record(day=‘%r‘, checkin=‘%r‘, late=‘%r‘, level_early=‘%r‘)>" % (
            self.day, bool(self.checkin), bool(self.late), bool(self.level_early))
data = session.query(Record, Student).filter(Record.student_id == Student.id).all()  # 這句就是SQL的JOIN
print(data[0][1].name, data[0][0])

上面是不需要任何關聯關系的時候可以使用的方法。另外還有個join方法,需要有外鍵關聯。先往下看。

外鍵關聯

關聯關系主要分三種情況:一對一、一對多/多對一、多對多

一對一

創建外鍵關聯需要導入 from sqlalchemy import ForeignKey
下面是創建被關聯的表的時候用的SQL語句:

> CREATE TABLE student (
    -> id INT UNSIGNED AUTO_INCREMENT,
    -> name VARCHAR(20) NOT NULL,
    -> age TINYINT,
    -> PRIMARY KEY (id)
    -> );

再創建一張表考試分數的表,關聯id:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey  # 外鍵關聯需要這個
from sqlalchemy.dialects.mysql import INTEGER  # 要使用無符號的整數

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)
Base = declarative_base()  # 生成orm基類

class Student(Base):
    __tablename__ = ‘student‘  # 表名,這張表不創建,可以寫的簡單點
    id = Column(primary_key=True)  # 只要聲明你需要的字段名,主鍵必須聲明
    name = Column()  # 字段類型可以不要,我們不是創建表
    age = Column()

class Exam(Base):
    __tablename__ = ‘exam‘
    name = Column(String(32), primary_key=True)
    student_id = Column(INTEGER(unsigned=True), ForeignKey("student.id"), primary_key=True)  # 聲明外鍵關聯
    score = Column(Integer, nullable=False)  # 規定不能為空
Base.metadata.create_all(engine)  # 創建表

上面踩了個坑。要建立關聯,需要保證被關聯的字段類型和長度是一樣的。student表創建時用了無符號的數字這個數據類型,所以創建的新表的類型也得一致,要使用這個類型就得導入 from sqlalchemy.dialects.mysql import INTEGER 。這個類型就是無符號的數字類型。數據類型一致後成功創建了包含外鍵關聯的新表。
只是創建還不夠,我們還要使用。上面的Exam類中少寫了一行代碼。通過relationship,聲明關聯的表之間的關系,並且可以通過這個關系互相調用被關聯的表的屬性值。這個relationship也需要再導入模塊。
首先,先確保我們新創建的Exam表中有數據:

name student_id score
test1 1 94
test2 1 92

現在可以通過建立的關聯,查詢考試的成績,把student_Id通過關聯從student表中獲取到name。
另外還可以通過student表中的name,查詢這個學生所有考試的成績:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey  # 外鍵關聯需要這個
from sqlalchemy.dialects.mysql import INTEGER  # 要使用無符號的整數
from sqlalchemy.orm import relationship, sessionmaker

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=False)  # 這裏把echo關掉
Base = declarative_base()  # 生成orm基類

class Student(Base):
    __tablename__ = ‘student‘  # 表名,這張表不創建,可以寫的簡單點
    id = Column(INTEGER(unsigned=True), primary_key=True)  # 主鍵必須聲明
    name = Column(String())
    age = Column(Integer)

class Exam(Base):
    __tablename__ = ‘exam‘
    name = Column(String(32), primary_key=True)
    student_id = Column(INTEGER(unsigned=True), ForeignKey("student.id"), primary_key=True)  # 聲明外鍵關聯
    score = Column(Integer, nullable=False)  # 規定不能為空
    student = relationship(‘Student‘, backref=‘exam‘)  # 使用這個,必須要先聲明ForeighKey。註意參數,前面是類名,後面是表名
# Base.metadata.create_all(engine)  # 創建表
session = sessionmaker(bind=engine)()
data = Session.query(Exam).first()
print(data.student.name, data.name, data.score)  # 打印考試成績,把id替換成name
data = Session.query(Student).first()  # 打印一個考生所有考試的成績
for i in data.exam:
    print(data.name, i.name, i.score)

建立了關聯關系後,相當於另一張表的對象就是這張表中的一個屬性。屬性名是對方的表名。
join的多表查詢。建立了關聯關系後,現在可以用了:

# 上面的部分就省了,使用join可以沒有relationship,但是要聲明ForeignKey
session = sessionmaker(bind=engine)()
data = session.query(Exam).join(Student).all()
print(data)
data = session.query(Student).join(Exam).all()
print(data)
data = session.query(Student).join(Exam, isouter=True).all()  # 外連接
print(data)

默認是內連接,加上參數可以是外連接。因為不需聲明了ForeignKey才能使用join,貌似不存在左連接和右連接的問題。有外鍵約束,其中一張表一定是所有的屬性值都被另外一張表包含的。
上面是查詢,還可以通過關聯對象來創建。比如對student表裏的某個同學創建他在exam表裏的考試成績:

# 上面的部分就省了,使用join可以沒有relationship,但是要聲明ForeignKey
session = sessionmaker(bind=engine)()
data = session.query(Student).filter(Student.name == ‘Bob‘).first()  # 通過student表來操作exam表
print(data, data.exam)  # 此時data.exam就是Bob在exam表裏的記錄
data.exam = [Exam(name=‘test1‘, score=88),
             Exam(name=‘test2‘, score=85)]  # 通過對象屬性賦值的方式寫入數據
session.commit()  # 最後記得提交

最後是查詢記錄,打印所有訂單的信息:

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
data = session.query(Order).all()
for i in data:
    # 要獲取關聯的數據,仍然是使用通過relationship創建的名字
    print(i.id, i.name, i.ship_addr_fk.addr, i.bill_addr_fk.addr)

多對一-多外鍵關聯

在這種關系中,A表中的一行只能匹配B表中的一行,但是B表中的一行可能被A表中的多行匹配到,即A表的多行可能匹配的是B表中的同一行。舉例說明:
A表是一張貨物訂單表,4個字段:id(訂單號)、收貨人、收貨地址、收發票的地址。後面2個都是地址,實際情況中可能有需要把貨物發往一處,但是發票需要投遞到另外一處的情況。比如你幫別人買東西、
B表是地址表,2個字段(簡單點):id、地址。A表中的收貨地址和收發票地址記錄的內容就是B表中的對應地址的id。具體地址需要關聯到B表才能查到。
訂單表(order):
這裏不小心用了order這個mysql的關鍵字作為了表名。應該避免這種情況,要麽換個詞,要麽用order_。不過用了也不出錯。但是你用SQL語句的時候可能操作不了這個表。在SQL語句中如果要使用這個表名,請用 `order` ,是Esc下面數字1左邊的那個符號。

id name ship_addr bill_addr
1 Adam 1 2
2 Bob 1 3
3 Cara 4 4

地址表(address):

id addr
1 Beijing
2 Shanghai
3 Guangzhou
4 Shenzhen

首先創建這兩張表:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.mysql import INTEGER  # 要使用無符號的整數
from sqlalchemy import ForeignKey  # 外鍵關聯需要這個
from sqlalchemy.orm import relationship, sessionmaker

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)
Base = declarative_base()

class Order(Base):
    __tablename__ = ‘order‘
    id = Column(INTEGER(unsigned=True), primary_key=True)
    name = Column(String(32))
    ship_addr = Column(INTEGER(unsigned=True), ForeignKey(‘address.id‘))  # 現在是多對一關聯
    bill_addr = Column(INTEGER(unsigned=True), ForeignKey(‘address.id‘))  # 2個的ForeignKey寫的一樣,程序分不清楚
    # ship_addr_fk = relationship(‘Address‘)  # 無法寫backref,因為Address表不知道是哪個addr關聯的它,無法反查的
    # bill_addr_fk = relationship(‘Address‘)  # 當然也不能不寫,否則兩個是一樣的,程序沒有邏輯可以區分
    ship_addr_fk = relationship(‘Address‘, foreign_keys=[ship_addr])  # 這樣就能分清楚是哪個外鍵對應哪個字段了
    bill_addr_fk = relationship(‘Address‘, foreign_keys=[bill_addr])
class Address(Base):
    __tablename__ = ‘address‘
    id = Column(INTEGER(unsigned=True), primary_key=True)
    addr = Column(String(32))

Base.metadata.create_all(engine)  # 創建表

然後來插入數據:

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
addr1 = Address(addr=‘Beijing‘)
addr2 = Address(addr=‘Shanghai‘)
addr3 = Address(addr=‘Guangzhou‘)
addr4 = Address(addr=‘Shenzhen‘)
session.add_all([addr1, addr2, adr3, addr4])  # 可以同時插入多條數據。用列表。
# addr可以使用屬性ship_addr=1來指定。也可以像下面這樣用relationship創建的屬性來調用
order1 = Order(name=‘Adam‘, ship_addr_fk=addr1, bill_addr_fk=addr2)  # 使用關聯創建地址
session.add(order1)
session.commit()

上面是同時創建地址和訂單記錄。也可能是地址已經存在了,那麽就是要用查詢的方法獲取到地址的對象,然後再創建訂單記錄:

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
addr_list = session.query(Address).all()
addr_list.insert(0, ‘‘)  # 在開頭隨便插一個,讓列表第一個元素下標就是1
order2 = Order(name=‘Bob‘, ship_addr_fk=addr_list[1], bill_addr_fk=addr_list[3])  # 使用關聯創建地址
order3 = Order(name=‘Cara‘, ship_addr_fk=addr_list[4], bill_addr_fk=addr_list[4])
session.add_all([order2, order3])
session.commit()

多對多-多對多關聯

這次先說例子:設計一個能描述“圖書”與“作者”的關系的表結構。要求是:一本書可以有好幾個作者,一個作者可以寫好幾本書。
數據庫的字段只能放數據,不能放列表,所以不能是這樣的形式:

書名 作者id
Hamlet 1,2

那麽多個作者字段呢?

書名 作者1 作者2 作者3 作者4 作者5
Hamle 1 2

萬一我有6個作者呢?好吧,以防萬一我留50個作者字段名。邏輯通,但是字段設多了浪費,少了不夠用。這裏的情況主要是字段數量不固定,而且我們甚至不知道字段的上限。書作者的情景下還不明顯,如果線路經過路由的跳數,最短1跳,長的可以30跳,甚至不能保證不會出現上百跳的情況。怎麽辦呢?
在多對多關系中,A表中的一行可以匹配B表中的多行,反之亦然。要創建這種關系,需要定義第三個表,稱為結合表,它的主鍵由A表和B表的外部鍵組成。
書名表(book):

id 書名
1 Hamlet
2 Othello
3 King Lear
4 Macbeth

作者表(author):

id name
1 Adam
2 Bob
3 Cara
4 Dan

結合表(book2author):

book_id author_id
1 1
2 2
2 3
3 2
3 3
3 4
4 4
4 2

數據結構清楚了,首先來創建表:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Table  # 新導入一個Table
from sqlalchemy.dialects.mysql import INTEGER  # 要使用無符號的整數
from sqlalchemy import ForeignKey  # 外鍵關聯需要這個
from sqlalchemy.orm import relationship, sessionmaker

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)
Base = declarative_base()

# 第三張表用另外一種方法來創建
# 沒有用類,也不需要用到類。使用的時候根不需要手動來操作這張表
# 使用的時候根本不用知道這張表的存在,我們只需要維護好另外2張表就好了
book2author = Table(‘book2author‘, Base.metadata,
                    Column(‘book_id‘, INTEGER(unsigned=True), ForeignKey(‘book.id‘)),
                    Column(‘author_id‘, INTEGER(unsigned=True), ForeignKey(‘author.id‘))
                    )  # 外鍵實際是關聯在這裏的
# 上的這張表你以後再也不用管它了,甚至不用知道它的存在

class Book(Base):
    __tablename__ = ‘book‘
    id = Column(INTEGER(unsigned=True), primary_key=True)
    name = Column(String(32))
    author = relationship(‘Author‘, secondary=book2author, backref=‘book‘)  # 關聯author表,但是實際是通過secondary來查
    def __repr__(self):
        return self.name

class Author(Base):
    __tablename__ = ‘author‘
    id = Column(INTEGER(unsigned=True), primary_key=True)
    name = Column(String(32))
    book = relationship(‘Book‘, secondary=book2author, backref=‘author‘)
    def __repr__(self):
        return self.name

Base.metadata.create_all(engine)  # 創建表

插入上面的記錄:

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
book1 = Book(name=‘Hamlet‘)
book2 = Book(name=‘Othello‘)
book3 = Book(name=‘King Lear‘)
book4 = Book(name=‘Macbeth‘)
author1 = Author(name=‘Adam‘)
author2 = Author(name=‘Bob‘)
author3 = Author(name=‘Cara‘)
author4 = Author(name=‘Dan‘)
# 下面是創建第三張表,通過book來創建的。完全不用操作第三張表
# 我們現在不知道有第三張表,只知道author是關聯到book的
book1.author = [author1]
book2.author = [author2, author3]
book3.author = [author2, author3, author4]
book4.author = [author4, author2]
session.add_all([book1, book2, book3, book4, author1, author2, author3, author4])
session.commit()

查詢記錄:

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
author_obj = session.query(Author).filter(Author.name == ‘Bob‘).first()  # 通過作者查書名
print(author_obj, author_obj.book)
book_obj = session.query(Book).filter(Book.name == ‘Othello‘).first()  # 通過書名查作者
print(book_obj, book_obj.author)

接下來來刪除數據:
通過查找先獲取到對象,然後移除對象。第三張表永遠不用管,自動都會通過關聯處理好。

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
author_obj = session.query(Author).filter(Author.name == ‘Bob‘).first()  # 通過作者查書名
print(author_obj, author_obj.book)
book_obj = session.query(Book).filter(Book.name == ‘Othello‘).first()  # 通過書名查作者
print(book_obj, book_obj.author)  # 這裏的參數都是對象,類中寫了__repr__方法
book_obj.author.remove(author_obj)  # 從這本書中移除Bob這個作者的對象,其實就是刪除了第三張表中的一條記錄
session.commit()  # 提交一下,在看看Bob寫了哪些書,Othello的作者現在有誰了
author_obj = session.query(Author).filter(Author.name == ‘Bob‘).first()  # 通過作者查書名
print(author_obj, author_obj.book)
book_obj = session.query(Book).filter(Book.name == ‘Othello‘).first()  # 通過書名查作者
print(book_obj, book_obj.author)

刪除作者,把Bob徹底幹掉:

# 插入數據,接在創建表的代碼後面。實際使用的時候,分開到不同的文件,用import導入表的class
session = sessionmaker(bind=engine)()
author_obj = session.query(Author).filter(Author.name == ‘Bob‘).first()  # 先用查找來獲取到對象
session.delete(author_obj)  # 刪除這個對象
session.commit()

使用中文

要使用中文需要再engine裏加一個參數,修改一下第一個參數的url,最後加一段:

engine = create_engine("mysql+pymysql://admin:[email protected]/week12?charset=utf8‘",
                       encoding=‘utf-8‘, echo=True)  # 支持中文

這樣你的sqlalchemy就可以使用中文了。
確認你建庫的時候使用了utf8,默認是‘latin1‘。這樣你的數據庫也支持中文了。
系統可能不支持,這樣你還是打印不出來。系統可能沒有安裝中文字符集,可能還要設置環境。還是算了不要搞系統了。
不過你還可以用ssh登錄,這樣只要你本地的ssh能打印中文就可以了,我們不需要在系統上輸出。

作業

學員管理系統
用戶角色,講師\學員, 用戶登陸後根據角色不同,能做的事情不同,分別如下

  • 講師視圖
    • 管理班級,可創建班級,根據學員qq號把學員加入班級
    • 可創建指定班級的上課紀錄,註意一節上課紀錄對應多條學員的上課紀錄,即每節課都有整班學員上,為了紀錄每位學員的學習成績,需在創建每節上課紀錄時,同時為這個班的每位學員創建一條上課紀錄
    • 為學員批改成績, 一條一條的手動修改成績
  • 學員視圖
    • 提交作業
    • 查看作業成績
    • 一個學員可以同時屬於多個班級,就像報了Linux的同時也可以報名Python一樣,所以提交作業時需先選擇班級,再選擇具體上課的節數
    • 附加:學員可以查看自己的班級成績排名

Python自動化開發學習12-MariaDB