@Scrazy
2017-03-27T08:43:10.000000Z
字数 4890
阅读 1192
python
数据持久化通常有一下几种方式:
1. 文件
2. 数据库系统
3. 其他
接下来,我们就专注于第二种方式:数据库
MySQL 是目前应用最广泛的数据库。开源、免费、操作方便使得它很流行。
首先安装 MySQL
$ sudo pacman -S mysql
选择 MariaDB 的实现版本,也是 Arch 官方仓库推荐的。安装完成后要执行一句:
mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
启动其守护进程
$ systemctl start mysqld
$ mysql_secure_installation
$ systemctl restart mysqld
MySQL 的 Python 驱动有好几个,我们选用 MySQLdb:
$ pip install mysqlclient
mysqlclient 是 MySQLdb 的 Python3 版本。
接下来,我们执行以下步骤:
1. 创建一个用户 名为 lambda 密码为 plus。
2. 创建一个数据库 d。
3. 设置用户权限,赋予用户 lambda 对数据库 d 有全部权限。
$︎︎ mysql -u root -p
MariaDB [(none)]> create User 'lambda'@'localhost' identified by 'plus';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> create database d;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> grant ALl privileges on d.* To 'lambda'@'localhost';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> quit
Bye
下面。我们把常量放到一个独立的文件 consts.py 中:
# coding=utf-8
HOSTNAME = ''
DATABASE = 'd'
USERNAME = 'lambda'
PASSWORD = 'plus'
DB_URL = 'mysql://{}:{}@{}/{}'.format(
USERNAME, PASSWORD, HOSTNAME, DATABASE)
In [22]: import MySQLdb
In [10]: from consts import HOSTNAME, USERNAME, PASSWORD, DATABASE
In [23]: conn = MySQLdb.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)
In [24]: cur = conn.cursor()
In [25]: cur.execute('create table user (id varchar(20) primary key, name varchar(20))')
Out[25]: 0
In [26]: cur.execute('insert into user (id, name) values (%s, %s)', ['1', 'Lambda'])
Out[26]: 1
In [27]: cur.rowcount
Out[27]: 1
In [35]: cur = conn.cursor()
In [36]: cur.execute('select * from user where id = %s',('1',))
Out[36]: 1
In [37]: values = cur.fetchall()
In [38]: values
Out[38]: (('1', 'Lambda'),)
In [39]: cur.close()
In [40]: conn.close()
数据库操作主要以 CRUD 为主。CRUD 是 Create 、Read、Update 和 Delete 的缩写。看一个例子 crud.py :
# coding=utf-8
import MySQLdb
from consts import USERNAME, HOSTNAME, DATABASE, PASSWORD
conn = MySQLdb.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)
with conn as cur:
cur.execute('drop table if exists users')
cur.execute('create table users (id int primary key auto_increment, '
'name varchar(20))')
cur.execute('insert into users(name) values ("haohao")')
cur.execute('insert into users(name) values ("haha")')
rows = cur.fetchall()
for row in rows:
print(row)
cur.execute('update users set name=%s where id=%s', ('hao', 1))
print('Number of rows updated:', cur.rowcount)
cur = conn.cursor()
cur.execute('select * from users')
rows = cur.fetchall()
for row in rows:
print(row[0], row[1])
ORM 全称对象关系映射 (Object Relational Mapping),其可以让我们用 Python 类的方式做数据库开发,不需要写 SQL 语句。
上面的例子中,返回的结果用 Python 的数据结构表示出来的话,可以用一个 list 表示多行,list 的每一个元素是 tuple,表示一行记录。也就是下面这种形式:
[
('1', 'hao'),
('2', 'ha'),
]
但是用 tuple 表示一行很难看出表的结构。如果把一个 tuple 用 class 实例来表示,就可以更容易地看出表的结构来:
class User(object):
def __init__(self, id, name):
self.id = id
self.name = name
[
User('1', 'hao'),
User('2', 'ha'),
]
所谓的 对象关系映射 把关系数据库的表结构映射到对象上。
而且:
因为随着项目的增大会暴露出一些问题:
1. SQL 语句重复利用率不高
2. 很多 SQL 语句是在业务逻辑中拼接出来的,数据库的改动,需要对这些逻辑非常了解。
况且:
ORM 还有以下优点:
1. 易用性。有效减少重复 SQL 语句出现的概率,写出的模型直观、清晰
2. 性能损耗低。使用 ORM 会有些许的性能损失,但相对于其便利性,大多数情况下大可以忽略
3. 可移植。比如 SQLAlchemy 支持多个数据库引擎,比如 MySQL、Postgresql 和 SQLite 等。切换数据库很方便。
4. 设计灵活。可以轻松的实现复杂的数据查询。
Python 中,最流行的 ORM 框架非 SQLAlchemy 莫属了。我们就使用它,首先来安装 SQLAlchemy :
$ pip install sqlalchemy
看例子:
# coding=utf-8
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base
from consts import DB_URL
engine = create_engine(DB_URL)
Base = declarative_base()
class Author(Base):
__tablename__ = 'author'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(100), nullable=False)
author_id = Column(Integer, ForeignKey('author.id'))
author = relationship('Author')
Author.books = relationship('Book', order_by=Book.id)
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()
author_haohao = Author(name='hahao')
author_duoduo = Author(name='duoduo')
author_haohao.books = [Book(title=u'Python Web 开发实战', author_id=author_haohao.id),
Book(title=u'Flask Web Development', author_id=author_haohao.id),
Book(title=u'HTTP: The Definitive Guide', author_id=author_haohao.id)]
author_duoduo.books = [Book(title=u'一条狗的修养', author_id=author_duoduo.id),
Book(title=u'忠犬八公', author_id=author_duoduo.id),
Book(title=u'一条狗的使命', author_id=author_duoduo.id)]
session.add_all([author_haohao, author_duoduo])
session.commit()
Book 表的 author_id 就是 Author 的 id 字段,使用 ForeignKey 关联之后,就不需要在 Book 上独立存储一份 user_id 数据了。我们接着验证:
$ ipython -i mysql_pre/orm_sql.py --profile=web_develop
In [7]: session.query(Book).join(Author).filter(Book.id.in_([3])).all()[0].title
Out[7]: 'HTTP: The Definitive Guide'
In [14]: session.query(Author).join(Book).filter(Book.title=='一条狗的修养').first().name
Out[14]: 'duoduo'
In [17]: for a, b in session.query(Author, Book).\
...: filter(Author.id==Book.author_id).\
...: filter(Book.title=='一条狗的使命').all():
...: print('Author ID: {}'.format(a.id))
...: print('Book title: {}'.format(b.title))
...:
Author ID: 2
Book title: 一条狗的使命
所有文件都在 mysql_pre 目录下:
$︎︎ tree mysql_pre/
mysql_pre/
├── consts.py
├── curd.py
└── orm_sql.py
0 directories, 3 files
参考:
[1] Arch Wiki
[2] 董伟明. Python Web 开发实战. 北京:电子工业出版社,2016
[3] 廖雪峰的网站
[4] SQLAlchemy