@Scrazy
2017-03-27T08:43:10.000000Z
字数 4890
阅读 1280
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 -pMariaDB [(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)]> quitBye
下面。我们把常量放到一个独立的文件 consts.py 中:
# coding=utf-8HOSTNAME = ''DATABASE = 'd'USERNAME = 'lambda'PASSWORD = 'plus'DB_URL = 'mysql://{}:{}@{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, DATABASE)
In [22]: import MySQLdbIn [10]: from consts import HOSTNAME, USERNAME, PASSWORD, DATABASEIn [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]: 0In [26]: cur.execute('insert into user (id, name) values (%s, %s)', ['1', 'Lambda'])Out[26]: 1In [27]: cur.rowcountOut[27]: 1In [35]: cur = conn.cursor()In [36]: cur.execute('select * from user where id = %s',('1',))Out[36]: 1In [37]: values = cur.fetchall()In [38]: valuesOut[38]: (('1', 'Lambda'),)In [39]: cur.close()In [40]: conn.close()
数据库操作主要以 CRUD 为主。CRUD 是 Create 、Read、Update 和 Delete 的缩写。看一个例子 crud.py :
# coding=utf-8import MySQLdbfrom consts import USERNAME, HOSTNAME, DATABASE, PASSWORDconn = 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 = idself.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-8from sqlalchemy import create_engine, Column, Integer, String, ForeignKeyfrom sqlalchemy.orm import sessionmaker, relationshipfrom sqlalchemy.ext.declarative import declarative_basefrom consts import DB_URLengine = 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].titleOut[7]: 'HTTP: The Definitive Guide'In [14]: session.query(Author).join(Book).filter(Book.title=='一条狗的修养').first().nameOut[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: 2Book title: 一条狗的使命
所有文件都在 mysql_pre 目录下:
$︎︎ tree mysql_pre/mysql_pre/├── consts.py├── curd.py└── orm_sql.py0 directories, 3 files
参考:
[1] Arch Wiki
[2] 董伟明. Python Web 开发实战. 北京:电子工业出版社,2016
[3] 廖雪峰的网站
[4] SQLAlchemy