赞
踩
一、SQLAlchemy简介
官方文档地址:
The Database Toolkit for Python
SQLAlchemy 是python中,通过ORM操作数据库的框架。简单点来说,就是帮助我们从烦冗的sql语句中解脱出来,从而不需要再去写原生的sql语句,只需要用python的语法来操作对象,就能被自动映射为sql语句。
它有几个不同的组件,可以单独使用或组合在一起。其主要组件依赖关系组织如下图所示:
Schema / Types
类到表之间的映射规则SQL Expression Language
SQL 语句Engine
引擎Connection Pooling
连接池Dialect
方言,调用不同的数据库 API(Oracle, postgresql, Mysql) 并执行对应的 SQL语句
通过PIP安装
pip install SQLAlchemy
使用setup.py安装
python setup.py install
任何SQLAlchemy应用程序的开始都是一个Engine对象,此对象充当连接到特定数据库的中心源,提供被称为connection pool的对于这些数据库连接。
Engine对象通常是一个只为特定数据库服务器创建一次的全局对象,并使用一个URL字符串进行配置,该字符串将描述如何连接到数据库主机或后端。
- >>> from sqlalchemy import create_engine
- >>> engine = create_engine('sqlite:///:memory:', echo=True)
create_engine的参数有很多,我列一些比较常用的:
repr()
其参数列表的默认日志处理程序。utf-8
PoolListener
将接收连接池事件的对象。也就是我们在Python中创建的一个类,对应着数据库中的一张表,类的每个属性,就是这个表的字段名。
这种的类对应于数据库中表的类,就称为映射类,我们要创建一个映射类,是基于基类定义的,每个映射类都要继承这个基类 declarative_base()。
- >>> from sqlalchemy.orm import declarative_base
-
- >>> Base = declarative_base()
既然我们有了一个“基”类,就可以根据它定义任意数量的映射类。
我们将新建一张名为users
的表,也就是用户表。一个名为User
类将是我们映射此表的类。在类中,我们定义了要映射到的表的详细信息,主要是表名以及列的名称和数据类型:
- >>> from sqlalchemy import Column, Integer, String
- >>> class User(Base):
- ... __tablename__ = 'users'
- ...
- ... id = Column(Integer, primary_key=True)
- ... name = Column(String)
- ... fullname = Column(String)
- ... nickname = Column(String)
- ...
- ... def __repr__(self):
- ... return "<User(name='%s', fullname='%s', nickname='%s')>" % (
- ... self.name, self.fullname, self.nickname)
__tablename__ 代表表名
Column : 代表数据表中的一列,内部定义了数据类型
primary_key:主键
通过定义User
类,我们已经定义了关于表的信息,称为table metadata,也就是表的元数据。我们可以通过检查__table__
属性:
- >>> User.__table__
- Table('users', MetaData(),
- Column('id', Integer(), table=<users>, primary_key=True, nullable=False),
- Column('name', String(), table=<users>),
- Column('fullname', String(), table=<users>),
- Column('nickname', String(), table=<users>), schema=None)
开始创建表:
- >>> Base.metadata.create_all(engine)
- BEGIN...
- CREATE TABLE users (
- id INTEGER NOT NULL,
- name VARCHAR,
- fullname VARCHAR,
- nickname VARCHAR,
- PRIMARY KEY (id)
- )
- [...] ()
- COMMIT
映射完成后,现在让我们创建一个User
对象的实例:
- >>> ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname')
- >>> ed_user.name
- 'ed'
- >>> ed_user.nickname
- 'edsnickname'
- >>> str(ed_user.id)
- 'None'
此时,实例对象只是在环境的内存中有效,并没有在表中真正生成数据。
- >>> from sqlalchemy.orm import sessionmaker
- >>> Session = sessionmaker(bind=engine)
- # 实例化
- >>> session = Session()
我们对表的所有操作,都是通过会话实现的。
- >>> ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname')
- >>> session.add(ed_user)
这里我们新增了一个用户,此时这个数据并没有被同步的数据库中,而是处于等待的状态。
只有执行了 commit()
方法后,才会真正在数据表中创建数据。
如果我们查询数据库,则首先刷新所有待处理信息,然后立即发出查询。
- >>> our_user = session.query(User).filter_by(name='ed').first()
- >>> our_user
- <User(name='ed', fullname='Ed Jones', nickname='edsnickname')>
此时得到的结果也并不是数据库表中的最终数据,而是映射类的一个对象。
在 commit() 之前,对实例对象的属性所做的更改,可以进行回滚,回到更改之前。
>>> session.rollback()
本质上只是把某一条数据(也就是映射类的实例)从内存中删除而已,并没有对数据库有任何操作。
通过 query 关键字查询。
- >>> for instance in session.query(User).order_by(User.id):
- ... print(instance.name, instance.fullname)
- ed Ed Jones
- wendy Wendy Williams
- mary Mary Contrary
- fred Fred Flintstone
query.join() 连接查询
- >>> session.query(User).join(Address).\
- ... filter(Address.email_address=='jack@google.com').\
- ... all()
- [<User(name='jack', fullname='Jack Bean', nickname='gjffdd')>]
query(column.label()) 可以为字段名(列)设置别名:
- >>> for row in session.query(User.name.label('name_label')).all():
- ... print(row.name_label)
- ed
- wendy
- mary
- fred
aliased()为查询对象设置别名:
- >>> from sqlalchemy.orm import aliased
- >>> user_alias = aliased(User, name='user_alias')
- SQL>>> for row in session.query(user_alias, user_alias.name).all():
- ... print(row.user_alias)
- <User(name='ed', fullname='Ed Jones', nickname='eddie')>
- <User(name='wendy', fullname='Wendy Williams', nickname='windy')>
- <User(name='mary', fullname='Mary Contrary', nickname='mary')>
- <User(name='fred', fullname='Fred Flintstone', nickname='freddy')>
- # 等于
- query.filter(User.name == 'ed')
-
- # 不等于
- query.filter(User.name != 'ed')
-
- # like和ilike
- query.filter(User.name.like('%ed%'))
- query.filter(User.name.ilike('%ed%')) # 不区分大小写
-
- # in
- query.filter(User.name.in_(['ed', 'wendy', 'jack']))
- query.filter(User.name.in_(
- session.query(User.name).filter(User.name.like('%ed%'))
- ))
- # not in
- query.filter(~User.name.in_(['ed', 'wendy', 'jack']))
-
- # is
- query.filter(User.name == None)
- query.filter(User.name.is_(None))
-
- # is not
- query.filter(User.name != None)
- query.filter(User.name.is_not(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.name == 'wendy'))
-
- # match
- query.filter(User.name.match('wendy'))
文字字符串可以灵活地用于Query
查询。
- >>> from sqlalchemy import text
- SQL>>> for user in session.query(User).\
- ... filter(text("id<224")).\
- ... order_by(text("id")).all():
- ... print(user.name)
- ed
- wendy
- mary
- fred
使用冒号指定绑定参数。要指定值,请使用Query.params()
方法:
- >>> session.query(User).filter(text("id<:value and name=:name")).\
- ... params(value=224, name='fred').order_by(User.id).one()
- <User(name='fred', fullname='Fred Flintstone', nickname='freddy')>
一个用户可以有多个邮件地址,意味着我们要新建一个表与用户表进行映射和查询。
- >>> from sqlalchemy import ForeignKey
- >>> from sqlalchemy.orm import relationship
- >>> class Address(Base):
- ... __tablename__ = 'addresses'
- ... id = Column(Integer, primary_key=True)
- ... email_address = Column(String, nullable=False)
- ... user_id = Column(Integer, ForeignKey('users.id'))
- ...
- ... user = relationship("User", back_populates="addresses")
- ...
- ... def __repr__(self):
- ... return "<Address(email_address='%s')>" % self.email_address
-
- >>> User.addresses = relationship(
- ... "Address", order_by=Address.id, back_populates="user")
ForeignKey
定义两列之间依赖关系,表示关联了用户表的用户ID
relationship 告诉ORMAddress
类本身应链接到User
类,back_populates 表示引用的互补属性名,也就是本身的表名。
除了表的一对多,还存在多对多的关系,例如在一个博客网站中,有很多的博客BlogPost
,每篇博客有很多的Keyword
,每一个Keyword
又能对应很多博客。
对于普通的多对多,我们需要创建一个未映射的Table
构造以用作关联表。如下所示:
- >>> from sqlalchemy import Table, Text
- >>> # association table
- >>> post_keywords = Table('post_keywords', Base.metadata,
- ... Column('post_id', ForeignKey('posts.id'), primary_key=True),
- ... Column('keyword_id', ForeignKey('keywords.id'), primary_key=True)
- ... )
下一步我们定义BlogPost
和Keyword
,使用互补 relationship 构造,每个引用post_keywords
表作为关联表:
- >>> class BlogPost(Base):
- ... __tablename__ = 'posts'
- ...
- ... id = Column(Integer, primary_key=True)
- ... user_id = Column(Integer, ForeignKey('users.id'))
- ... headline = Column(String(255), nullable=False)
- ... body = Column(Text)
- ...
- ... # many to many BlogPost<->Keyword
- ... keywords = relationship('Keyword',
- ... secondary=post_keywords,
- ... back_populates='posts')
- ...
- ... def __init__(self, headline, body, author):
- ... self.author = author
- ... self.headline = headline
- ... self.body = body
- ...
- ... def __repr__(self):
- ... return "BlogPost(%r, %r, %r)" % (self.headline, self.body, self.author)
-
-
- >>> class Keyword(Base):
- ... __tablename__ = 'keywords'
- ...
- ... id = Column(Integer, primary_key=True)
- ... keyword = Column(String(50), nullable=False, unique=True)
- ... posts = relationship('BlogPost',
- ... secondary=post_keywords,
- ... back_populates='keywords')
- ...
- ... def __init__(self, keyword):
- ... self.keyword = keyword
多对多关系的定义特征是secondary
关键字参数引用Table
表示关联表的对象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。