一、ORM介绍
ORM 全称 Object Relational Mapping
, 翻译过来叫对象关系映射
。简单的说,ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系。这样,我们要操作数据库,数据库中的表或者表中的一条记录就可以直接通过操作类或者类实例来完成。ORM 相当于把数据库也给你实例化了,在代码操作MySQL中级又加了orm这一层。
ORM的优点:
- 隐藏了数据访问细节,使得我们的通用数据库交互变得简单易行,并且完全不用考虑SQL语句
- ORM使我们构造固化数据结构变得简单易行
二、sqlalchemy安装
SQLAlchemy 是Python 社区最知名的 ORM 工具之一,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型。
在windows下通过pip安装SQLAlchemy(这里我们使用的是python3.5):
pipinstallsqlalchemy pip install pymysql
#由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互
三、连接与创建
在命令行下输入下面命令启动 MySQL:
$ mysql -uroot -p
然后创建数据库blog:
> create database blog;
1.连接数据库
新建个 Python 文件 db_link_test.py ,
# coding: utf-8
from sqlalchemy import create_engine
# 创建实例,并连接blog库
engine = create_engine('mysql+pymysql://root:0208@localhost:3306/blog?charset=utf8')
print(engine)
然后运行程序,如果输出下面信息,即说明连接成功。
Engine(mysql+pymysql://root:*@localhost:3306/blog?charset=utf8)
2.描述表结构
要使用 ORM, 我们需要将数据表的结构用 ORM 的语言描述出来,我们以一个users表为例,
# coding: utf-8
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer
engine = create_engine('mysql+pymysql://root:0208@localhost:3306/blog?charset=utf8')
Base = declarative_base()
class User(Base):
# __tablename__ 指定在 MySQL 中表的名字
__tablename__ = 'users'
# Column 代表数据库中的一列
id = Column(Integer, primary_key=True)
username = Column(String(64), nullable=False, index=True)
password = Column(String(64), nullable=False)
email = Column(String(64), nullable=False, index=True)
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.username)
- __tablename__ 指定在 MySQL 中表的名字,
- Column 代表数据库中的一列,
- nullable=False 代表这一列不可以为空,index=True 表示在该列创建索引。
四、关系定义
1、一对多关系
对于一个普通的博客应用来说,用户和文章显然是一个一对多的关系,一篇文章属于一个用户,一个用户可以写很多篇文章,那么他们之间的关系可以这样定义:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(64), nullable=False, index=True)
password = Column(String(64), nullable=False)
email = Column(String(64), nullable=False, index=True)
# 一对多:
articles = relationship('Article')
class Article(Base):
__tablename__ = 'articles'
id = Column( Integer, primary_key =True )
title = Column( String(255) , nullable=False , index = True )
content = Column(Text)
# “多”的一方的 articles 表是通过外键关联到 users 表的:
user_id = Column(Integer,ForeignKey('users.id'))
author = relationship('User')
说明:(1).每篇文章有一个外键指向 users 表中的主键 id, 而在 User 中使用 SQLAlchemy 提供的 relationship 描述 关系。而用户与文章的之间的这个关系是双向的,所以我们看到上面的两张表中都定义了 relationship。
SQLAlchemy 提供了 backref 让我们可以只需要定义一个关系:
articles = relationship(‘Article’, backref=’author’)
添加了这个就可以不用再在 Article 中定义 relationship 了!所以上面也可以这么写:
class User(Base):
...
articles = relationship('Article' , backref='author')
class Article(Base):
...
(2).通过relationship中的backref字段反向查出所有它在User表里的关联项数据.
def Query2(session):
print(session.query(Article).get(1) )
print(session.query(Article).get(1).author )
此处session指会话,后面再提,第一句是查询articles表中的第一行,第二句是通过找到articles表中的第一行中的外键user_id字段,再根据这个字段数字找到users表中id所在行。
图示:articles表
users表:
输出结果如下:
Article(‘Tempore cumque esse sed.’)
User(‘Carmen Valencia’)
2、一对一关系
在 User 中我们只定义了几个必须的字段, 但通常用户还有很多其他信息,但这些信息可能不是必须填写的,我们可以把它们放到另一张 UserInfo 表中,这样 User 和 UserInfo 就形成了一对一的关系。你可能会奇怪一对一关系为什么不在一对多关系前面?那是因为一对一关系是基于一对多定义的,定义方法和一对多相同,只是需要添加 userlist=False 。
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(64), nullable=False, index=True)
password = Column(String(64), nullable=False)
email = Column(String(64), nullable=False, index=True)
articles = relationship('Article' , backref='author')
userinfo = relationship('UserInfo',backref='user',uselist=False)
class UserInfo(Base):
__tablename__ = 'userinfos'
id = Column(Integer, primary_key =True)
name = Column( String(64))
qq = Column(String(11))
phone=Column(String(11))
link=Column(String(64))
user_id = Column(Integer,ForeignKey('users.id'))
3、多对多关系
一遍博客通常有一个分类,好几个标签。标签与博客之间就是一个多对多的关系。多对多关系不能直接定义,需要分解成俩个一对多的关系,为此,需要一张额外的表 (即article_tag) 来协助完成:
# 中间表 自己创建。不需要手动管理,orm自动维护
article_tag = Table(
'article_tag',Base.metadata,
Column('article_id',Integer,ForeignKey('articles.id')),
Column('tag_id',Integer,ForeignKey('tags.id'))
)
class Tag(Base):
__tablename__= 'tags'
id = Column(Integer,primary_key=True)
name = Column( String(64) , nullable=False , index = True)
class Article(Base):
__tablename__ = 'articles'
id = Column( Integer, primary_key =True )
title = Column( String(255) , nullable=False , index = True )
content = Column(Text)
user_id = Column(Integer,ForeignKey('users.id'))
cate_id = Column(Integer, ForeignKey('categories.id'))
# articles 表不知道第三张表,所以关联一下第三张表
tags = relationship('Tag', secondary='article_tag', backref='articles')
说明:创建了article_tag表,有两个字段 article_id 和 tag_id 。
4、映射到数据
表已经描述好了,在文件末尾使用下面的命令在我们连接的数据库中创建对应的表:
if __name__ == '__main__':
Base.metadata.create_all(engine) #创建表结构 (这里是父类调子类)