pymysql與SQLAchemy的基本知識點整理

本篇對于Python操作MySQL主要使用兩種方式:

  • 原生模塊 pymsql
  • ORM框架 SQLAchemy

一、pymysql

    1. 下載安裝
      pip install pymysql
  • 2.使用操作
    ------1.執行SQL
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
  
# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
# 創建游標
cursor = conn.cursor()
# 執行SQL,并返回收影響行數
effect_row = cursor.execute("update hosts set host = '1.1.1.2'")
# 執行SQL,并返回受影響行數
#effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
# 執行SQL,并返回受影響行數
#effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
# 提交,不然無法保存新建或者修改的數據
conn.commit()
# 關閉游標
cursor.close()
# 關閉連接
conn.close()

------2.獲取新創建數據自增ID

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
  
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor()
cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
conn.commit()
cursor.close()
conn.close()
  
# 獲取最新自增ID
new_id = cursor.lastrowid

------3.獲取查詢數據

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
  
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor()
cursor.execute("select * from hosts")
  
# 獲取第一行數據
row_1 = cursor.fetchone()
  
# 獲取前n行數據
# row_2 = cursor.fetchmany(3)
# 獲取所有數據
# row_3 = cursor.fetchall()
  
conn.commit()
cursor.close()
conn.close()

注:在fetch數據時按照順序進行,可以使用cursor.scroll(num,mode)來移動游標位置,如:

  • cursor.scroll(1,mode='relative') # 相對當前位置移動
  • cursor.scroll(2,mode='absolute') # 相對絕對位置移動
    ------ 4.fetch數據類型
    關于默認獲取的數據是元祖類型,如果想要或者字典類型的數據,即:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
  
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
# 游標設置為字典類型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
r = cursor.execute("call p1()")
  
result = cursor.fetchone()
  
conn.commit()
cursor.close()
conn.close()

二、SQLAchemy

SQLAlchemy是Python編程語言下的一款ORM框架,該框架建立在數據庫API之上,使用關系對象映射進行數據庫操作,簡言之便是:將對象轉換成SQL,然后使用數據API執行SQL并獲取執行結果
安裝:pip install SQLAlchemy

image.png

SQLAlchemy本身無法操作數據庫,其必須以來pymsql等第三方插件,Dialect用于和數據API進行交流,根據配置文件的不同調用不同的數據庫API,從而實現對數據庫的操作,如:

MySQL-Python
    mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
   
pymysql
    mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
   
MySQL-Connector
    mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
   
cx_Oracle
    oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
   
更多詳見:http://docs.sqlalchemy.org/en/latest/dialects/index.html

1、SQLAchemy的基本使用

  • 創建表
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
 
engine = create_engine("mysql+pymysql://root:123456@localhost/testdb",
                                    encoding='utf-8', echo=True)
 
 
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) #創建表結構

除上面的創建之外,還有一種創建表的方式,雖不常用,但還是看看吧

from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey
from sqlalchemy.orm import mapper
 
metadata = MetaData()
 
user = Table('user', metadata,
            Column('id', Integer, primary_key=True),
            Column('name', String(50)),
            Column('fullname', String(50)),
            Column('password', String(12))
        )
 
class User(object):
    def __init__(self, name, fullname, password):
        self.name = name
        self.fullname = fullname
        self.password = password
 
mapper(User, user) 
#the table metadata is created separately with the Table construct,
 then associated with the User class via the mapper() function

事實上,我們用第一種方式創建的表就是基于第2種方式的再封裝。

  • 新增
from sqlalchemy.orm import sessionmaker, relationship

Session_class = sessionmaker(bind=engine) 
#創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例
Session = Session_class() #生成session實例

user_obj = User(name="alex",password="123456") #生成你要創建的數據對象
print(user_obj.name,user_obj.id)  #此時還沒創建對象呢,不信你打印一下id發現還是None
 
Session.add(user_obj) #把要創建的數據對象添加到這個session里, 一會統一創建
print(user_obj.name,user_obj.id) #此時也依然還沒創建
 
Session.commit() #現此才統一提交,創建數據
  • 查詢
my_user = Session.query(User).filter_by(name="alex").first()
#這樣查詢出來的不是直接的數據是一個對象
print(my_user)#<__main__.User object at 0x105b4ba90>
#所以再經一輪提取才能獲得數據
print(my_user.id,my_user.name,my_user.password)

如果想查詢出來直接是數據的話,可以通過修改類的定義來返回

def __repr__(self):
    return "<User(name='%s',  password='%s')>" % (
        self.name, self.password)
  • 修改
    修改就是先查詢出將要修改的內容,然后直接重新對其賦值,這樣就能達到修改的目的。
my_user = Session.query(User).filter_by(name="alex").first()
my_user.name = "Alex Li" 
Session.commit()
  • 回滾
my_user = Session.query(User).filter_by(id=1).first()
my_user.name = "Jack"
 
fake_user = User(name='Rain', password='12345')
Session.add(fake_user)
  #這時看session里有你剛添加和修改的數據
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) 
#此時你rollback一下
Session.rollback() 
#再查就發現剛才添加的數據沒有了。
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) 
# Session
# Session.commit()
  • 獲取所有數據
    print(Session.query(User.name,User.id).all())
  • 多條件查詢
    objs = Session.query(User).filter(User.id>0).filter(User.id<7).all()
    上面2個filter的關系相當于 user.id >1 AND user.id <7 的效果
  • 統計和分組
#統計
Session.query(User).filter(User.name.like("Ra%")).count()
#分組
from sqlalchemy import func
print(Session.query(func.count(User.name),User.name).group_by(User.name).all() )
#相當于原生sql為
select count(user.name) AS count_1, user.name AS user_name
FROM user GROUP BY user.name
  • 外鍵關聯
    我們創建一個addresses表,跟user表關聯
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
 
class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String(32), nullable=False)
    user_id = Column(Integer, ForeignKey('user.id'))
 
    user = relationship("User", backref="addresses") 
#這個nb,允許你在user表里通過backref字段反向查出所有它在addresses表里的關聯項
 
    def __repr__(self):
        return "<Address(email_address='%s')>" % self.email_address

表創建好后,我們可以這樣反查試試

obj = Session.query(User).first()
for i in obj.addresses: #通過user對象反查關聯的addresses記錄
    print(i)
 
addr_obj = Session.query(Address).first()
print(addr_obj.user.name)  #在addr_obj里直接查關聯的user表

創建關聯對象

obj = Session.query(User).filter(User.name=='rain').all()[0]
print(obj.addresses)
 
obj.addresses = [Address(email_address="r1@126.com"), #添加關聯對象
                 Address(email_address="r2@126.com")]
Session.commit()

2、多外鍵關聯
下表中,Customer表有2個字段都關聯了Address表

from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
 
Base = declarative_base()
 
class Customer(Base):
    __tablename__ = 'customer'
    id = Column(Integer, primary_key=True)
    name = Column(String)
 
    billing_address_id = Column(Integer, ForeignKey("address.id"))
    shipping_address_id = Column(Integer, ForeignKey("address.id"))
 
    billing_address = relationship("Address") 
    shipping_address = relationship("Address")
 
class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    street = Column(String)
    city = Column(String)
    state = Column(String)

創建表結構是沒有問題的,但你Address表中插入數據時會報下面的錯.

sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join
condition between parent/child tables on relationship
Customer.billing_address - there are multiple foreign key
paths linking the tables.  Specify the 'foreign_keys' argument,
providing a list of those columns which should be
counted as containing a foreign key reference to the parent table.

解決辦法如下:

class Customer(Base):
    __tablename__ = 'customer'
    id = Column(Integer, primary_key=True)
    name = Column(String)
 
    billing_address_id = Column(Integer, ForeignKey("address.id"))
    shipping_address_id = Column(Integer, ForeignKey("address.id"))
 
    billing_address = relationship("Address", foreign_keys=[billing_address_id])
    shipping_address = relationship("Address", foreign_keys=[shipping_address_id])

這樣sqlachemy就能分清哪個外鍵是對應哪個字段了
3、多對多關系
現在來設計一個能描述“圖書”與“作者”的關系的表結構,需求是

  • 一本書可以有好幾個作者一起出版
  • 一個作者可以寫好幾本書
#一本書可以有多個作者,一個作者又可以出版多本書

from sqlalchemy import Table, Column, Integer,String,DATE, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker


Base = declarative_base()

book_m2m_author = Table('book_m2m_author', Base.metadata,
                        Column('book_id',Integer,ForeignKey('books.id')),
                        Column('author_id',Integer,ForeignKey('authors.id')),
                        )

class Book(Base):
    __tablename__ = 'books'
    id = Column(Integer,primary_key=True)
    name = Column(String(64))
    pub_date = Column(DATE)
    authors = relationship('Author',secondary=book_m2m_author,backref='books')

    def __repr__(self):
        return self.name

class Author(Base):
    __tablename__ = 'authors'
    id = Column(Integer, primary_key=True)
    name = Column(String(32))

    def __repr__(self):
        return self.name

接下來創建幾本書和作者

Session_class = sessionmaker(bind=engine) 
#創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例
s = Session_class() #生成session實例
 
b1 = Book(name="Python入門到放棄")
b2 = Book(name="精通Python72式")
b3 = Book(name="MYSQL入門到裝逼")
b4 = Book(name="C#學習")
 
a1 = Author(name="Alex")
a2 = Author(name="Jack")
a3 = Author(name="Rain")
 
b1.authors = [a1,a2]
b2.authors = [a1,a2,a3]
 
s.add_all([b1,b2,b3,b4,a1,a2,a3])
 
s.commit()

此時,手動連上mysql,分別查看這3張表,你會發現,book_m2m_author中自動創建了多條紀錄用來連接book和author表

mysql> select * from books;
+----+------------------+----------+
| id | name             | pub_date |
+----+------------------+----------+
|  1 | Python入門到放棄   | NULL     |
|  2 | 精通Python72式     | NULL     |
|  3 | MYSQL入門到裝逼     | NULL     |
|  4 | C#學習     | NULL     |
+----+------------------+----------+
4 rows in set (0.00 sec)
 
mysql> select * from authors;
+----+------+
| id | name |
+----+------+
| 10 | Alex |
| 11 | Jack |
| 12 | Rain |
+----+------+
3 rows in set (0.00 sec)
 
mysql> select * from book_m2m_author;
+---------+-----------+
| book_id | author_id |
+---------+-----------+
|       2 |        10 |
|       2 |        11 |
|       2 |        12 |
|       1 |        10 |
|       1 |        11 |
+---------+-----------+
5 rows in set (0.00 sec)

此時,我們去用orm查一下數據

print('--------通過書表查關聯的作者---------')
 
book_obj = s.query(Book).filter_by(name="Python入門到放棄").first()
print(book_obj.name, book_obj.authors)
 
print('--------通過作者表查關聯的書---------')
author_obj =s.query(Author).filter_by(name="Alex").first()
print(author_obj.name , author_obj.books)
s.commit()

輸出如下:

--------通過書表查關聯的作者---------
Python入門到放棄 [Alex, Jack]
--------通過作者表查關聯的書---------
Alex [精通Python72式, Python入門到放棄]
  • 多對多刪除
    刪除數據時不用管boo_m2m_authors , sqlalchemy會自動幫你把對應的數據刪除
  • 通過書刪除作者
author_obj =s.query(Author).filter_by(name="Jack").first()
 
book_obj = s.query(Book).filter_by(name="精通Python72式").first()
 
book_obj.authors.remove(author_obj) #從一本書里刪除一個作者
s.commit()
  • 直接刪除作者
    刪除作者時,會把這個作者跟所有書的關聯關系數據也自動刪除
author_obj =s.query(Author).filter_by(name="Alex").first()
# print(author_obj.name , author_obj.books)
s.delete(author_obj)
s.commit()
  • 處理中文
    sqlalchemy設置編碼字符集一定要在數據庫訪問的URL上增加charset=utf8,否則數據庫的連接就不是utf8的編碼格式:
    eng = create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容

  • 轉載,覺得這篇寫 SQLAlchemy Core,寫得非常不錯。不過后續他沒寫SQLAlchemy ORM... ...
    非夢nj閱讀 5,462評論 1 14
  • 關于Mongodb的全面總結 MongoDB的內部構造《MongoDB The Definitive Guide》...
    中v中閱讀 32,001評論 2 89
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發此異常。 O...
    我想起個好名字閱讀 5,407評論 0 9
  • #神劍山莊# (307) 文/小齊同學 是的,我沒有理由 你來了, 我在路口等你 那夜晚的月亮好浪漫 你走了, 我...
    小齊同學閱讀 330評論 0 4
  • 拆頁五: 《高績效教練》,39頁 提高覺察力和責任感最有效的問題應以尋求量化或收集事實的詞語開始,比如“什么”,“...
    黃兄快跑閱讀 148評論 2 0