本課程帶領你們使用 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 爲每篇文章生成幾條測試評論。