SQLAlchemy 教程 —— 基础入门篇

SQLAlchemy 教程 —— 基础入门篇

1、课程简介

1.1 实验内容

本课程带领你们使用 SQLAlchemy 链接 MySQL 数据库,建立一个博客应用所须要的数据表,并介绍了使用 SQLAlchemy 进行简单了 CURD 操做及使用 Faker 生成测试数据。python

1.2课程知识点

  • 学会用 SQLALchemy 链接数据库(MySQL, SQLite, PostgreSQL), 建立数据表;
  • 掌握表数据之间一对一,一对多及多对多的关系并能转化为对应 SQLAlchemy 描述;
  • 掌握使用 SQLAlchemy 进行 CURD 操做;
  • 学会使用 Faker 生成测试数据

学习本课程须要你对 Python 与 MySQL 都有基本的掌握。mysql

2、ORM 与 SQLAlchemy 简介

ORM 全称 Object Relational Mapping, 翻译过来叫对象关系映射。简单的说,ORM 将数据库中的表与面向对象语言中的类创建了一种对应关系。这样,咱们要操做数据库,数据库中的表或者表中的一条记录就能够直接经过操做类或者类实例来完成。sql

orm1

SQLAlchemy 是Python 社区最知名的 ORM 工具之一,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型。shell

接下来咱们将使用 SQLAlchemy 和 MySQL 构建一个博客应用的实验库。数据库

先安装 SQLAlchemy:vim

$ sudo pip install sqlalchemy

3、链接与建立

实验楼环境已经为咱们安装了 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

orm2

看到上面的内容就说明咱们的 MySQL 能够正常启动了(注意,上面的密码不须要输入内容,直接回车就行), 而且咱们咱们经过命令:

> create database blog;

建立一个名为 blog 的数据库为下面的使用做准备。

另外,咱们须要安装一个 Python 与 MySQL 之间的驱动程序:

$ sudo apt-get install python-mysqldb

3.1 链接数据库

咱们在 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 数据库。

运行下这个程序,看到下面的信息说明咱们已经链接成功了:

orm3

3.2 描述表结构

要使用 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中,指定该列的一些配置。第一个字段表明类的数据类型,上面咱们使用 StringInteger 俩个最经常使用的类型,其余经常使用的包括:

Text
Boolean
SmallInteger
DateTime

nullable=False 表明这一列不能够为空,index=True 表示在该列建立索引。

另外定义 __repr__ 是为了方便调试,你能够不定义,也能够定义的更详细一些。

$ python db.py

运行程序,咱们在 MySQL 中看看表是如何建立的:

orm7

4、关系定义

4.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') 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 了!

4.2 一对一关系

在 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 。

4.3 多对多关系

一遍博客一般有一个分类,好几个标签。标签与博客之间就是一个多对多的关系。多对多关系不能直接定义,须要分解成俩个一对多的关系,为此,须要一张额外的表来协助完成:

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) 

4.4 映射到数据

表已经描述好了,在文件末尾使用下面的命令在咱们链接的数据库中建立对应的表:

if __name__ == '__main__':
    Base.metadata.create_all(engine)

进入 MySQL 看看:

orm8

全部的表都已经建立好了!

5、简单 CURD

当你想打电话给朋友时,你是否得用手机拨通他的号码才能创建起一个会话?一样的,你想和 MySQL 交谈也得先经过 SQLAlchemy 创建一个会话:

from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=engine)
session = Session()

你能够把 sessionmaker 想象成一个手机,engine 当作 MySQL 的号码,拨通这个“号码”咱们就建立了一个 Session 类,下面就能够经过这个类的实例与 MySQL 愉快的交谈了!

5.1 Create

若是你玩过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() 就能够了。

5.2 Retrieve

orm9

若是咱们知道用户 id,就能够用 get 方法, filter_by 用于按某一个字段过滤,而 filter 可让咱们按多个字段过滤,all 则是获取全部。

获取某一字段值能够直接类的属性获取:

orm10

5.3 Update

更新一个字段:

>>> 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() 

5.4 Delete

>>> 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() 

6、总结与习题

本篇教程带领你们使用 SQLAlchemy 建立了一个博客应用的数据库及相关表,再次过程当中, 介绍了使用 SQLAlchemy 定义一对1、一对多及多对多关系,CURD 及使用 Faker 生成测试数据。

习题

如今博客应用须要添加评论功能,使用 SQLAlchemy 建立一张评论表, 并使用 Faker 为每篇文章生成几条测试评论。

相关文章
相关标签/搜索