1. 程式人生 > >SqlAlchemy ORM

SqlAlchemy ORM

width sse base 配置 splay 格式 blog des 顯示

ORM介紹

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

技術分享圖片

orm的優點:

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

缺點:

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

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

技術分享圖片

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:password@host:port/dbname[?key=value&key=value...]
	
更多詳見:http://docs.sqlalchemy.org/en/latest/dialects/index.html

ORM創建表結構:

方法1:

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

# create_engine() 初始化數據庫連接
# ‘數據庫類型+數據庫驅動名稱://用戶名:口令@機器地址:端口號/數據庫名‘
# charset=utf-8 設置表支持中文字符、echo=True打印創建過程
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboy?charset=utf-8",
                       encoding="utf-8",echo=True)

Base = declarative_base()   # 創建orm的基類

# 定義User對象
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,create_engine
from sqlalchemy.orm import mapper

engine = create_engine("mysql+pymysql://root:123456@localhost/oldboy?charset=utf8",echo=True) 

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)  # 創建映射關系

metadata.create_all(engine)   # 創建表結構

插入數據:

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

user_obj = User(name="alex",password="alex3714")  # 生成要創建的數據對象
print(user_obj.name,user_obj.id)  # 此時還沒創建對象,user_obj.id返回None

Session.add(user_obj)  # 把要創建的數據對象添加到session裏,統一創建
print(user_obj.name,user_obj.id)  # 此時也依然沒創建

Session.commit()  # 統一提交,創建數據

查詢數據:

# query:查詢對象、filter_by:過濾條件、first:顯示第一個
my_user = Session.query(User).filter_by(name="alex").first() print(my_user) # 返回的是內存地址<__main__.User object at 0x7f92d89ef518> 在User類中加入__repr__函數,可將內存地址中相應數據返回 # 重構__repr__方法後,不管直接輸出對象還是通過print打印的信息都按我們__repr__方法中定義的格式進行顯示
def __repr__(self): # __repr__ 和 __str__這兩個方法都是用於顯示的,__str__是面向用戶的,而__repr__面向程序員
  return "<%s(name=%s,password=%s)>" %(self.__class__.name,self.name,self.password)

修改數據:

my_user = Session.query(User).filter_by(name="alex").filter_by(password="sb1234").first()  # 多個條件時可加多個filter_by

my_user.name="Alex Li"    # 修改mysql_user中name的值

Session.commit()  # 提交

事務回滾:

my_user = Session.query(User).filter_by(id=1).first()  # 查id=1的數據
my_user.name = "Jack"  # 修改id=1數據中的name

fake_user = User(name="Rain",password="123456")  # 插入數據
Session.add(fake_user)  # 要插入的數據添加到Session裏面

# filter與filter_by都是過濾條件,用法類似,裏面的條件語句不同
print(Session.query(User).filter(User.name.in_(["Jack","rain"])).all()) # 查詢出所有User對象中name為Jack、rain的

Session.rollback()  # 事務回滾

print(Session.query(User).filter(User.name.in_(["Jack","rain"])).all())

簡單語句:

# all()獲取所有數據
Session.query(User.name,User.id).all()

# 多條件查詢
Session.query(User).filter(User.id>1).filter(User.id<7).all()

# 統計次數
Session.query(User).filter(User.name.like("Ra%")).count()

# 分組
from sqlalchemy import func

Session.query(User.name,func.count(User.name)).group_by(User.name).all() # 按User.name分組

相當於sql: select user.name,count(user.name) from user group by user.name;

外鍵關聯:

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

engine = create_engine("mysql+pymysql://root:123456@localhost/oldboy?charset=utf8",
                       encoding="utf8")

Base = declarative_base()  # 創建ORM基類

session_class = sessionmaker(bind=engine)  # 對表的操作交給session_class來操作,返回的是一個class
session = session_class()  # 創建一個session對象,在session中完成多個事務


class User(Base):
    __tablename__ = "user"
    id = Column(Integer,primary_key=True)
    name = Column(String(32))
    password = Column(String(64))

    def __repr__(self):
        return "<User(name=%s,password=%s)>" %(self.name,self.password)

class Address(Base):
    __tablename__ = "addresses"
    id = Column(Integer,primary_key=True)
    email_address = Column(String(32),nullable=False)
	#註意點:增加用戶時user_id只能是表user中存在的id,否則會報錯
    user_id = Column(Integer,ForeignKey("user.id"))  # addresses表中的外鍵關聯user表中的id
	
	# relationship函數是sqlalchemy對關系之間提供的一種便利的調用方式
    user = relationship("User",backref="addresses")  # 通過user調用user表中的變量, user表中通過addresses調用addresses表中的變量

    def __repr__(self):
        return "<Address(email_address=%s)>" %self.email_address


Base.metadata.create_all(engine)   # 構造表結構

obj = session.query(User).all()[0]  # 查詢user表語句

print("obj.addresses:",obj.addresses)

obj.addresses = [Address(email_address="[email protected]"),
                 Address(email_address="[email protected]")]  # 通過user對象反查關聯的addresses插入數據

for i in obj.addresses: # 通過user對象反查關聯的addresses記錄
  print(i)
session.commit() # 事務提交 addr_obj = session.query(Address).all()[-1] # 查看address表中的數據 print(addr_obj.user) # 通過user查看user表中的數據

常用 查詢語法:

	equals:
		query.filter(User.name == "ed")
	not equals:
		query.filter(User.name != "ed")
	like:
		query.filter(User.name.like("%ed%"))
	in:
		query.filter(User.name.in_(["ed","wendy","jack"]))
	not in:
		query.filter(~User.name.in_(["ed","wendy","jack"]))
	is null / is not null:
		query.filter(User.name == None)
		query.filter(User.name.is_(None))
		query.filter(User.name != None)
		query.filter(User.name.isnot(None))
	and:
		from sqlalchemy import and_
		query.filter(and_(User.name == "ed",User.fullname == "Ed Jones"))
		query.filter(User.name == "ed",User.fullname == "Ed Jones")
		query.filter(User.name == "ed").filter(User.fullname == "Ed Jones")
	or:
		from sqlalchemy import or_
		query.filter(or_(User.name == "ed",User.fullname == "Ed Jones"))
	match:
		query.filter(User.name.match("wendy"))

多外鍵關聯:(Customer表有2個字段都關聯了Address表)

技術分享圖片
 1 from sqlalchemy import Integer,ForeignKey,String,Column
 2 from sqlalchemy.ext.declarative import declarative_base
 3 from sqlalchemy.orm import relationship
 4 from sqlalchemy import create_engine
 5 
 6 engine = create_engine("mysql+pymysql://root:123456@localhost/oldboy?charset=utf8")
 7 
 8 Base = declarative_base()
 9 
10 
11 class Customer(Base):
12     __tablename__ = "customer"
13     id = Column(Integer,primary_key=True)
14     name = Column(String(32))
15 
16     billing_address_id = Column(Integer,ForeignKey("address.id"))
17     shopping_address_id = Column(Integer,ForeignKey("address.id"))
18 
19     bill_address = relationship("Address",foregin_keys=[billing_address_id])
20     shopping_address = relationship("Address",foregin_keys=[shopping_address_id])
21 
22 
23 class Address(Base):
24     __tablename__ = "address"
25     id = Column(Integer,primary_key=True)
26     street = Column(String(32))
27     city = Column(String(32))
28     state = Column(String(32))
29 
30 
31 Base.metadata.create_all(engine)
多外鍵關聯

多對多關系:

技術分享圖片
 1 from sqlalchemy import Table,Column,Integer,String,DATE,ForeignKey
 2 from sqlalchemy.orm import relationship
 3 from sqlalchemy.ext.declarative import declarative_base
 4 from sqlalchemy import create_engine
 5 from sqlalchemy.orm import sessionmaker
 6 
 7 engine = create_engine("mysql+pymysql://root:123456@localhost/oldboy?charset=utf8",
 8                        encoding="utf8")
 9 
10 Base = declarative_base()
11 
12 # 外鍵關聯表,ORM自動維護,不需要手動維護的一般采用Table這種方式創建
13 book_m2m_author = Table("book_m2m_author",Base.metadata,
14                         Column("book_id",Integer,ForeignKey("books.id")),
15                         Column("author_id",Integer,ForeignKey("authors.id"))
16                         )
17 
18 class Book(Base):
19     __tablename__ = "books"
20     id = Column(Integer,primary_key=True)
21     name = Column(String(32))
22     pub_date = Column(DATE)
23     authors = relationship("Author",secondary=book_m2m_author,backref="books")
24 
25     def __repr__(self):
26         return self.name
27 
28 
29 class Author(Base):
30     __tablename__ = "authors"
31     id = Column(Integer,primary_key=True)
32     name = Column(String(32))
33 
34     def __repr__(self):
35         return self.name
36 
37 Base.metadata.create_all(engine)
38 
39 
40 session_class = sessionmaker(bind=engine)
41 s = session_class()
42 
43 b1 = Book(name="Python自動化")
44 b2 = Book(name="人工智能")
45 b3 = Book(name="心靈雞湯")
46 b4 = Book(name="讀者")
47 
48 a1 = Author(name="Alex")
49 a2 = Author(name="Jack")
50 a3 = Author(name="Rain")
51 
52 b1.authors = [a1,a2]
53 b2.authors = [a1,a2,a3]
54 
55 s.add_all([b1,b2,b3,a1,a2,a3])
56 
57 s.commit()
58 
59 # ------------ 書名查作者 ----------------
60 obj = s.query(Book).filter(Book.name.like("%ython%")).first()
61 print(obj.name,obj.authors)
62 
63 # ------------ 作者查書名 ----------------
64 author_obj = s.query(Author).filter_by(name="Alex").first()
65 print(author_obj.name,author_obj.books)
ORM多對多

多對多刪除

刪除數據時不用管boo_m2m_authors , sqlalchemy會自動幫你把對應的數據刪除

通過書刪除作者

刪除數據時不用管boo_m2m_authors , sqlalchemy會自動幫你把對應的數據刪除

通過書刪除作者

author_obj =s.query(Author).filter_by(name="Jack").first()
 
book_obj = s.query(Book).filter_by(name="跟Alex學把妹").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)

補充知識點:

    • # 排序
    • users = session.query(User).order_by(User.name)
    • # 降序(需要導入desc方法)
    • from sqlalchemy import desc
    • users = session.query(User).order_by(desc(User.name))

 

  

SqlAlchemy ORM