本课程带领你们使用 SQLAlchemy 链接 MySQL 数据库,建立一个博客应用所须要的数据表,并介绍了使用 SQLAlchemy 进行简单了 CURD 操做及使用 Faker 生成测试数据。python
学习本课程须要你对 Python 与 MySQL 都有基本的掌握。mysql
ORM 全称 Object Relational Mapping
, 翻译过来叫对象关系映射
。简单的说,ORM 将数据库中的表与面向对象语言中的类创建了一种对应关系。这样,咱们要操做数据库,数据库中的表或者表中的一条记录就能够直接经过操做类或者类实例来完成。sql
SQLAlchemy 是Python 社区最知名的 ORM 工具之一,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型。shell
接下来咱们将使用 SQLAlchemy 和 MySQL 构建一个博客应用的实验库。数据库
先安装 SQLAlchemy:vim
$ sudo pip install sqlalchemy
实验楼环境已经为咱们安装了 MySQL,但尚未启动, 在启动 MySQL 以前,咱们须要进行一些配置,将 MySQL 默认的 latin1 编码改为 utf8 。ruby
$ sudo vim /etc/mysql/my.cnf
经过上面的命令打开 MySQL 的配置文件, 添加下面几个配置:bash
[client] default-character-set = utf8 [mysqld] character-set-server = utf8 [mysql] default-character-set = utf8
保存退出。如今咱们能够启动 MySQL 服务了:session
$ sudo service mysql start
在命令行下输入下面命令启动 MySQL:app
$ mysql -uroot -p
看到上面的内容就说明咱们的 MySQL 能够正常启动了(注意,上面的密码不须要输入内容,直接回车就行), 而且咱们咱们经过命令:
> create database blog;
建立一个名为 blog
的数据库为下面的使用做准备。
另外,咱们须要安装一个 Python 与 MySQL 之间的驱动程序:
$ sudo apt-get install python-mysqldb
咱们在 Code 下新建个 Python 文件,叫什么名字就随你便了,这里咱们叫 db.py
,写入下面的内容:
# coding: utf-8 from sqlalchemy import create_engine engine = create_engine('mysql+mysqldb://root@localhost:3306/blog') print(engine)
在上面的程序中,咱们链接了默认运行在 3306
端口的 MySQL 中的 blog
数据库。
运行下这个程序,看到下面的信息说明咱们已经链接成功了:
要使用 ORM, 咱们须要将数据表的结构用 ORM 的语言描述出来。SQLAlchmey 提供了一套 Declarative 系统来完成这个任务。咱们以建立一个 users
表为例,看看它是怎么用 SQLAlchemy 的语言来描述的:
# 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+mysqldb://root@localhost:3306/blog?charset=utf8') Base = declarative_base() 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) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.username)
咱们看到,在 User
类中,用 __tablename__
指定在 MySQL 中表的名字。咱们建立了三个基本字段,类中的每个 Column
表明数据库中的一列,在 Colunm
中,指定该列的一些配置。第一个字段表明类的数据类型,上面咱们使用 String
, Integer
俩个最经常使用的类型,其余经常使用的包括:
Text Boolean SmallInteger DateTime
nullable=False
表明这一列不能够为空,index=True
表示在该列建立索引。
另外定义 __repr__
是为了方便调试,你能够不定义,也能够定义的更详细一些。
$ python db.py
运行程序,咱们在 MySQL 中看看表是如何建立的:
对于一个普通的博客应用来讲,用户和文章显然是一个一对多的关系,一篇文章属于一个用户,一个用户能够写不少篇文章,那么他们之间的关系能够这样定义:
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') def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.username) 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')) author = relationship('User') def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.title)
每篇文章有一个外键指向 users
表中的主键 id
, 而在 User
中使用 SQLAlchemy 提供的 relationship
描述 关系。而用户与文章的之间的这个关系是双向的,因此咱们看到上面的两张表中都定义了 relationship
。
SQLAlchemy 提供了 backref
让咱们能够只须要定义一个关系:
articles = relationship('Article', backref='author')
添加了这个就能够不用再在 Article
中定义 relationship
了!
在 User
中咱们只定义了几个必须的字段, 但一般用户还有不少其余信息,但这些信息可能不是必须填写的,咱们能够把它们放到另外一张 UserInfo
表中,这样User
和 UserInfo
就造成了一对一的关系。你可能会奇怪一对一关系为何不在一对多关系前面?那是由于一对一关系是基于一对多定义的:
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) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.username) 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'))
定义方法和一对多相同,只是须要添加 userlist=False
。
一遍博客一般有一个分类,好几个标签。标签与博客之间就是一个多对多的关系。多对多关系不能直接定义,须要分解成俩个一对多的关系,为此,须要一张额外的表来协助完成:
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) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.name)
表已经描述好了,在文件末尾使用下面的命令在咱们链接的数据库中建立对应的表:
if __name__ == '__main__': Base.metadata.create_all(engine)
进入 MySQL 看看:
全部的表都已经建立好了!
当你想打电话给朋友时,你是否得用手机拨通他的号码才能创建起一个会话?一样的,你想和 MySQL 交谈也得先经过 SQLAlchemy 创建一个会话:
from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine) session = Session()
你能够把 sessionmaker
想象成一个手机,engine
当作 MySQL 的号码,拨通这个“号码”咱们就建立了一个 Session 类,下面就能够经过这个类的实例与 MySQL 愉快的交谈了!
若是你玩过LOL, 我想你必定知道Faker。而在 Python的世界中,Faker 是用来生成虚假数据的库。 安装它:
$ sudo pip install faker
下面结合 Faker 库建立一些测试数据:
faker = Factory.create()
Session = sessionmaker(bind=engine)
session = Session()
faker_users = [User(
username=faker.name(),
password=faker.word(),
email=faker.email(),
) for i in range(10)] session.add_all(faker_users) faker_categories = [Category(name=faker.word()) for i in range(5)] session.add_all(faker_categories) faker_tags= [Tag(name=faker.word()) for i in range(20)] session.add_all(faker_tags) for i in range(100): article = Article( title=faker.sentence(), content=' '.join(faker.sentences(nb=random.randint(10, 20))), author=random.choice(faker_users), category=random.choice(faker_categories) ) for tag in random.sample(faker_tags, random.randint(2, 5)): article.tags.append(tag) session.add(article) session.commit()
在上面的代码中咱们建立了10个用户,5个分类,20个标签,100篇文章,而且为每篇文章随机选择了2~5个标签。
使用 SQLAlchemy 往数据库中添加数据,咱们只须要建立相关类的实例,调用 session.add()
添加一个,或者 session.add_all()
一次添加多个, 最后 session.commit()
就能够了。
若是咱们知道用户 id,就能够用 get
方法, filter_by
用于按某一个字段过滤,而 filter
可让咱们按多个字段过滤,all
则是获取全部。
获取某一字段值能够直接类的属性获取:
更新一个字段:
>>> a = session.query(Article).get(10) >>> a.title = 'My test blog post' >>> session.add(a) >>> session.commit()
添加一个标签:
>>> a = session.query(Article).get(10) >>> a.tags.append(Tag(name='python')) >>> session.add(a) >>> session.commit()
>>> a = session.query(Article).get(10) >>> session.delete(a) >>> session.commit()
删除直接调用 delete
删除获取到的对象,提交 session 便可。
# coding: utf-8 import random from faker import Factory from sqlalchemy import create_engine, Table from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import ForeignKey from sqlalchemy import Column, String, Integer, Text from sqlalchemy.orm import sessionmaker, relationship engine = create_engine('mysql+mysqldb://root@localhost:3306/blog?charset=utf8') Base = declarative_base() 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) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.username) 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')) 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')) tags = relationship('Tag', secondary='article_tag', backref='articles') def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.title) class Category(Base): __tablename__ = 'categories' id = Column(Integer, primary_key=True) name = Column(String(64), nullable=False, index=True) articles = relationship('Article', backref='category') def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.name) 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) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.name) if __name__ == '__main__': Base.metadata.create_all(engine) faker = Factory.create() Session = sessionmaker(bind=engine) session = Session() faker_users = [User( username=faker.name(), password=faker.word(), email=faker.email(), ) for i in range(10)] session.add_all(faker_users) faker_categories = [Category(name=faker.word()) for i in range(5)] session.add_all(faker_categories) faker_tags= [Tag(name=faker.word()) for i in range(20)] session.add_all(faker_tags) for i in range(100): article = Article( title=faker.sentence(), content=' '.join(faker.sentences(nb=random.randint(10, 20))), author=random.choice(faker_users), category=random.choice(faker_categories) ) for tag in random.sample(faker_tags, random.randint(2, 5)): article.tags.append(tag) session.add(article) session.commit()
本篇教程带领你们使用 SQLAlchemy 建立了一个博客应用的数据库及相关表,再次过程当中, 介绍了使用 SQLAlchemy 定义一对1、一对多及多对多关系,CURD 及使用 Faker 生成测试数据。
如今博客应用须要添加评论功能,使用 SQLAlchemy 建立一张评论表, 并使用 Faker 为每篇文章生成几条测试评论。