Python折腾数据库(一)peewee

SQL数据库恐怕没几个人能够绕过去,自己折腾跑分析也有几个年头了回头想想之前就是吃了没有充分利用数据库来管理数据的亏。

二的话,有缘再见,最近想学一下SQLAlchemy

先推荐两个数据库的图形化查看软件

  • dbeaver ***** 全平台 除了驱动不太好下,其他基本完美
  • sqlite-browser *** 全平台 仅支持Sqlite,数据一多还会卡
  • MySQL-wokbench **** 全平台 仅支持MySQL

关于peewe

官方文档 http://docs.peewee-orm.com/en/latest/
官方文档写的很完善,示例清晰,如果英语可以,还是直接看文档的好

之前我写过一点东西,简书地址如下,可以搭配着看,部分内容重合
http://www.jianshu.com/p/26648d192cd4

一、安装

  1. pypi:pip install peewee
  2. anaconda or conda:conda install -c conda-forge peewee
    推荐使用anaconda管理包,尤其是Windows环境下,依赖问题貌似比Linux更加棘手(主要是不知道哪个包引起的,也不知道从那下载,最起码peewee我c++装遍了也没用)
  3. 源码 github
git clone https://github.com/coleifer/peewee.git
cd peewee
python setup.py install

二、创建数据库和表单

peewee的使用异常简单,通过继承peewee提供的Model类,就可以一个类代表一个表,然后进行各种处理,方便快捷。

下面是一些简单示例,并没有详细追求表之间的逻辑性等等,因此看看就好,别细追究

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
from peewee import Model          # peewee提供的基础类,一个Model就对应一个数据库
from peewee import SqliteDatabase      # 我使用sqlite做展示,peewee支持常见的MySQL、PostgreSQL等等多种数据库
from peewee import BooleanField    # 几种常见的数据类型,还有PrimaryField自己可以看看用法
from peewee import CharField
from peewee import FloatField
from peewee import ForeignKeyField
from peewee import IntegerField
from peewee import TextField


# 指定database的路径为同该.py文件目录下的test.db数据库
# 而且便于后边其他脚本的导入(主要是为了db.atomic()的使用)
dbpath = os.path.join(
    os.path.dirname(os.path.abspath(__file__)),
    'test.db'
)

# 初始化数据库
db = SqliteDatabase(dbpath)

class BaseModel(Model):
    u"""
    基础类
    可以在此处制定一些大家都需要的列,
    然后每个继承的子类(表)中都会有这么固定的几列
    """

    class Meta:
        u"""指定数据库."""
        database = db


class Teacher(BaseModel):
    u"""
    教师类
    继承自基础数据库,表明这些表都是该数据库里的
    """
    name = CharField()    # 教师姓名
    page = TextField()      # 教师主页,由于字符串比较长,因此用text
    class_ = CharField()    # 上什么课

u"""
教师表,没有指定表的名字,peewee会默认使用类的名称Teacher作为表的名称
"""

class Student(BaseModel):
    u"""
    一个数据库的简单示例
    """
    student = CharField(default='zhang')        # 学生姓名
    student_ID = IntegerField(unique=True)      # 学号,学号必须是独一份的,不允许重复
    math = ForeignKeyField(null=True)        # 数学老师,数学老师允许为空,也就是说,可以不上数学课
    english = ForeignKeyField()      # 英语老师,默认是由zhang来上

    class Meta:
        u"""
        db_table指定表名,
        order_by 指定表中数据的排序顺序,
        indexes是为表中数据添加索引,加快后续的查询
        其中我指定对学生姓名和学号之间建立索引,两个一块查会有速度优势,
        后边的True表明,这两个数据组合必须是unique的
        """
        db_table = 'student'
        order_by = ('student_ID', 'student', ) 
        indexes = (
            (('student', 'student_ID'), True),
        )

三、插入数据

首先,新建表

如果一个表存在,我们在新建该表的话peewee会报错,并且程序退出,为了避免该种错误,通通在新建和删除之前,进行一次该表是否存在的判断

u"""
建表和删除表
由于peewee的表封装成了类的形式,
因此可以通过传参进行传递,
便于写重复利用的function
"""
def create_table(table):
    u"""
    如果table不存在,新建table
    """
    if not table.table_exists():
        table.create_table()

def drop_table(table):
    u"""
    table 存在,就删除
    """
    if table.table_exists():
        table.drop_table()

# 分别建立Teacher和Student表
create_table(Teacher)
create_table(Student)

其次,插入数据

peewee会默认生成一定名为id的主键列,我们不用特地指定该列的值,peewee会默认从1递增,该id可以像普通列一样通过点号和转换成字典获取,也可以通过一下三种插入方式手动指定(必须为正整数,而且必须unique,即不得存在多个相同id)

插入数据主要有三种方式,除了用法上,主要会带来性能上的极大差异,如果数据量比较大,用最慢的方法会被慢死的,曾经50M左右的数据用最基础(也最慢)的插入,插了两天多,中档方法只需要4分钟左右,最快的只用了1分钟不到。

  1. 基础款,最慢
# 直接create,create之后还会将创建的这条数据赋值给你,方便进行其他操作
teacher = Teacher.create(name='zhang', page='http://xxx.xxxx.xxx', class_=''english")

# 比如,第二个表我们用到了外键,此时就需要上边的那条数据了,否则还需要自行查询获取结果
Student.create(student='no.1', student_ID='001', english=teacher)
  1. 中档,稍快
tem = {
  'teacher': 'zhang',
  'page': 'http://xxx.xxx.xxx',
  'class_': 'math'
}

# 假设有一个长度为10k的列表里边的数据全部是同tem形式一致的字典
# db就是上边我们指定的数据库
# 还有另外一个关键词,和其他的atomic用法,但是最推荐这个,也只介绍这个,有需要自己查文档
with db.atomic():
    for i in data:
        Teacher.create(**i)
  1. 最快
    接上边虚拟的data,继续插入
# 最快的插入方法,想在快估计只能换模块了
# peewee性能限制,无法一次插入太多条,因此根据自己的情况调整,一般不能比100更多,出现插入出错的情况,可以适当减少该数值
with db.atomic():
    for i in range(0, len(data), 100):
        Teacher.insert_many(data[i: i+100).execute()

加速的原理很简单,数据库方面慢就慢在它commit的过程中,第一个方法,每插入一条就commit一次,不满才怪

四、查询

插叙单条数据
# get的括号中给定条件,可以一次性多个条件
teacher = Teacher.get(Teacher.name == 'zhang', class_ == 'math')
查询多条数据,及一些简单的骚操作
# 只需要教师名字,只要上数学课的老师,老师的名字只保留一份,如果有两个zhang,结果中,只保留一个
teachers = Teacher.select(Teacher.name).where(Teacher.class_=='math').distinct()

# 获取教师姓名
for teacher in teachers:
    print(teacher.name)

# 将peewee的类转化成元组构成的列表,每个元组中包含有该条查询的所有数据
teacher1 = teacher.tuples()

# 将peewee的类转化成字典构成的列表
teacher2 = teacher.dict()

# 在上边的结果上进一步查询
teacher3 = teachers.select().,where(teahcer.name == 'zhang')
外链查询
# 导入两个非常有用的函数
# playhouse是peewee的一个附加模块,提供了许多附加的小功能
from playhouse.shortcut import model_to_dict, dict_to_model

# 还记得学生类用了外链么
student = Student.select().join(Teacher)
# 即可查看字典形式的,学生和相应的任课老师的信息
print(model_to_dict(student))

查询就是通过一个简单的“.”来获取不同的功能,非常具有Python的风格,使用起来非常简便
,同时如果对SQL语句有了解的话,什么时候改用哪个功能,也很好决断;

五、更新数据

# 注意到,有个用了点好,有个没有用么
query = Teacher.update(name="gnahz").where(Teacher.class_="math")
query.execute()

六、其他,几个我常用的小功能

以官方示例来做一些展示,给出关键词和用法,可能暂时不了解用不上,不过不妨了解一下心里有数,万一哪天需要呢

# paginate,选取第2页,每页10条,按Tweet.id排序,该条命令实质上选取了排序之后的11~20,这10条结果
Tweet.select().order_by(Tweet.id).paginate(2, 10)

# count,统计数目,没啥好说的,只不过数量较大时,该命令比较慢,如果常用该数目,建议直接多建一个表,记录一下
Tweet.select().count()
Tweet.select().where(Tweet.id > 50).count()

u"""
比较特殊,表建立以后,
还可以进行添加列、删除列、更换表名等等操作,对表进行一下修饰
但是一般用不上
"""
migrator = SqliteMigrator(db)    # 这里的db就是我们最开始初始化的数据库

# 下面给某个表,添加了两个列,删除一个列,修改完成后,千万别忘了把原始的class给改掉
title_field = CharField(default='')

migrate(
    migrator.add_column('some_table', 'title', title_field),    # 某个表,新的列名,列的数据形式
    migrator.rename_column('story', 'mod_date', 'modified_date')  # 某个表,改列名
    migrator.drop_column('some_table', 'old_column'),    # 删除某列
    migrator.rename_table('story', 'stories_tbl')      # 重命名表
    migrator.add_index('story', ('pub_date',), False),    #添加index
    migrate(migrator.drop_index('story', 'story_pub_date_status'))    # 删除index
)

# 其他用法,是否允许某个表中的某列拥有null值
# Note that when making a field not null that field must not have any
# NULL values present.
migrate(
    # Make `pub_date` allow NULL values.
    migrator.drop_not_null('story', 'pub_date'),

    # Prevent `modified_date` from containing NULL values.
    migrator.add_not_null('story', 'modified_date'),
)
几个查询符号
一些特殊的查询方式

官方示例:

# Find the user whose username is "charlie".
User.select().where(User.username == 'charlie')

# Find the users whose username is in [charlie, huey, mickey]
User.select().where(User.username << ['charlie', 'huey', 'mickey'])

Employee.select().where(Employee.salary.between(50000, 60000))

Employee.select().where(Employee.name.startswith('C'))

Blog.select().where(Blog.title.contains(search_string))

最后絮叨一句,peewee会在插入数据时默认生成一个名字叫id的primary key,这个id可以像其他列一样通过点号访问,我们做外键的时候其实直接赋值相应的id号给指定外键的列就可以,
因此,如果有时候需要插入大量含有外键的数据,查询过程严重拖慢插入速度时,我们可以通过提前获取对应外链的id号,通过直接指定外链的方式加快速度

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容