Peewee轻量级ORM
支持 MySQL、PostgreSQL、Sqlite、BerkeleyDB等常见数据库,相信能够满足大多数需求了
官方文档十分详尽,虽然都是英文的,这个模块有太多可以挖掘的东西,目前发现基本涵盖了所有需要的数据库的功能
官方文档
关于ORM,摘抄自维基百科
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在>编程语言里使用的“虚拟对象数据库”。如今已有很多免费和收费的ORM产品,而有些程序员更倾向于创建自己的ORM工具。
面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。
简单的说:ORM相当于中继数据。具体到产品上,例如下边的ADO.NET Entity Framework。DLINQ中实体类的属性[Table]就算是一种中继数据。
对象关系映射成功运用在不同的面向对象持久层产品中,如:Torque,OJB,Hibernate,TopLink,Castor JDO,TJDO,Active Record,NHibernate,ADO.NET Entity Framework 等。
说白了,就是当你需要用SQL数据库的时候怎么办?尤其是需要快速大量的操纵数据的时候怎么办,很明显直接学习和使用SQL不是特别的方便。ORM就是来解决这个问题的,将复杂的SQL语言转化为开发者用的顺手的语种,以Python的Peewee为例,它将每个SQL中的表都转化为一个类,对类进行操作,对于Python写手来说,一点也不复杂了吧。
安装模块
- 最基本的安装方法,不推荐,Linux还好,依赖问题好解决。Windows的话,八成会要求安装一个什么C++的解释器,总之,我没折腾懂怎么解决
pip install peewee
- 从源码装 github(https://github.com/coleifer/peewee),我觉着吧,应该也会有依赖问题难以解决
git clone https://github.com/coleifer/peewee.git
cd peewee
python setup.py install
- 官方没有提到,但是我最推荐,最方便的安装方法,anaconda,没听说过的请移步
conda install -c conda-forge peewee
- 官方文档中还有其他的安装方法,比如cpython等,由于不常用,就不说了
基础知识
peewee将数据库的不同部分映射为了类(官方文档称之为Model)的不同成分
peewee将数据库中对应的各种类别,转化为了模块中的各种Filed,比如
还有一个需要提一下的是ForeignKeyFiled, 方便将两个table union到一起的
from peewee import Model, CharField, ForeignKeyField
class Category(Model):
name = CharField()
parent = ForeignKeyField('self', related_name='children', null=True)
每个Filed都有一些参数可以设置:
- null = False,当前这个Field是否可以为null值
- unique = False, 当前这个Field中的值是否唯一,比如id
- default = None,指定一个默认值
更多
关于Sqlite多说几句(其他数据库可不可以这样用,不清楚,没试过)
一下是关于commit的问题,默认Sqlite是自动commit的,问题就是巨慢,因此连接数据库时可以指定autocommit为False,但是就需要用一下三种方式手动进行commit,后边还有一个atomic()功能与transaction()一致
# Define a database with autocommit turned off.
db = SqliteDatabase('my_app.db', autocommit=False)
# You must call begin()
db.begin()
User.create(username='charlie')
db.commit()
# If using a transaction, then no changes are necessary.
with db.transaction():
User.create(username='huey')
# If using a function decorated by transaction, no changes are necessary.
@db.transaction()
def create_user(username):
User.create(username=username)
言归正传,开始举例
基本使用方法
我使用Sqlite数据库来做展示,Sqlite可以直接导入导出CSV文件,官方也给出了解决方法 【详情】
然而,我还是决定自己处理,毕竟有很多数据需要修饰什么的
第一次接触这个模块是从《Python高效开发实战Django Tornado Flask Twisted》(刘长龙著),对它的第一印象就是貌似很简单(?),因此,就直接沿着这个模块用下来了。
以Human protein atlas下载到的细胞系表达量数据为例,简单说明一些用法
下载到一个CSV文件,总大小51.3M
一共包含五列数据,分别是ensembl_id,gene_name, tissue, value,value_type
1. 连接数据库
第一步,先指定一个数据库,新建各种所需的model
#!/usr/bin/python3
# -*- coding: utf-8 -*-
u"""peewee数据库."""
import os
from peewee import Model
from peewee import SqliteDatabase
from peewee import CharField, FloatField
# autocommint顾名思义,就是没进行一个操作数据库是否自己commit一次,默认True
# 如果要进行大量的insert或者update操作的话,特别耗时,不过不要紧,peewee提供了解决方法
db = SqliteDatabase(os.path.join(
os.path.dirname(__file__), 'expression_value.db'), autocommit=True)
# 一个基础Model,没别的作用
# 如果一个数据库中有多个不同的表,其中很多内容有一样,就可以通过基础类全部指定好
# 然后不同的Model简单的一继承就好了
class BaseModel(Model):
u"""基础类,指定数据库和基本数据类型.s"""
tissue = CharField()
ensembl_id = CharField()
gene_name = CharField()
value = FloatField()
class Meta:
u"""指定数据库."""
database = db
# 用来储存HPA数据的那张表,建立一些新的独有的列
# 表是按照tissue和ensembl_id来排序
class HPA(BaseModel):
u"""存储HPA表达量数据的."""
value_type = CharField(default='TPM')
class Meta:
u"""指定表."""
db_table = 'HPA' # table name
order_by = ('tissue', 'ensembl_id', )
以上一个数据库和一张表就搞定了
2. 插入数据的基础方法
首先自然是读取文件,根据自己的文件格式,自己调整代码
我读取csv的方式如open_csv()所示
class Bulk(object):
u"""测试bulk."""
def __init__(self, csv):
u"""csv."""
self.csv = csv
def open_csv(self):
u"""打开csv文件,然后返回信息."""
with open(self.csv) as reader:
for line in reader:
# 去除换行符
line = line.rstrip()
# csv每列按照,分隔,每一列还用“包围,因此,分割再分割就好了
yield [x.replace('"', '') for x in line.split(',')]
@staticmethod
def creat_new_table(table):
u"""用来创建table的."""
# 如果,该Model代表的table没有被创建,就新建一个
if not table.table_exists():
table.create_table()
def normal_insert(self):
u"""最普通的insert方式."""
# 创建HPA这个表
self.creat_new_table(HPA)
# 最基本的插入方法就是create(),而且由于autocommit是True,因此我们无需手动进行db.commit()
for line in self.open_csv():
# try 用来处理表头无法转化成float的问题,遇到表头就略过
try:
HPA.create(ensembl_id=line[0], gene_name=line[1],
tissue=line[2], value=float(line[4]))
except ValueError:
continue
create()是peewee中最基础的数据插入方式,但同时,也是最慢的,尤其是autocommit=True的情况下
有多慢呢?
这个50+M的csv,我用了两天多才搞定,这两天反正电脑一直在用,就这么浪吧。
3. 插入数据的进阶方法
peewee是支持直接将字典导入数据库的,因此,有dict_to_model和model_to_dict两个类方法,能够实现SQL与Python内置类型以及json的交互,然而,这次我需要用字典,但是用不上这两个方法,感兴趣可以自己查
首先需要对所有的数据进行修饰,将其转化成字典
u"""接上回,想要加快导入的速度,必须使用字典导入的方式,用法如下"""
def construct_dict(self):
u"""按照field: value构成字典"""
# 这些键值,都是数据库中定义的列名
keys = ['ensembl_id', 'gene_name', 'tissue', 'value', 'value_type']
# 将每一行的数据都转换成一个字典
for line in self.open_csv():
try:
line[3] = float(line[3])
except ValueError:
continue
yield {key: value for key, value in zip(keys, line)}
constract_dict()返回的结果如下,每一行的数据都转化成一个对应的字典
{
‘ensembl_id’: 'EN*******',
'gene_name': 'TP53',
'tissue': 'liver',
'value': 5,
'value_type': 'TPM'
}
然后,将其导入数据库,根据官方文档的示例修改而来,确实有用,速度快多了
# ticktock是自己写的一个装饰器,用来展示function运行时间的
@ticktock()
def insert_(self):
u"""测试atomic()."""
# 新建表
self.creat_new_table(HPA)
# 上下文管理一下,使用了atomic()之后,就会在上下文退出的时候才最终commit
# 减少了中间最耗时的反复commit过程
with db.atomic():
# 然后将处理好的字典一次导入数据库
for data_dict in self.construct_dict():
HPA.create(**data_dict)
运行时间,一共两次,第一次autocommit=False,第二次True
表明,在atomic()的光辉下,autocommit不autocommit的对性能不缠身根本性的影响
蓝色的输出就是ticktock()的能力了
4. 插入数据,再快点?
然而,根据官方文档来看,上边那个还不是最快的,最快的是insert_many()这个方法
需要手动execute的来了
@ticktock()
def insert_many(self):
u"""测试insert many."""
self.creat_new_table(HPA)
# 基本上就是,将每一行的数据转化成一个字典,然后这些个字典构成一个list
# 直接导入,即可
data = [x for x in self.construct_dict()]
with db.atomic():
HPA.insert_many(data).execute()
然而,我一次导入的量太大,崩溃了
因此,如果量太大,还是老老实实的按照官网的说明,将所有的数据分成chunks来处理
@ticktock()
def insert_many(self):
u"""测试insert many."""
self.creat_new_table(HPA)
# 最懒的写法,内存耗费最大,完全浪费了生成器节省内存的有点,我图什么???
data = [x for x in self.construct_dict()]
with db.atomic():
# 100个100个的导入总没问题了吧
for idx in range(0, len(data), 100):
HPA.insert_many(data[idx: idx + 100]).execute()
事实胜于雄辩,速度确实有提升了非常多
所以,最开始,我用create,慢慢create了两天,现在一分半就解决了,我图什么呢???
更多原创精彩视频敬请关注生信杂谈: