本课程带领大家使用 SQLAlchemy 连接 MySQL 数据库,创建一个博客应用所需要的数据表,并介绍了使用 SQLAlchemy 进行简单了 CURD 操作及使用 Faker 生成测试数据。
学习本课程需要你对 Python 与 MySQL 都有基本的掌握。
ORM 全称
, 翻译过来叫
- Object Relational Mapping
。简单的说,ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系。这样,我们要操作数据库,数据库中的表或者表中的一条记录就可以直接通过操作类或者类实例来完成。
- 对象关系映射
SQLAlchemy 是 Python 社区最知名的 ORM 工具之一,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型。
接下来我们将使用 SQLAlchemy 和 MySQL 构建一个博客应用的实验库。
先安装 SQLAlchemy:
- $ sudo pip install sqlalchemy
实验楼环境已经为我们安装了 MySQL,但还没有启动, 在启动 MySQL 之前,我们需要进行一些配置,将 MySQL 默认的 latin1 编码改成 utf8 。
- $ sudo vim /etc/mysql/my.cnf
通过上面的命令打开 MySQL 的配置文件, 添加下面几个配置:
- [client]
- default-character-set = utf8
- [mysqld]
- character-set-server = utf8
- [mysql]
- default-character-set = utf8
保存退出。现在我们可以启动 MySQL 服务了:
- $ sudo service mysql start
在命令行下输入下面命令启动 MySQL:
- $ 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)
在上面的程序中,我们连接了默认运行在
端口的 MySQL 中的
- 3306
数据库。
- blog
运行下这个程序,看到下面的信息说明我们已经连接成功了:
要使用 ORM, 我们需要将数据表的结构用 ORM 的语言描述出来。SQLAlchmey 提供了一套 Declarative 系统来完成这个任务。我们以创建一个
表为例,看看它是怎么用 SQLAlchemy 的语言来描述的:
- 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+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
指定在 MySQL 中表的名字。我们创建了三个基本字段,类中的每一个
- __tablename__
代表数据库中的一列,在
- 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
中使用 SQLAlchemy 提供的
- User
描述 关系。而用户与文章的之间的这个关系是双向的,所以我们看到上面的两张表中都定义了
- 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
当做 MySQL 的号码,拨通这个 "号码" 我们就创建了一个 Session 类,下面就可以通过这个类的实例与 MySQL 愉快的交谈了!
- engine
如果你玩过 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()
来源: