最近在學習tornado,發現沒有Orm模型。其自帶的torndb模塊不支持python3,學習一下SQLAlchemy供之後工做中使用。python
本文學習使用 SQLAlchemy 鏈接 MySQL 數據庫,建立一個博客應用所須要的數據表,並介紹了使用 SQLAlchemy 進行簡單了 CURD 操做及使用 Faker 生成測試數據。mysql
1.1 知識要點sql
ORM 全稱 Object Relational Mapping, 翻譯過來叫對象關係映射。簡單的說,ORM 將數據庫中的表與面嚮對象語言中的類創建了一種對應關係。這樣,咱們要操做數據庫,數據庫中的表或者表中的一條記錄就能夠直接經過操做類或者類實例來完成。數據庫
SQLAlchemy 是Python 社區最知名的 ORM 工具之一,爲高效和高性能的數據庫訪問設計,實現了完整的企業級持久模型。bash
接下來咱們將使用 SQLAlchemy 和 MySQL 構建一個博客應用的實驗庫。session
安裝SQLAlchemyapp
pip install sqlalchemy
複製代碼
數據庫咱們採用Mysql,安裝過程這裏省略。可參考個人lnmp安裝步驟。tornado
啓動mysql服務工具
systemctl start mysqld
複製代碼
進入數據庫命令行post
mysql
複製代碼
更改數據庫受權,遠程主機可訪問
update mysql.user set Host='%' where HOST='localhost' and User='root';
複製代碼
接下來咱們使用圖形化數據庫操做工具(Navicat Premium)來操做數據庫 建立一個blog的數據庫
安裝數據庫驅動
pip install pymysql
複製代碼
3.1 鏈接數據庫
新建一個db.py的文件,寫入下面的內容:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'junxi'
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('mysql+pymysql://blog:123456@localhost:3306/blog?charset=utf8')
Base = declarative_base()
print(engine)
複製代碼
運行:
Engine(mysql+pymysql://blog:***@localhost:3306/blog?charset=utf8)
複製代碼
3.2 描述表結構
要使用 ORM, 咱們須要將數據表的結構用 ORM 的語言描述出來。SQLAlchmey 提供了一套 Declarative 系統來完成這個任務。咱們以建立一個 users 表爲例,看看它是怎麼用 SQLAlchemy 的語言來描述的:
編輯db.py:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'junxi'
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, Text, Boolean, DateTime, ForeignKey, Table
from sqlalchemy.orm import relationship, sessionmaker
engine = create_engine('mysql+pymysql://blog:123456@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)
if __name__ == '__main__':
Base.metadata.create_all(engine)
複製代碼
咱們看到,在 User 類中,用 __tablename__
指定在 MySQL 中表的名字。咱們建立了三個基本字段,類中的每個 Column 表明數據庫中的一列,在 Colunm中,指定該列的一些配置。第一個字段表明類的數據類型,上面咱們使用 String, Integer 倆個最經常使用的類型,其餘經常使用的包括:
Text Boolean SmallInteger DateTime ForeignKey nullable=False 表明這一列不能夠爲空,index=True 表示在該列建立索引。
另外定義__repr__
是爲了方便調試,你能夠不定義,也能夠定義的更詳細一些。
運行 db.py
運行程序,咱們在Mysql命令行中看看錶是如何建立的:
C:\Windows\system32>mysql -ublog -p123456
mysql> use blog;
Database changed
mysql> show create table users\G;
*************************** 1. row ***************************
Table: users
Create Table: CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`email` varchar(64) NOT NULL,
PRIMARY KEY (`id`),
KEY `ix_users_username` (`username`),
KEY `ix_users_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
複製代碼
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, name="標題")
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')
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 ArticleTag(Base):
__tablename__ = 'article_tag'
id = Column(Integer, primary_key=True)
article_id = Column(Integer, ForeignKey('articles.id'))
tag_id = Column(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 映射到數據庫
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = 'junxi'
""" mysql://username:password@hostname/database postgresql://username:password@hostname/database sqlite:////absolute/path/to/database sqlite:///c:/absolute/path/to/database """
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, Text, Boolean, DateTime, ForeignKey, Table
from sqlalchemy.orm import relationship, sessionmaker
engine = create_engine('mysql+pymysql://blog:123456@localhost:3306/blog?charset=utf8')
Base = declarative_base()
# print(engine)
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')
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"))
author = relationship('User')
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.title)
""" # 這是建立表的另外一種寫法 article_tag = Table( 'article_tag', Base.metadata, Column('article_id', Integer, ForeignKey('articles.id')), Column('tag_id', Integer, ForeignKey('tags.id')) ) """
class ArticleTag(Base):
__tablename__ = 'article_tag'
id = Column(Integer, primary_key=True)
article_id = Column(Integer, ForeignKey('articles.id'))
tag_id = Column(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> use blog;
Database changed
mysql> show tables;
+----------------+
| Tables_in_blog |
+----------------+
| article_tag |
| articles |
| tags |
| userinfos |
| users |
+----------------+
5 rows in set (0.00 sec)
複製代碼
全部的表都已經建立好了!