python之SQLAlchemy

摘要:
用户包括openstack\Dropbox等知名公司或应用,主要用户列表http://www.sqlalchemy.org/organizations.html#openstackDialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:12345678910111213MySQL-Pythonmysql+mysqldb://:@[:]/pymysqlmysql+pymysql://:@/[?key=value&key=value...]更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html安装sqlalchemy1pipinstallSQLAlchemypipinstallpymysql#由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互导入SQLAlchemy,并初始化DBSession:#导入:fromsqlalchemyimportColumn,String,create_enginefromsqlalchemy.ormimportsessionmakerfromsqlalchemy.ext.declarativeimportdeclarative_base#创建对象的基类:Base=declarative_base()#定义User对象:classUser:#表的名字:__tablename__='user'#表的结构:id=Columnname=Column#初始化数据库连接:engine=create_engine#创建DBSession类型:DBSession=sessionmaker以上代码完成SQLAlchemy的初始化和具体每个表的class定义。

ORM介绍

orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。

python之SQLAlchemy第1张

orm的优点:

  1. 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
  2. ORM使我们构造固化数据结构变得简单易行。

缺点:

  1. 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。

2. sqlalchemy安装

在Python中,最有名的ORM框架是SQLAlchemy。用户包括openstack\Dropbox等知名公司或应用,主要用户列表http://www.sqlalchemy.org/organizations.html#openstack

python之SQLAlchemy第2张

Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

安装sqlalchemy

1
pipinstallSQLAlchemy
pipinstallpymysql#由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互

导入SQLAlchemy,并初始化DBSession:

#导入:
from sqlalchemy importColumn, String, create_engine
from sqlalchemy.orm importsessionmaker
from sqlalchemy.ext.declarative importdeclarative_base

#创建对象的基类:
Base =declarative_base()

#定义User对象:
classUser(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,例如School:

classSchool(Base):
    __tablename__ = 'school'id =...
    name = ...

create_engine()用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息:

数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'

你只需要根据需要替换掉用户名、口令等信息即可。

下面,我们看看如何向数据库表中添加一行记录。

由于有了ORM,我们向数据库表中添加一行记录,可以视为添加一个User对象:

#创建session对象:
session =DBSession()
#创建新User对象:
new_user = User(id='5', name='Bob')
#添加到session:
session.add(new_user)
#提交即保存到数据库:
session.commit()
#关闭session:
session.close()

可见,关键是获取session,然后把对象添加到session,最后提交并关闭。DBSession对象可视为当前数据库连接。

如何从数据库表中查询数据呢?有了ORM,查询出来的可以不再是tuple,而是User对象。SQLAlchemy提供的查询接口如下:

#创建Session:
session =DBSession()
#创建Query查询,filter是where条件,最后调用one()返回唯一行,如果调用all()则返回所有行:
user = session.query(User).filter(User.id=='5').one()
#打印类型和对象的name属性:
print('type:', type(user))
print('name:', user.name)
#关闭Session:
session.close()

运行结果如下:

type: <class '__main__.User'>
name: Bob

可见,ORM就是把数据库表的行与相应的对象建立关联,互相转换。

由于关系数据库的多个表还可以用外键实现一对多、多对多等关联,相应地,ORM框架也可以提供两个对象之间的一对多、多对多等功能。

除上面的创建之外,还有一种创建表的方式,虽不学用,但还是看看吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fromsqlalchemyimportTable, MetaData, Column, Integer, String, ForeignKey
fromsqlalchemy.ormimportmapper
metadata=MetaData()
user=Table('user', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('fullname', String(50)),
Column('password', String(12))
)
classUser(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

最基本的表我们创建好了,那我们开始用orm创建一条数据试试

1
2
3
4
5
6
7
8
9
10
11
Session_class=sessionmaker(bind=engine)#创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session=Session_class()#生成session实例
user_obj=User(name="alex",password="alex3714")#生成你要创建的数据对象
print(user_obj.name,user_obj.id)#此时还没创建对象呢,不信你打印一下id发现还是None
Session.add(user_obj)#把要创建的数据对象添加到这个session里, 一会统一创建
print(user_obj.name,user_obj.id)#此时也依然还没创建
Session.commit()#现此才统一提交,创建数据

我擦,写这么多代码才创建一条数据,你表示太tm的费劲了,正要转身离开,我拉住你的手不放开,高潮还没到。。

查询

1
2
my_user=Session.query(User).filter_by(name="alex").first()
print(my_user)

此时你看到的输出是这样的应该

1
<__main__.Userobjectat0x105b4ba90>

我擦,这是什么?这就是你要的数据呀, 只不过sqlalchemy帮你把返回的数据映射成一个对象啦,这样你调用每个字段就可以跟调用对象属性一样啦,like this..

1
2
3
4
print(my_user.id,my_user.name,my_user.password)
输出
1alex alex3714

不过刚才上面的显示的内存对象对址你是没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码

1
2
3
def__repr__(self):
return"<User(name='%s', password='%s')>"%(
self.name,self.password)

修改

1
2
3
4
5
my_user=Session.query(User).filter_by(name="alex").first()
my_user.name="Alex Li"
Session.commit()

回滚

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() )#这时看session里有你刚添加和修改的数据
Session.rollback()#此时你rollback一下
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() )#再查就发现刚才添加的数据没有了。
# Session
# Session.commit()

获取所有数据

1
print(Session.query(User.name,User.id).all() )

多条件查询

1
objs=Session.query(User).filter(User.id>0).filter(User.id<7).all()

上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果

统计和分组

1
Session.query(User).filter(User.name.like("Ra%")).count()

分组

1
2
fromsqlalchemyimportfunc
print(Session.query(func.count(User.name),User.name).group_by(User.name).all() )

相当于原生sql为

1
2
SELECT count(user.name) AS count_1, user.name AS user_name
FROM user GROUP BY user.name

输出为

[(1, 'Jack'), (2, 'Rain')]

外键关联

我们创建一个addresses表,跟user表关联

1
2
3
4
5
6
7
8
9
10
11
12
13
fromsqlalchemyimportForeignKey
fromsqlalchemy.ormimportrelationship
classAddress(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

Therelationship.back_populatesparameter is a newer version of a very common SQLAlchemy feature calledrelationship.backref. Therelationship.backrefparameter hasn’t gone anywhere and will always remain available! Therelationship.back_populatesis the same thing, except a little more verbose and easier to manipulate. For an overview of the entire topic, see the sectionLinking Relationships with Backref.  

表创建好后,我们可以这样反查试试

1
2
3
4
5
6
obj=Session.query(User).first()
foriinobj.addresses:#通过user对象反查关联的addresses记录
print(i)
addr_obj=Session.query(Address).first()
print(addr_obj.user.name)#在addr_obj里直接查关联的user表

创建关联对象

1
2
3
4
5
6
7
8
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()

常用查询语法

Common Filter Operators

Here’s a rundown of some of the most common operators used in filter():

  • equals:

         query.filter(User.name == 'ed')
    
  • not equals:

         query.filter(User.name != 'ed')
    
  • LIKE:

    query.filter(User.name.like('%ed%'))

  • IN:

  • NOT IN:
    query.filter(~User.name.in_(['ed', 'wendy', 'jack']))

  • IS NULL:

  • IS NOT NULL:

  • AND:
    2.1. ObjectRelationalTutorial 17

query.filter(User.name.in_(['ed', 'wendy', 'jack']))
# works with query objects too:

query.filter(User.name.in_( session.query(User.name).filter(User.name.like('%ed%'))

))

query.filter(User.name == None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))

SQLAlchemy Documentation, Release 1.1.0b1

# use and_()

from sqlalchemy import and_
query.filter(and_(User.name == 'ed', User.fullname == 'Ed Jones'))

# or send multiple expressions to .filter()
query.filter(User.name == 'ed', User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')

Note: Makesureyouuseand_()andnotthePythonandoperator! • OR:

Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH:

query.filter(User.name.match('wendy'))
Note: match() uses a database-specific MATCH or CONTAINS f

4.多外键关联

One of the most common situations to deal with is when there are more than one foreign key path between two tables.

Consider aCustomerclass that contains two foreign keys to anAddressclass:

下表中,Customer表有2个字段都关联了Address表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fromsqlalchemyimportInteger, ForeignKey, String, Column
fromsqlalchemy.ext.declarativeimportdeclarative_base
fromsqlalchemy.ormimportrelationship
Base=declarative_base()
classCustomer(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")
classAddress(Base):
__tablename__='address'
id=Column(Integer, primary_key=True)
street=Column(String)
city=Column(String)
state=Column(String)

创建表结构是没有问题的,但你Address表中插入数据时会报下面的错

1
2
3
4
5
6
sqlalchemy.exc.AmbiguousForeignKeysError: Couldnotdetermine 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 alistof those columns which should be
counted as containing a foreign key reference to the parent table.

解决办法如下

1
2
3
4
5
6
7
8
9
10
classCustomer(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就能分清哪个外键是对应哪个字段了

用orm如何表示呢?

python之SQLAlchemy第3张python之SQLAlchemy第4张
#一本书可以有多个作者,一个作者又可以出版多本书


from sqlalchemy importTable, Column, Integer,String,DATE, ForeignKey
from sqlalchemy.orm importrelationship
from sqlalchemy.ext.declarative importdeclarative_base
from sqlalchemy importcreate_engine
from sqlalchemy.orm importsessionmaker


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')),
                        )

classBook(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):
        returnself.name

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

    def __repr__(self):
        return self.name
orm 多对多

接下来创建几本书和作者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Session_class=sessionmaker(bind=engine)#创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
s=Session_class()#生成session实例
b1=Book(name="跟Alex学Python")
b2=Book(name="跟Alex学把妹")
b3=Book(name="跟Alex学装逼")
b4=Book(name="跟Alex学开车")
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表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
mysql> select*frombooks;
+----+------------------+----------+
|id| name | pub_date |
+----+------------------+----------+
|1| 跟Alex学Python | NULL |
|2| 跟Alex学把妹 | NULL |
|3| 跟Alex学装逼 | NULL |
|4| 跟Alex学开车 | NULL |
+----+------------------+----------+
4rowsinset(0.00sec)
mysql> select*fromauthors;
+----+------+
|id| name |
+----+------+
|10| Alex |
|11| Jack |
|12| Rain |
+----+------+
3rowsinset(0.00sec)
mysql> select*frombook_m2m_author;
+---------+-----------+
| book_id | author_id |
+---------+-----------+
|2|10|
|2|11|
|2|12|
|1|10|
|1|11|
+---------+-----------+
5rowsinset(0.00sec)

此时,我们去用orm查一下数据

1
2
3
4
5
6
7
8
9
print('--------通过书表查关联的作者---------')
book_obj=s.query(Book).filter_by(name="跟Alex学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()

输出如下

1
2
3
4
--------通过书表查关联的作者---------
跟Alex学Python [Alex, Jack]
--------通过作者表查关联的书---------
Alex [跟Alex学把妹, 跟Alex学Python]

牛逼了我的哥!!完善实现多对多  

多对多删除

删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除

通过书删除作者

1
2
3
4
5
6
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()

直接删除作者 

删除作者时,会把这个作者跟所有书的关联关系数据也自动删除

1
2
3
4
author_obj=s.query(Author).filter_by(name="Alex").first()
# print(author_obj.name , author_obj.books)
s.delete(author_obj)
s.commit()

免责声明:文章转载自《python之SQLAlchemy》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇如何修改已有的ONNX模型Latex 中插入 Matlab 代码下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

把一个json字符串转换成对应的c#类型

放弃使用Newtonsoft,自己动手。usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Reflection; usingSystem.Text.RegularExpressions; namespaceConsol...

利用C#的反射机制动态调用DLL类库

最近由于业务要求,需要动态调用DLL类库,所以研究了一下,感觉还好也不太难,今天就把自己理解的写了一个小例子(已经通过VS2005跑通),供大家一起研究和探讨,有理解不当的地方还请高手们多多指正,谢谢啦! 好,在这之前我先把反射所需要使用的几个类给大家列一下: 1、使用Assembly类定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型...

JSON数据的处理中的特殊字符

     JSON如今是非经常见的处理数据的方式了。但因为自己使用的是反射获取数据,必须自己处理特殊字符,但总是发现有一些看不见的字符在前台 var obj = jQuery.parseJSON(msg);会转换失败。     比如例如以下在Vs中能够看到仅仅有两个字符    可实际上却有三个字符,使用notepad++打开 一直不明确这些字符是怎样进...

Python基础:数值(布尔型、整型、长整型、浮点型、复数)

一、概述 Python中的 数值类型(Numeric Types)共有5种:布尔型(bool)、整型(int)、长整型(long)、浮点型(float)和复数(complex)。 数值类型支持的主要操作如下: 操作 说明 bool int long float complex x ** y 指数运算 √ √ √ √ √ +x 符号不变 √...

C语言操作mysql

php中 mysqli, pdo 可以用 mysqlnd 或 libmysqlclient 实现 前者 从 php 5.3.0起已内置到php中, 并且支持更多的特性,推荐用 mysqlnd mysqlnd , libmysqlclient 对比:http://php.net/manual/en/mysqlinfo.library.choosing.ph...

marshaller unmarshaller解析xml和读取xml

JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而...