Python SQLalchemy的學習與使用

  SQLAlchemy是python中最著名的ORM(Object Relationship Mapping)框架了。html

前言:什麼是ORM?

  ORM操做是全部完整軟件中後端處理最重要的一部分,主要完成了後端程序和數據庫之間的數據同步和持久化的操做。python

  數據庫表示一個二維表,包含多行多列。把一個表的內容用python的數據結構表示出來的話,能夠用一個list表示多行,list的每個元素是tuple,表示一行記錄,好比,包含id和name的user表:mysql

[
    ('1', 'james'),
    ('2', 'durant'),
    ('3', 'curry')
]

  Python的DB-API返回的數據結構就是像上面這樣表示的。可是用tuple表示一行很難看出表的結構,若是把一個tuple用class實例來表示,就能夠更容易的看出表的結構來:git

class User(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name

[
    User('1', 'james'),
    User('2', 'durant'),
    User('3', 'curry')
]

  這就是傳說中的ORM技術:Object-Relational Mapping ,把關係數據庫的表結構映射到對象上。可是由誰來作這個轉化呢?因此ORM框架應運而生。目前來講也是描述程序中對象和數據庫中數據記錄之間的映射關係的統稱,是一種進行程序和數據庫之間數據持久化的一種編程思想。程序員

  ORM思想的核心是隱藏了數據訪問細節,提供了通用的數據庫交互,而且徹底不用考慮SQL語句,從而快速開發。sql

  一句話解釋ORM就是:一種能夠把model中的模型和數據庫中的一條數據相互轉換的工具。數據庫

  舉個例子:編程

 

#sql中的表                                                      

 #建立表:
     CREATE TABLE employee(                                     
                id INT PRIMARY KEY auto_increment ,                    
                name VARCHAR (20),                                      
                gender BIT default 1,                                  
                birthday DATA ,                                         
                department VARCHAR (20),                                
                salary DECIMAL (8,2) unsigned,                          
              );

  #sql中的表紀錄                                                  
  #添加一條表紀錄:                                                          
      INSERT employee (name,gender,birthday,salary,department)            
             VALUES   ("alex",1,"1985-12-12",8000,"保潔部");               
  #查詢一條表紀錄:                                                           
      SELECT * FROM employee WHERE age=24;                               
  #更新一條表紀錄:                                                           
      UPDATE employee SET birthday="1989-10-24" WHERE id=1;              
  #刪除一條表紀錄:                                                          
      DELETE FROM employee WHERE name="alex"                             
#python的類
class Employee(models.Model):
     id=models.AutoField(primary_key=True)
     name=models.CharField(max_length=32)
     gender=models.BooleanField()
     birthday=models.DateField()
     department=models.CharField(max_length=32)
     salary=models.DecimalField(max_digits=8,decimal_places=2)
 #python的類對象
      #添加一條表紀錄:
          emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保潔部")
          emp.save()
      #查詢一條表紀錄:
          Employee.objects.filter(age=24)
      #更新一條表紀錄:
          Employee.objects.filter(id=1).update(birthday="1989-10-24")
      #刪除一條表紀錄:
          Employee.objects.filter(name="alex").delete()

 

  在Python中,最有名的ORM框架是SQLAlchemy。咱們來看看其語法。後端

一,初始化數據庫鏈接

  SQLAlchemy自己沒法操做數據庫,其必須經過pymysql等第三方插件。上圖中Dialect用於和數據API進行交流,根據配置文件的不一樣調用不一樣的數據庫API,從而實現對數據庫的操做。服務器

  使用sqlalchemy進行數據庫操做,首先咱們須要創建一個指定數據庫的連接引擎對象,而創建引擎對象的方式被封裝在了sqlalchemy.create_engine函數中,經過指定的數據庫鏈接信息便可建立。

  create_engine()用來初始化數據庫鏈接。SQLAlchemy用一個字符串表示鏈接信息:

'數據庫類型+數據庫驅動名稱://用戶名:口令@機器地址:端口號/數據庫名'

  你只須要根據須要替換掉用戶名,口令等信息便可。舉個例子:

# 初始化數據庫連接

engine = create_engine('mysql+mysqlconnector://root:123456@localhost:3306/test')

  我使用了mysql數據庫,數據庫鏈接框架用的是mysqlconnector,用戶名爲root,密碼是123456,端口號是localhost(127.0.0.1),端口號是3306(mysql服務器默認端口號),test是數據庫的名字。

二,建立user模型及其數據庫建表操做

2.1,安裝SQLAlchemy

  若是沒有安裝SQLAlchemy請先安裝,這個不是python自帶的。

pip  install sqlalchemy

2.2,數據庫建表

  在test數據庫中,建立user表,SQL以下:

create table user (id varchar(20) primary key, name varchar(20))

  建立表,表名爲user,表中有兩個字段。一個是id,varchar類型,最多支持20個字符,設置爲主鍵,另外一個是name,varchar類型,最多支持20個字符。

(PS:主鍵時惟一的,當你重複插入時會報錯,並終止插入操做)。

  插入數據,以下:

insert into user(id,name) values('1001','james');

insert into user(id,name) values('1002','durant');

insert into user(id,name) values('1003','curry');

  

  更改表名

alter table origin_name rename to new_name;

  

2.3,建立user模型

  在model.py中建立 一個User類,用來和User表中的字段進行關聯。

# load module
from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 建立對象的基類
Base = declarative_base()

# 定義User對象
class User(Base):
    # 表的名字
    __tablename__ = 'user'

    # 表的結構
    id = Column(String(20), primary_key=True)
    name = Column(String(20))

  這段代碼的意思,User類的字段名和表中徹底一致,並且字段的屬性也同樣,以id字段爲例,表中id的屬性是Varchar(20),primary key,模型中String(20),primary_key=True。咱們能夠清晰的看到varchar在程序中對應String,primary_key對應程序中的primary_key屬性,並且是一個bool類型。

(PS:這裏的String是sqlAlchemy中的一個類,這樣相似的類咱們還會用到Column)

2.4,Column類的學習

  構建函數爲

Column.__init__(self, name, type_, *args, **kwargs)

  name  列名

  type_ 類型 ,更多類型SQLAlchemy.types

  下面是*args參數定義

  • Constraint(約束)
  • ForeignKey(外鍵)
  • ColumnDefault(默認)
  • Sequenceobjects(序列)定義

  下面是**kwargs參數定義

  • primary_key 若是爲True,則是主鍵
  • nullable 是否可謂Null,默認是True
  • default 默認值,默認是None
  • index 是不是索引,默認是True
  • unique 是否惟一鍵,默認是False
  • onupdate 指定一個更新時候的值,這個操做是定義在SQLAlchemy中,不是在數據庫裏的,當更新一條數據時設置,大部分用於update Time這類字段
  • autoincrement 設置爲整型自動增加,只有沒有默認值,而且是Integer類型,默認是True
  • quote 若是列名是關鍵字,則強制轉義,默認False

  

三,CRUD(Create Read Update Delete,增查改刪)

  在SQLAlchemy中,增刪改查操做是經過一個session對象(DBSession是由sessionmaker建立的)來完成的。

3.1,初始化DBSession,鏈接會話

  咱們的程序中的對象要使用sqlalchemy的管理,實現對象的ORM操做,就須要按照框架指定的方式進行類型的建立操做,SQLAlchemy封裝了基礎類的聲明操做和字段屬性的定義限制方式,開發人員要作的事情就是引入須要的模塊並在建立對象的時候使用他們便可。

  基礎類封裝在sqlalchemy.ext.declarative_base模塊中,字段屬性的定義封裝在SQLAlchemy模塊中,經過sqlalchemy.Column定義屬性,經過封裝的Integer,String,Float等定義屬性的限制。

  首先咱們須要建立DBSession,在此基礎上就能夠進行增刪改查操做了。

# load module
from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 建立對象的基類
Base = declarative_base()

# 定義User對象
class User(Base):
    # 表的名字
    __tablename__ = 'user'

    # 表的結構
    id = Column(String(20), primary_key=True)
    name = Column(String(20))

# 初始化數據庫鏈接
engine = create_engine('mysql+mysqlconnector://root:password@localhost:3306/test')

# 建立DBSession類型
DBSession = sessionmaker(bind=engine)

  上面的代碼完成SQLAlchemy的初始化和具體每一個表的class定義。若是有多個表,就繼續定義其餘class。

  注意:若是在建立會話的時候尚未指定數據庫引擎,能夠經過以下的方式完成會話:

Session = sessionmaker()
..
Session.configure(bind=engine)
session = Session()

  完整代碼以下:

def MySQLConnect(connection_info:str, id, status):
    engine = create_engine(connection_info)
    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    traininfo = session.query(TrainInfo).filter(TrainInfo.id == id).one()
    if traininfo is None:
        info = TrainInfo(id=id, status=status)
        session.add(info)
    else:
        traininfo.status = '1'
    session.commit()
    session.close()

  

3.2,添加操做

  程序中存在一個對象Object數據,經過ORM核心模塊進行增長的函數定義將對象保存在數據庫的操做過程,就是增長操做。好比註冊操做中,經過用戶輸入的帳號密碼等信息建立了一個獨立的對象,經過add()函數將對象增長保存到數據庫中,數據庫中就存在用戶這個對象數據了。

  下面,咱們看看如何向數據庫表中添加一行記錄。

  因爲有ORM,咱們向數據庫表中添加一行記錄,能夠視爲添加一個User對象:

# 建立session對象
session = DBSession()
# 建立新的User對象
new_user = User(id='1001', name='james')
# 添加到session
session.add(new_user)
# 提交即保存到數據庫
session.commit()
# 關閉session
session.close()

  可見,關鍵是獲取session,而後把對象添加到session,最後提交併關閉。DBSession對象可視爲當前數據庫鏈接。

3.3,查詢操做

  查詢時經過Session的query()方法建立一個查詢對象,這個函數的參數能夠是任何類或者類的描述的集合。查詢出來的數據是一個對象,直接經過對象的屬性調用。

  如何從數據庫表中查詢數據呢?有了ORM,查詢出來的能夠再也不是tuple,而是User對象。SQLAlchemy提供的查詢接口以下:

# 建立session
session = DBSession()
# 建立Query查詢,filter是where條件,最後調用one()返回惟一行,若是調用all()則返回全部行
user = session.query(User).filter(User.id=='1001').one()
# 打印類型和對象的name屬性
print('type:', type(user))
print('name:', user.name)
# 關閉Session:
session.close()

  可見,ORM就是把數據庫表的行與相應的對象創建關聯,互相轉換。

  因爲關係數據庫的多個表還能夠用外鍵實現一對多,多對多等關聯,相應的,ORM框架也能夠提供兩個對象之間的一對多,多對多等功能。

  例如,若是一個User擁有多個Book,就能夠定義一對多關係以下:

class User(Base):
    __tablename__ = 'user'

    id = Column(String(20), primary_key=True)
    name = Column(String(20))
    # 一對多:
    books = relationship('Book')

class Book(Base):
    __tablename__ = 'book'

    id = Column(String(20), primary_key=True)
    name = Column(String(20))
    # 「多」的一方的book表是經過外鍵關聯到user表的:
    user_id = Column(String(20), ForeignKey('user.id'))

  當咱們查詢一個User對象時,該對象的books屬性將返回一個包含若干個Book對象的list .

 3.4,更新操做

  程序中存在的一個對象Object數據,有本身的id編號(可使程序中自行賦值定義,更多的操做是從數據庫中查詢出來存在的一個對象),經過ORM核心模塊進行修改函數的定義將對象改變的數據更新到數據庫中已經存在的記錄中的過程,就是更新操做。好比用戶更改登陸密碼操做時,根據程序中查詢獲得的一個用戶{id編號,帳號,密碼},在程序中經過改變其密碼屬性數據,而後經過update()函數將改變的數據更新保存到數據庫中,數據庫原來的數據就發生了新的改變。

  更新操做要多一步,就是要先根據篩選條件拿到要更改的對象,而後給對象賦值,再次提交(commit)便可。

# 建立session對象
session = DBSession()
# 查找須要更新的字段id
user_result = session.query(User).filter_by(id='1001').first()
# 更新字段名稱
user_result.name = 'durant'
# 提交即保存到數據庫
session.commit()
# 關閉session
session.close()

  

3.5,刪除操做

  程序中存在的一個對象或者已知的id編號,經過主鍵編號或者對象的任意屬性進行數據庫中數據記錄的刪除的操做過程稱爲刪除操做。如管理員刪除某個會員帳號的操做,經過獲取要刪除會員的帳號,而後經過delete()函數將要刪除的會員信息告知數據庫執行刪除操做,數據庫中的某條存在的數據記錄就被刪除掉了。

# 建立session對象
session = DBSession()
# 查找須要刪除的字段id
user_result = session.query(User).filter_by(id='1001').first()
# 刪除字段內容
session.delete(user_result)
# 提交即保存到數據庫
session.commit()
# 關閉session
session.close()

  

3.6,代碼整合

# 導入依賴
from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 建立對象的基類
Base = declarative_base()

# 定義User對象
class User(Base):
    # 表的名字
    __tablename__ = 'user'

    # 表的結構
    id = Column(String(20), primary_key=True)
    name = Column(String(20))


# 初始化數據庫連接
engine = create_engine('mysql+mysqlconnector://root:123456@localhost:3306/test')

# 建立DBSession類型
DBSession = sessionmaker(bind=engine)


# 添加
# 建立Session對象
session = DBSession()
# 建立User對象
new_user = User(id='1001', name='james')
# 添加到session
session.add(new_user)
# 提交
session.commit()
# 關閉session
session.close()


# 查詢
# 建立session
session = DBSession()
# 利用session建立查詢,query(對象類).filter(條件).one()/all()
user = session.query(User).filter(User.id=='1001').one()
print('type:{0}'.format(type(user)))
print('name:{0}'.format(user.name))
# 關閉session
session.close()


# 更新
session = DBSession()
user_result = session.query(User).filter_by(id='1001').first()
user_result.name = "durant"
session.commit()
session.close()


# 刪除
session = DBSession()
user_willdel = session.query(User).filter_by(id='1001').first()
session.delete(user_willdel)
session.commit()
session.close()

  

四,查詢對象Query

  Session是sqlalchemy和數據庫交互的橋樑,Session提供了一個Query對象實現數據庫中數據的查詢操做。

4.1,常規查詢query

  直接指定類型進行查詢

user_list = session.query(User)
for user in user_list:
    print(user.name)

  

4.2,指定排序查詢

  經過類型的屬性指定排序方式

# 默認順序
user_list = session.query(User).order_by(User.id) 

# 指定倒序
user_list = session.query(User).order_by(-User.id)

# 多個字段
user_list = session.query(User).order_by(-User.id, User.name)

  

4.3,指定列查詢

  指定查詢數據對象的屬性,查詢目標數據

user_list = session.query(User, User.name).all()
for u in user_list:
    print(u.User, u.name)

  

4.4,指定列屬性別名

  對於名詞較長的字段屬性,能夠指定名稱在使用時簡化操做

user_list = session.query(User.name.label('n')).all()

for user in user_list:
    print(user.n)

  

4.5,指定類型別名

  對於類型名稱較長的狀況,一樣能夠指定別名進行處理

from sqlalchemy.orm import aliased

user_alias = aliased(User, name=’u_alias’)

user_list = session.query(u_alias, u_alias.name).all()
for u in user_list:
    print(u.u_alias, u.name)

  

4.6,切片查詢

  對於常常用於分頁操做單額切片查詢,在使用過程當中直接使用python內置的切片便可。

user_list = session.query(User).all()[1:3]

  

五,條件篩選filter

  上面主要對數據查詢對象query有一個比較直觀的感覺和操做,在實際使用過程當中常常用到條件查詢,主要經過filter 和 filter_by 進行操做,下面重點學習最爲頻繁的filter條件篩選函數。  

5.1,等值條件——equals / not equals

# equals    相等判斷
session.query(User).filter(User.id == 1)

# not equals   不等判斷
session.query(User).filter(User.name != ‘james’)

  

5.2,模糊條件——like

session.query(User).filter(User.name.like(‘%james%’))

  

5.3,範圍條件——in / not in

# IN
session.query(User).filter(User.id.in_([1,2,3,4]))

session.query(User).filter(User.name.in_([
     session.query(User.name).filter(User.id.in_[1,2,3,4])
]))


# NOT IN
session.query(User).filter(~User.id.in_([1,2,3]))

  

5.4,空值條件——is null / is not null

# IS NULL
session.query(User).filter(User.name == None)
session.query(User).filter(User.name.is_(None)) # pep8

# IS NOT NULL
session.query(User).filter(User.name != None)
session.query(User).filter(User.name.isnot(None)) # pep8

  

5.5,而且條件——and

from sqlalchemy import and_

session.query(User).filter(User.name=’james’).filter(User.age=12)
session.query(User).filter(User.name=’james, User.age=12)
session.query(User).filter(and_(User.name=’james’, User.age=12))

  

5.6,或者條件——or

from sqlalchemy import or_
session.query(User).filter(or_(User.name=’james’, User.name=’durant’))

  

5.7,SQL語句查詢

  某些特殊的狀況下,咱們也可能在本身的程序中直接使用sql語句進行操做。

from sqlalchemy import text

session.query(User).from_statement(
text(‘select * from users where name=:name and age=:age’))
.params(name=’james’, age=32).all()

  

5.8,filter() 和 filter_by()的區別

  首先看一個例子:

# load module
from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
import warnings

warnings.filterwarnings('ignore')

# 建立對象的基類
Base = declarative_base()

# 定義User對象
class User(Base):
    # 表的名字
    __tablename__ = 'user'

    # 表的結構
    id = Column(String(20), primary_key=True)
    name = Column(String(20))

engine = create_engine('mysql+pymysql://root:wangjian@localhost:3306/sqlalchemy')

DBSession = sessionmaker(bind=engine)
session = DBSession()

res1 = session.query(User).filter(User.id == 1001)
res2 = session.query(User).filter_by(id = 1001)

for i in res1:
    print(i.id, i.name)

for i in res2:
    print(i.id, i.name)

  結果以下:

1001 james
1001 james

  因此,從例子能夠看出,filter能夠像寫SQL的where條件那樣寫 < , > 等條件,但引用列名時,須要經過類名,屬性名的方式。 filter_by可使用python的正常參數傳遞條件,指定列名時,不須要額外指定類名,參數名對應類中的屬性名,不能用 < ,  > 等條件。

  filter不支持組合查詢,只能連續調用filter變相實現,filter_by的參數是 **kwargs,直接支持組合查詢。

filter_res = {'id':1002, 'name':'durant'}
res = session.query(User).filter_by(**filter_res)
for i in res:
    print(i.id, i.name)


結果:
1002 durant

  

六,查詢結果

6.1,all()函數返回查詢列表

  返回一個列表,能夠經過遍歷列表獲取每一個對象。

session.query(User).all()

  

6.2,filter()函數返回單項數據的列表生成器

session.query(User).filter(..)

  

6.3,one() / one_or_none() / scalar() 返回單獨的一個數據對象

  one()返回且僅返回一個查詢結果,當結果數量不足或者多餘一個時會報錯。

session.query(User).filter(..).one()/one_or_none()/scalar()

  

6.4,first() 返回一個單項結果

  返回至多一個結果,並且以單項形式,而不是隻有一個元素的tuple形式返回。

session.query(User).filter(User.id > 1001).first()

  

七,遇到的問題及其解決方法

7.1  sqlalchemy查詢中,若是沒有符合條件的結果,會返回一個空的對象,如何判斷這個對象是空的?

users = session.query(user).filter(user.id == '234).all()#users爲object列表
if(len(user) == 0):
    print "數據庫中沒有id爲234的用戶‘’
else:
    print "數據庫中包含id爲234的用戶「
#而後視狀況看是可能有多個返回值,仍是隻有一個返回值,(當id爲主鍵時只可能返回一個object數據)
users1 = users[0]

  解決問題的代碼:

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

warnings.filterwarnings("ignore")

Base = declarative_base()

class TrainInfo(Base):
    __tablename__ = 'phm'

    phmid = Column(String(20), primary_key=True)
    status = Column(Integer, default=0)


def MySQLConnect(connection_info:str, phmid, status):
    engine = create_engine(connection_info)
    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    traininfo = session.query(TrainInfo).filter(TrainInfo.phmid == phmid).one()
    if traininfo is None:
        info =TrainInfo(phmid=phmid, status=status)
        session.add(info)
    else:

        traininfo.status = '1'
    session.commit()
    session.close()

  

八,擴展知識

8.1 Python雙下劃線開頭的函數和變量

python 用下劃線做爲變量前綴和後綴指定特殊變量。

  • __XXX 不能用 ‘ from module  import * ’ 導入
  • __XXX__  系統定義名稱
  • __XXX     類中的私有變量名
  • 核心風格:避免用下劃線做爲變量名的開始。

  由於下劃線對解釋器有特殊的意義,並且是內建標識符所使用的符號,咱們建議程序員避免使用下劃線做爲變量名的開始。通常來講,變量名__XXX 被看作是「私有」的,在模塊或類外不可使用。當變量是私有的時候,用_XXX來表示變量是很好地習慣。由於變量名__XXX___對python來講有特殊含義,對於普通的變量應當避免這種命名風格。

  「單下劃線」開始的成員變量叫作保護變量,意思是隻有類對象和子類對象本身能訪問到這些變量;

  「雙下劃線」開始的是私有成員,意思是隻有類對象本身能訪問,連子類對象也不能訪問到這個數據。

  以單下劃線開頭(__foo)的表明不能直接訪問的類屬性,需經過類提供的接口進行訪問,不能用「from  XXX  import * 」而導入;

  以雙下劃線開頭的(__foo)表明類的私有成員;

  以雙下劃線開頭和結尾的(__foo__)表明python裏特殊方法專用的標識,如__init__() 表明類的構造函數。

8.2 if __name__  == ''__main__''

  全部的python模塊都是對象而且有幾個有用的屬性,你可使用這些屬性方便的測試你所書寫的模塊。

  模塊是對象,而且全部的模塊都有一個內置屬性__name__。一個模塊的__name__的值要你您如何應用模塊。若是import模塊,那麼__name__的值一般爲模塊的文件名,不帶路徑或者文件擴展名。可是咱們也能夠像一個標準的程序同樣直接運行模塊,在這種狀況下__name__的值將會是一個特別的缺省值:__main__。

 

參考文獻:https://www.jianshu.com/p/20593da77c04

原文出處:https://www.cnblogs.com/wj-1314/p/10627828.html

相關文章
相關標籤/搜索