1. 程式人生 > >Python入門學習筆記01(sqlalchemyd的使用)

Python入門學習筆記01(sqlalchemyd的使用)

清理 完全 修改 性能 單純 creat 語句 nsh linu

Object-Relational Mapping,作用是將關系型數據庫的表結構映射到對象上,使我們可以不必關心具體的SQL語句進行數據庫的增刪改查操作。

缺點是映射操作不可避免的會有性能上的損耗,優點是不必考慮SQL,程序與數據庫的交互被封裝,可以快速地開發。

sqlalchemy是Python中最著名的ORM框架。

sqlalchemy連接數據庫

sqlalchemy支持主流的數據庫,連接不同的數據庫需要安裝不同的組件
1.連接mysql(mariadb)
sqlalchemy默認使用mysql-python作為鏈接驅動,既default模式
選哪種驅動,就裝哪個包。
#default默認鏈接方式
engine = create_engine(‘mysql://scott:tiger@localhost/foo‘)
# mysql-python,聲明使用mysql-python驅動
engine = create_engine(‘mysql+mysqldb://scott:tiger@localhost/foo‘)
#MySQL-connector-python 聲明使用MySQL-connector-python驅動(推薦使用)
engine = create_engine(‘mysql+mysqlconnector://scott:tiger@localhost/foo‘)
#OurSQL 聲明使用OurSQL驅動
engine = create_engine(‘mysql+oursql://scott:tiger@localhost/foo‘)

2.連接Microsoft SQL Server
sqlalchemy默認使用 pyodbc作為鏈接驅動。
#pyodbc
engine = create_engine(‘mssql+pyodbc://scott:tiger@mydsn‘)
#pymssql
engine = create_engine(‘mssql+pymssql://scott:tiger@hostname:port/dbname‘)

3.連接PostgreSQL
PostgreSQL默認使用 psycopg2作為鏈接驅動,既default模式
#default
engine = create_engine(‘postgresql://scott:tiger@localhost/mydatabase‘)
#psycopg2
engine = create_engine(‘postgresql+psycopg2://scott:tiger@localhost/mydatabase‘)
#pg8000
engine = create_engine(‘postgresql+pg8000://scott:tiger@localhost/mydatabase‘)

4.連接Oracle
Oracle可能只有 cx_oracle一個驅動包,既default模式和聲明模式一樣。
#default
engine = create_engine(‘oracle://scott:[email protected]:1521/sidname‘)
#cx_oracle
engine = create_engine(‘oracle+cx_oracle://scott:tiger@tnsname‘)

使用sqlalchemy創建數據庫中表的映射

sqlalchemy創建映射的方法優良有兩種:

1.定義一個類難過過這個類來映射表結構

from sqlalchemy.orm import sessionmaker
#映射類的基類
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.engine import create_engine
from sqlalchemy import Column,Integer,String

"""
定義一個類,用來映射數據庫中的表
該類的基類通過declarative_base()獲得
"""
Base = declarative_base()
class Customer(Base):
    __tablename__ = "customer"
    id = Column(Integer,primary_key=True)
    name = Column(String(32))
    age = Column(Integer)
    #用於打印對象時的輸出
    def __repr__(self):
        return "id:%d name:%s age:%d" %(self.id,self.name,self.age)

2.通過Table創建映射

from sqlalchemy import MetaData,Table,Column,String,Integer
from sqlalchemy import create_engine
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.orm import mapper

"""
通過MetaData創建數據庫表的表結構,這種創建方法一般用於不需要對該表手動進行操作的情況,
例如連接多張表的中間表
"""
metadata = MetaData()
customer = Table("customer",metadata,
                 Column("id",Integer,primary_key=True),
                 Column("name",String(32)),
                 Column("age",Integer)
                 )

"""
定義一個字段與表一一對應的普通類,註意此時繼承的是object
"""
class Customer(object):
    def __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
    #用於打印對象時的輸出
    def __repr__(self):
        return "id:%d name:%s age:%d" %(self.id,self.name,self.age)

"""
通過mapper建立表結構與類的映射
"""
mapper(Customer, customer)

  

增刪改查

1.前置工作,創建映射關系

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

engine = create_engine(‘mssql+pyodbc://python:123456@mydb‘,echo=False)

session_class = sessionmaker(bind=engine)
session = session_class()

Base =declarative_base()

"""
定義表結構
"""
class Student(Base):
    __tablename__ = "student"
    """
    sqlalchemy默認定義為主鍵便是自增的,可以用autoincrement手動指定
    """
    id = Column(Integer,primary_key=True)
    name = Column(String(32),nullable=False)
    grade_and_class = Column(String(32),nullable=False)
    age = Column(Integer,nullable=False)

    def __repr__(self):
        return "%d %s %s" %(self.id,self.name,self.grade_and_class)

使用sqlalchemy進行數據庫的增刪改查操作都是基於上面定義的映射關系

2.創建表

Base.metadata.create_all(engine)

定義好映射關系之後執行create_all操作,會循環繼承自Base的所有類,如果數據庫中不存在同名的表,那麽orm便會根據類中的定義創建一張新的表。如果已經存在同名的表則直接pass

3.刪除表

Base.metadata.drop_all(engine)

定義好映射關系之後執行drop_all操作,會循環繼承自Base的所有類並將其從數據庫中刪除

4.添加記錄

obj = Student(name="馬克",grade_and_class="202",age=19)
session.add(obj)
obj = Student(name="Mike",grade_and_class="102",age=18)
session.add(obj)

obj1 = Student(name="Jack",grade_and_class="202",age=18)
obj2 = Student(name="Tom",grade_and_class="202",age=18)
session.add_all([obj1,obj2])
#add只是將對象添加到session之中,必須執行commit才會提交到數據庫
session.commit()

5.刪除記錄

objs =session.query(Student).filter(Student.name=="Pinkman").delete()

或者

objs =session.query(Student).filter(Student.name=="Pinkman")
for obj in objs:
    session.delete(obj)

6.修改記錄

"""
修改數據
"""
objs = session.query(Student).filter(Student.name=="Tom").all()
for obj in objs:
    obj.name = "Pinkman"
session.commit()

直接在映射上進行修改,session進行commit之後就會反映到數據庫中。

6.回滾

session操作的時候相當於自動開啟了一個begin,在commit之前可以任意回滾

session.query(Student).delete()
session.rollback()
session.query(Student).filter(Student.name == "Mike").delete()
session.commit()

7.查詢

"""
filter與filter_by的傳入參數格式有區別,其他的一樣
以下四種查詢限定條件的寫法結果完全一樣
查詢不用執行commit,因為對數據庫並沒有修改
"""
objs = session.query(Student).filter(Student.grade_and_class=="202",Student.age==18).all()
print(objs)
objs = session.query(Student).filter_by(grade_and_class="202",age=18).all()
print(objs)
objs = session.query(Student).filter(Student.grade_and_class == "202").filter(Student.age == 18).all()
print(objs)
objs = session.query(Student).filter_by(grade_and_class="202").filter_by(age=18).all()
print(objs)

like:

objs = session.query(Student).filter(Student.name.like("%i%"))

in:

objs = session.query(Student).filter(Student.age.in_([18,20]))

not in:

objs = session.query(Student).filter(~Student.age.in_([18,20]))

or:

objs = session.query(Student).filter(or_(Student.age==18,Student.name=="馬克"))

and:

objs = session.query(Student).filter(and_(Student.age==18,Student.name=="馬克"))

分組函數:

#所有的分組函數都定義在func中
objs1 = session.query(Student.name,func.sum(Student.age)).group_by(Student.name).all()


objs2 = session.query(Student.name,func.count(Student.name)).group_by(Student.name).all()

  

外鍵關聯

定義如下兩張表,以id進行外鍵關聯

"""
商品信息表goods:
id(編號,主鍵)
name(名稱,非空)
place_of_origin_id(產地id,非空)
產地信息表origin:
id(編號,主鍵)
name(名稱,非空)
"""

定義映射:

class Goods(Base):
    __tablename__ = "goods_demo"
    id = Column(Integer,primary_key=True)
    name = Column(String(32),nullable=False)
    #創建外鍵
    place_of_origin_id = Column(Integer,ForeignKey("origin_demo.id"),nullable=False)

    origin = relationship("Origin",backref="goods")

    def __repr__(self):
        return "[%s]產地[%s]" %(self.name,self.origin.name)


class Origin(Base):
    __tablename__ = "origin_demo"
    id = Column(Integer,primary_key=True)
    name = Column(String(32),nullable=False)

    def __repr__(self):
        return "%d:%s" %(self.id,self.name)

engine = create_engine(‘mssql+pyodbc://python:123456@mydb‘,echo=False)
#建表
Base.metadata.create_all(engine)

維護外鍵的時候,如果在relationship中定義了backref的話,那麽外鍵表通過該字段便可以直接反查與自己相關的引用表的數據

#引用外鍵的表可以通過定義的relationship訪問關聯的外鍵表數據
print("from goods to origin")
goods = session.query(Goods).filter(Goods.name == "英國的故事").all()
for item in goods:
    print(item.origin)

#外鍵表可以通過引用外鍵表中relationship中定義的backref反向查詢與自己有關聯的表的數據
print("from origin to goods")
origins = session.query(Origin).all()
for origin in origins:
    print(origin.goods)

多外鍵關聯

普通的多外鍵語法與單外鍵完全一樣,但如果一張表的多個字段應用了同一張表的同一個字段作為外鍵的話,必須在relationship中顯示的聲明外鍵對應的字段,否則在插入數據的時候程序無法區分會報錯

class Mail(Base):
    __tablename__ = "Mail_demo"
    id = Column(Integer,primary_key=True)
    addr_id1 = Column(Integer,ForeignKey("address_demo.id"),nullable=False)
    addr_id2 = Column(Integer,ForeignKey("address_demo.id"),nullable=False)

    addr1 = relationship("Address",foreign_keys=[addr_id1])
    addr2 = relationship("Address",foreign_keys=[addr_id2])

多對多的表結構 

如果兩個事物之間存在一種多對多的關系,比如一本書可能有一個以上的作者,而一個作者又可能寫過一本以上的書,此時要維護兩者之間的關系,一般會將這種關系抽離出來作為一個單獨的表,加上單純的書本信息表,作者信息表,三者構成一個多對多的數據庫關系。

"""
圖書信息表book
id
name
作者信息表author
id
name
圖書作者關系表relation_book_author
book_id
author_id
圖書信息和作者信息是多對多的關系,對應關系由關系表維護
"""

定義三個表的映射:

#因為在實際運用中我們並不會直接從這張中間表中獲取數據,所以用Table的方式直接映射
relation_book_author = Table("relation_book_author_demo",Base.metadata,
                             Column("book_id",Integer,ForeignKey("book_demo.id")),
                             Column("author_id",Integer,ForeignKey("author_demo.id"))
                             )

class Book(Base):
    __tablename__ = "book_demo"
    id = Column(Integer,primary_key=True)
    name = Column(String(32),nullable=False)

    authors = relationship("Author",secondary=relation_book_author,backref="books")

    def __repr__(self):
        return self.name

class Author(Base):
    __tablename__ = "author_demo"
    id = Column(Integer,primary_key=True)
    name = Column(String(32),nullable=False)

    def __repr__(self):
        return self.name

添加數據

book1 = Book(name="算法導論")
book2 = Book(name="計算機系統")
book3 = Book(name="Linux基礎")
book4 = Book(name="Python的前世今生")

author1 = Author(name="謝爾曼")
author2 = Author(name="肖恩")
author3 = Author(name="曼寧")
author4 = Author(name="凱夫拉")

book1.authors = [author1]
book2.authors = [author1,author2]
book3.authors = [author2,author3,author1]
book4.authors = [author2,author3]

#如果只是add所有的book的話,那麽和book有關聯信息的authoe也會一並提交
#註意,提交到數據庫的順序並不一定就是程序寫的順序,relation表中的id也許會和程序中的順序不一樣
session.add_all([book1,book2,book3,book4,author1,author2,author3,author4])
session.commit()

上面的代碼沒有直接對圖書作者關系信息表進行操作,相關的操作都是orm為我們自動執行的

查詢:

obj_book = session.query(Book).filter(Book.name == "Linux基礎").all()
print("Linux基礎的作者".center(30,"-"))
for item in obj_book:
    print(item.authors)

obj_author = session.query(Author).filter(Author.name == "肖恩").all()
print("肖恩寫的書".center(30,"-"))
for item in obj_author:
    print(item.books)

結果

----------Linux基礎的作者----------
[肖恩, 謝爾曼, 曼寧]
------------肖恩寫的書-------------
[計算機系統, Linux基礎, Python的前世今生]

同樣的,直接刪除Book和Author映射對象之後,orm也會為我們自動清理relation_book_author表中對應的數據。

直接刪除書
obj_book = session.query(Book).filter(Book.name == "算法導論").all()
for item in obj_book:
    session.delete(item)
    session.commit()

刪除某本書的一個作者
obj_book = session.query(Book).filter(Book.name == "Linux基礎").first()
obj_author = session.query(Author).filter(Author.name == "肖恩").first()

if obj_author in obj_book.authors:
    obj_book.authors.remove(obj_author)
    session.commit()

  

Python入門學習筆記01(sqlalchemyd的使用)