Python中操做SQLAlchemy

1、ORM 框架簡介

   對象-關係映射(Object/Relation Mapping,簡稱ORM),是隨着面向對象的軟件開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關係數據庫是企業級應用環境中永久存放數據的主流數據存儲系統。對象和關係數據是業務實體的兩種表現形式,業務實體在內存中表現爲對象,在數據庫中表現爲關係數據。內存中的對象之間存在關聯和繼承關係,而在數據庫中,關係數據沒法直接表達多對多關聯和繼承關係。所以,對象-關係映射(ORM)系統通常以中間件的形式存在,主要實現程序對象到關係數據庫數據的映射。node

1.ORM方法論基於三個核心原則:

  •     簡單性:以最基本的形式建模數據。
  •     傳達性:數據庫結構被任何人都能理解的語言文檔化。
  •     精確性:基於數據模型建立正確標準化了的結構。

面向對象是從軟件工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。爲了解決這個不匹配的現象,對象關係映射技術應運而生。O/R中字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。幾乎全部的程序裏面,都存在對象和關係數據庫。在業務邏輯層和用戶界面層中,咱們是面向對象的。當對象信息發生變化的時候,咱們須要把對象的信息保存在關係數據庫中。python

當開發一個應用程序的時候(不使用O/R Mapping),可能會寫很多數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息,等等。在DAL中寫了不少的方法來讀取對象數據,改變狀態對象等等任務。而這些代碼寫起來老是重複的。mysql


若是開你最近的程序,看看DAL代碼,確定會看到不少近似的通用的模式。咱們以保存對象的方法爲例,傳入一個對象,爲SqlCommand對象添加SqlParameter,把全部屬性和對象對應,設置SqlCommand的CommandText屬性爲存儲過程,而後運行SqlCommand。對於每一個對象都要重複的寫這些代碼。  除此以外,還有更好的辦法嗎?有,引入一個O/R Mapping。實質上,一個O/R Mapping會爲你生成DAL。與其本身寫DAL代碼,不如用O/R Mapping。用O/R Mapping保存,刪除,讀取對象,O/R Mapping負責生成SQL,你只須要關心對象就好。對象關係映射成功運用在不一樣的面向對象持久層產品中,sql

 2. 通常的ORM包括如下四部分:  

  •    一個對持久類對象進行CRUD操做的API;  
  •    一個語言或API用來規定與類和類屬性相關的查詢;  
  •    一個規定mapping metadata的工具;  
  •     一種技術可讓ORM的實現同事務對象一塊兒進行dirty checking, lazy association fetching以及其餘的優化操做。

•ORM:及Object-Relational Mapping,把關係數據庫的表結構映射到對象上數據庫

•咱們先來可能一個例子:數組

•若是咱們從數據庫查出來幾條數據,須要你在python中表示出來,若是你沒有接觸過ORM技術,你或許會使用下面的形式來存儲這個數據:安全

[
  (1, ‘feng’),
  (2, ‘shang’),
  (3, ‘huo’),
]

若是你想知道表結構是什麼樣的,是否是就費勁了,若是你想快速的取出其中的元素,就須要聽聽ORM的思想了。bash

 數據庫中每次查出來的數據都用一個類表示,這個類的屬性和數據庫中表的字段一一對應。多條數據,就是一個list,每一行數據都是一個類來表示,以下所示:session

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

[
    User(1, 「feng」),
    User(2, 「shang」),
    User(3, 「huo」),
]

當咱們須要得到id,或者name的時候,只須要經過循環獲取到對象,直接經過user1.id或者user1.name就能夠獲取到id和name的屬性。而且使得數據的存取很是的規範,這樣ORM架構應用而生。架構

2、SQLAlchemy介紹

Python中最有名的ORM架構就是SQLAlchemy,咱們主要就是來學習SQLAlchemy的使用


2.1 安裝環境:

pip install SQLAlchemy

2.2 安裝mysql

yum install mysql-server mysql
service mysqld restart
sysctmctl restart mysql.service

2.3 建立數據庫

create database sqlalchemy;

2.4 受權

GRANT ALL PRIVILEGES ON *.* TO 'fxq'@'%' IDENTIFIED BY ‘123456’;

2.5 初始化鏈接

from sqlalchemy import create_engine
engine = create_engine('mysql://fxq:123456@192.168.100.101/sqlalchemy', echo=True)

echo參數爲True時,會顯示每條執行的SQL語句,能夠關閉,
create_engine()返回一個Engine的實例,而且它表示經過數據庫語法處理細節的核心接口,在這種狀況下,數據庫語法將會被解釋成python的類方法。

    解釋說明:
    mysql://fxq:123456@192.168.100.101/sqlalchemy
    mysql:  指定是哪一種數據庫鏈接
    第一個fxq: 用戶名
    123456: fxq用戶對應的密碼
    192.168.100.101: 數據庫的ip
    sqlalchemy: 數據庫須要鏈接庫的名字

2.6 建立表格

1. 主要是經過sql語句來建立表格:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

sql = '''create table student(
    id int not null primary key,
    name varchar(50),
    age int,
    address varchar(100));
'''

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
conn = engine.connect()
conn.execute(sql)
engine.connect() #表示獲取到數據庫鏈接。相似咱們在MySQLdb中游標course的做用。

2. 經過ORM方式建立表格

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/10 21:58
# @Author  : Feng Xiaoqing
# @File    : demo2.py
# @Function: -----------

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
metadata = MetaData(engine)


student = Table('student', metadata,
            Column('id', Integer, primary_key=True),
            Column('name', String(50), ),
            Column('age', Integer),
            Column('address', String(10)),
)

metadata.create_all(engine)

以上方式均可以建立數據庫表
MetaData類主要用於保存表結構,鏈接字符串等數據,是一個多表共享的對象
metadata = MetaData(engine)    #綁定一個數據源的metadata
metadata.create_all(engine)         #是來建立表,這個操做是安全的操做,會先判斷表是否存在。

3. Table類

構造函數:

Table.__init__(self, name, metadata,*args, **kwargs)

name    表名
metadata      共享的元數據
*args Column 是列定義,詳見下一節
下面是可變參數 **kwargs 定義
schema 此表的結構名稱,默認None
autoload 自動從現有表中讀入表結構,默認False
autoload_with 從其餘engine讀取結構,默認None

include_columns 若是autoload設置爲True,則此項數組中的列明將被引用,沒有寫的列明將被忽略,None表示全部都列明都引用,默認None
mustexist 若是爲True,表示這個表必須在其餘的python應用中定義,必須是metadata的一部分,默認False
useexisting 若是爲True,表示這個表必須被其餘應用定義過,將忽略結構定義,默認False
owner 表全部者,用於Orcal,默認None
quote 設置爲True,若是代表是SQL關鍵字,將強制轉義,默認False
quote_schema  設置爲True,若是列明是SQL關鍵字,將強制轉義,默認False
mysql_engine  mysql專用,能夠設置'InnoDB'或'MyISAM'

4. Column類

構造函數:

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

一、name 列名
二、type_ 類型,更多類型 sqlalchemy.types
三、*args Constraint(約束),  ForeignKey(外鍵),  ColumnDefault(默認), Sequenceobjects(序列)定義
四、key 列名的別名,默認None
下面是可變參數 **kwargs
五、primary_key 若是爲True,則是主鍵
六、nullable 是否可爲Null,默認是True
七、default 默認值,默認是None
八、index 是不是索引,默認是True
九、unique 是否惟一鍵,默認是False
十、onupdate 指定一個更新時候的值,這個操做是定義在SQLAlchemy中,不是在數據庫裏的,當更新一條數據時設置,大部分用於updateTime這類字段
十一、autoincrement 設置爲整型自動增加,只有沒有默認值,而且是Integer類型,默認是True
十二、quote 若是列明是關鍵字,則強制轉義,默認False

5.建立會話:

說到數據庫,就離不開Session。Session的主要目的是創建與數據庫的會話,它維護你加載和關聯的全部數據庫對象。它是數據庫查詢(Query)的一個入口。
在Sqlalchemy中,數據庫的查詢操做是經過Query對象來實現的。而Session提供了建立Query對象的接口。
Query對象返回的結果是一組同一映射(Identity Map)對象組成的集合。事實上,集合中的一個對象,對應於數據庫表中的一行(即一條記錄)。所謂同一映射,是指每一個對象有一個惟一的ID。若是兩個對象(的引用)ID相同,則認爲它們對應的是相同的對象。
要完成數據庫查詢,就須要創建與數據庫的鏈接。這就須要用到Engine對象。一個Engine多是關聯一個Session對象,也可能關聯一個數據庫表。
固然Session最重要的功能仍是實現原子操做。
ORM經過session與數據庫創建鏈接進行通訊,以下所示:

from sqlalchemy.orm import sessionmaker

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

經過sessionmake方法建立一個Session工廠,而後在調用工廠的方法來實例化一個Session對象。

2.7 添加數據

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/10 20:58
# @Author  : Feng Xiaoqing
# @File    : demo1.py
# @Function: -----------

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


engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBsession = sessionmaker(bind=engine)
session = DBsession()

Base = declarative_base()

class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(100))
    age = Column(Integer)
    address = Column(String(100))

student1 = Student(id=1001, name='ling', age=25, address="beijing")
student2 = Student(id=1002, name='molin', age=18, address="jiangxi")
student3 = Student(id=1003, name='karl', age=16, address="suzhou")

session.add_all([student1, student2, student3])
session.commit()
session.close()

2.8 查詢

查詢是這個裏面最爲複雜,最爲繁瑣的一個步驟。
經過Session的query()方法建立一個查詢對象。這個函數的參數數量是可變的,參數能夠是任何類或者是類的描述的集合。下面來看一個例子:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 21:49
# @Author  : Feng Xiaoqing
# @File    : search.py
# @Function: -----------
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

my_stdent = session.query(Student).filter_by(name="fengxiaoqing2").first()
print(my_stdent)

此時咱們看到的輸出結果是這樣的:

<__main__.Student object at 0x032745F0>

前面咱們在賦值的時候,咱們能夠經過實例化一個對象,而後直接映射到數據庫中,那咱們在查詢出來的數據sqlalchemy直接給映射成一個對象了(或者是每一個元素爲這種對象的列表),對象和咱們建立表時候的class是一致的,咱們就也能夠直接經過對象的屬性就能夠直接調用就能夠了。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 21:49
# @Author  : Feng Xiaoqing
# @File    : search.py
# @Function: -----------
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

my_stdent = session.query(Student).filter_by(name="fengxiaoqing2").first()
print(my_stdent.id,my_stdent.name,my_stdent.age,my_stdent.address)

結果:

1000311 fengxiaoqing2 182 chengde


filter()  過濾表的條件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 21:49
# @Author  : Feng Xiaoqing
# @File    : search.py
# @Function: -----------
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()


my_stdent = session.query(Student).filter(Student.name.like("%feng%"))
print(my_stdent)

結果:

SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age, student.address AS student_address 
FROM student 
WHERE student.name LIKE %s

根據結果,咱們能夠看出來
filter_by最後的結果就是一個sql語句,咱們排錯的時候就能夠經過這個來排查咱們sql是否正確。
如下的這些過濾操做均可以在filter函數中使用:

equals:
query(Student).filter(Student.id == 10001)
not equals:
query(Student).filter(Student.id != 100)
LIKE:
query(Student).filter(Student.name.like(「%feng%」))

IN:
query(Student).filter(Student.name.in_(['feng', 'xiao', 'qing']))
not in
query(Student).filter(~Student.name.in_(['feng', 'xiao', 'qing']))

AND:
from sqlalchemy import and_
query(Student).filter(and_(Student.name == 'fengxiaoqing', Student.id ==10001))

或者
query(Student).filter(Student.name == 'fengxiaoqing').filter(Student.address == 'chengde')

OR:
from sqlalchemy import or_
query.filter(or_(Student.name == 'fengxiaoqing', Student.age ==18))

 

返回列表(List)和單項(Scalar)

all()  返回一個列表

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 21:49
# @Author  : Feng Xiaoqing
# @File    : search.py
# @Function: -----------
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

my_stdent = session.query(Student).filter(Student.name.like("%feng%")).all()
print(my_stdent)

結果:

[<__main__.Student object at 0x031405B0>, <__main__.Student object at 0x030FCA70>, <__main__.Student object at 0x031405F0>]

能夠經過遍歷列表來獲取每一個對象。
one()    返回且僅返回一個查詢結果。當結果的數量不足一個或者多於一個時會報錯。
把上面的all改爲one就報錯了。
first()    返回至多一個結果,並且以單項形式,而不是隻有一個元素的tuple形式返回這個結果.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 21:49
# @Author  : Feng Xiaoqing
# @File    : search.py
# @Function: -----------
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

my_stdent = session.query(Student).filter(Student.name.like("%feng%")).first()
print(my_stdent)

結果:

<__main__.Student object at 0x030A3610>

filter()和filter_by()的區別:

Filter:  能夠像寫 sql 的 where 條件那樣寫 > < 等條件,但引用列名時,須要經過 類名.屬性名 的方式。 
filter_by:  可使用 python 的正常參數傳遞方法傳遞條件,指定列名時,不須要額外指定類名。,參數名對應名類中的屬性名,但彷佛不能使用 > < 等條件。

當使用filter的時候條件之間是使用「==",fitler_by使用的是"="。

user1 = session.query(User).filter_by(id=1).first()
user1 = session.query(User).filter(User.id==1).first()

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

q = sess.query(IS).filter(IS.node == node and IS.password == password).all()

 

2.9 更新

更新就是查出來,直接更改就能夠了

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 22:27
# @Author  : Feng Xiaoqing
# @File    : update.py
# @Function: -----------

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

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

my_stdent = session.query(Student).filter(Student.id == 1002).first()
my_stdent.name = "fengxiaoqing"
my_stdent.address = "chengde"
session.commit()
student1 = session.query(Student).filter(Student.id == 1002).first()
print(student1.name, student1.address)

結果:

MariaDB [sqlalchemy]> select * from student;
+---------+---------------+------+---------+
| id      | name          | age  | address |
+---------+---------------+------+---------+
|    1002 | molin         |   18 | jiangxi |
|    1003 | karl          |   16 | suzhou  |
|  100011 | fengxiaoqing  |   18 | chengde |
|  100021 | fengxiaqing1  |  181 | chengde |
| 1000111 | fengxiaoqing  |   18 | chengde |
| 1000211 | fengxiaqing1  |  181 | chengde |
| 1000311 | fengxiaoqing2 |  182 | chengde |
+---------+---------------+------+---------+
7 rows in set (0.00 sec)

MariaDB [sqlalchemy]> select * from student;
+---------+---------------+------+---------+
| id      | name          | age  | address |
+---------+---------------+------+---------+
|    1002 | fengxiaoqing  |   18 | chengde |
|    1003 | karl          |   16 | suzhou  |
|  100011 | fengxiaoqing  |   18 | chengde |
|  100021 | fengxiaqing1  |  181 | chengde |
| 1000111 | fengxiaoqing  |   18 | chengde |
| 1000211 | fengxiaqing1  |  181 | chengde |
| 1000311 | fengxiaoqing2 |  182 | chengde |
+---------+---------------+------+---------+
7 rows in set (0.00 sec)

MariaDB [sqlalchemy]>

2.10 刪除

刪除其實也是跟查詢相關的,直接查出來,調用delete()方法直接就能夠刪除掉。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 22:27
# @Author  : Feng Xiaoqing
# @File    : update.py
# @Function: -----------

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

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()


session.query(Student).filter(Student.id == 1001).delete()
session.commit()
session.close()

2.11 統計、分組、排序

統計count()

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 22:27
# @Author  : Feng Xiaoqing
# @File    : update.py
# @Function: -----------

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

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

print(session.query(Student).filter(Student.name.like("%feng%")).count())

分組 group_by()

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 22:27
# @Author  : Feng Xiaoqing
# @File    : update.py
# @Function: -----------

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

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

std_group_by = session.query(Student).group_by(Student.age)
print(std_group_by)

結果的sql語句以下:

SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age, student.address AS student_address 
FROM student GROUP BY student.age

排序 order_by()     反序在order_by裏面用desc()方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/5/11 22:27
# @Author  : Feng Xiaoqing
# @File    : update.py
# @Function: -----------

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

Base = declarative_base()
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    age = Column(Integer)
    address = Column(String(100))

engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy')
DBSession = sessionmaker(bind=engine)
session = DBSession()

std_ord_desc = session.query(Student).filter(Student.name.like("%feng%")).order_by(Student.id.desc()).all()
for i in std_ord_desc:
  print(i.id)

結果:

1000311
1000211
1000111
100021
100011
1002
相關文章
相關標籤/搜索