美文网首页pythonflaskGoLang 开发 - 技术集锦
Python轻量级ORM -- Peewee 笔记(一)

Python轻量级ORM -- Peewee 笔记(一)

作者: 人世间 | 来源:发表于2016-11-18 18:31 被阅读3454次

    再识ORM

    Peewee是一个轻量级Python``ORM库。对于我,对ORM的经历了反反复复的爱恨交织过程。最早对其神奇之处的十分感叹;而后又鄙视其生成sql方式;如今,适时适当的在项目中使用ORM

    归根结底,对ORM态度的转变,源于对其认识的不足。最早接触ORM源自Django自带的ORM,用起来很爽。当时对sql并不熟悉,ORM了却了心头一大痛点。后来使用flask,没有自带的ORM,接触了sqlalchemy,也是一个很优秀的ORM。当时,我对于ORM的认识还是很浅薄,大概就认为ORM一个是数据库对象的map,其次就是能够生成sql查询。至于如何map,如何生成,如何执行等,基本上处于认知上的蛮荒时代。

    直到为了学习Python的元类,并尝试写自己的ORM,才算是对ORM有一个完整的认识。以前的认识可以称之为对其原理和使用方式上的纠结。不明其根本原理,使用也一知半解。现在的认识更多源于工程哲学上方面。使用程序人生的一篇文章软件随想录:代码与数据对ORM工程上的总结如下:

    我们用 ORM,或者 LINQ,而尽量减少 SQL 的使用,是因为我们通过把SQL这种嵌在代码里的字符串数据,转化成了代码,也就意味着我们拥有了编译时或者运行时的检查,乃至编辑时的检查 —— 各种 linter 很难检测出字符串中的SQL语法或者语义错误,但可以在你撰写 ORM代码时便提示你其中蕴含的语法/语义错误。这是非常伟大的一个进步 —— 对于软件工程来说,越早发现错误,消弭错误所花费的时间就越短。

    诚然,ORM是数据层中的更高的抽象,在工程上,抽象程度越高,对于扩展和维护将会更有利。当然,ORM常被人诟病的一大理由就是程式生成的SQL没有原生的高效。的确,对于复杂的SQL语句,使用ORM的组合可能比原生的sql更不可读或者不可维护。此时,我们就需要在项目中继续使用raw sql的形式。

    因此,在项目中使用数据层的抽象,ORM最好具备以下几个方面特性:

    1. ORM和数据库可以相互独立存在,ORM可以选择是否引入关系约束。即数据库的字段可以大于ORM中定义的map,有的orm数据表字段和orm中的class必须一直,这就在已经在的表中引入orm会有点棘手。
    2. ORM具备提供raw sql的功能。这样在orm无法方便提供查询的时候可以使用原生的sql解决问题。
    3. ORM提供生成数据表和根据数据表生成model的特性。这样就在数据的merge时候方便的操作。

    基于上述几点,发现Pythonpeewee基本满足我的要求。下面就peewee的简单使用方式做一个说明。例子主要取自官网的Quickstart

    分为两个部分,第一部分为基本使用,第二部分为整合到项目(以tornado)为例。

    Peewee 快速开始

    快速开始部分根据官网的文档为例子。

    定义数据模型

    顾名思义,ORM为数据对象关系的映射。首先我们需要定义数据模型(model)。新建一个文件learn_peewee.py

    import logging
    from peewee import MySQLDatabase, Model, CharField, DateField, BooleanField, IntegerField
    
    logger = logging.getLogger('peewee')
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.StreamHandler())
    
    db = MySQLDatabase('test', host='127.0.0.1', user='root', passwd='', charset='utf8', port=3306)
    
    class BaseModel(Model):
        class Meta:
            database = db
    
    class Person(BaseModel):
        name = CharField(verbose_name='姓名', max_length=10, null=False, index=True)
        passwd = CharField(verbose_name='密码', max_length=20, null=False, default='123456')
        email = CharField(verbose_name='邮件', max_length=50, null=True, unique=True)
        gender = IntegerField(verbose_name='姓别', null=False, default=1)
        birthday = DateField(verbose_name='生日', null=True, default=None)
        is_admin = BooleanField(verbose_name='是否是管理员', default=True)
    
    

    为了更好的测试学习,把log打开。首先,使用MySQLDatabase指定了所连接的数据库test

    然后定义了Person这个表的数据字段,如果不指定主键,peewee会自动帮我们创建一个id的字段作为主键。每一个Field都有几个参数可以配置,大概就是长度的大小,是否为空(null)和默认值(default),索引(index)和唯一索引(unique)几个常见的数据库选项。

    创建数据表

    定义好数据模型之后,下一步就是根据模型创建数据表了。

    In [1]: from learn_peewee import *
    
    In [2]: db
    Out[2]: <peewee.MySQLDatabase at 0x1114620d0>
    
    In [3]: db.is_closed()
    Out[3]: True
    
    In [4]: db.connect()
    
    In [6]: db.is_closed()
    Out[6]: False
    

    导入定义的数据库配置和数据模型。然后通过db.is_closed函数查看数据库的连接状态,使用db.connect函数创建连接。

    ☁  peewee-orm  netstat -ant | grep -i 3306
    tcp4       0      0  127.0.0.1.3306         127.0.0.1.61925        ESTABLISHED
    tcp4       0      0  127.0.0.1.61925        127.0.0.1.3306         ESTABLISHED
    tcp46      0      0  *.3306                 *.*                    LISTEN
    

    此时确实也能看见mysql的连接创建了。接下来就能创建数据表啦。使用模型的create_table方法。

    In [5]: Person.sqlall()
    Out[5]:
    ('CREATE TABLE `person` (`id` INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` VARCHAR(10) NOT NULL, `passwd` VARCHAR(20) NOT NULL, `email` VARCHAR(50), `gender` INTEGER NOT NULL, `birthday` DATE, `is_admin` BOOL NOT NULL)', [])
    ('CREATE INDEX `person_name` ON `person` (`name`)', [])
    ('CREATE UNIQUE INDEX `person_email` ON `person` (`email`)', [])
     
    In [6]: Person.create_table()
    ('CREATE TABLE `person` (`id` INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` VARCHAR(10) NOT NULL, `passwd` VARCHAR(20) NOT NULL, `email` VARCHAR(50), `gender` INTEGER NOT NULL, `birthday` DATE, `is_admin` BOOL NOT NULL)', [])
    ('CREATE INDEX `person_name` ON `person` (`name`)', [])
    ('CREATE UNIQUE INDEX `person_email` ON `person` (`email`)', [])
    
    

    通过sqlall方法可以看见peewee为我们生成的创建表的sql语句。执行的时候,log也显示了将要执行的sql。

    如果数据表已经存在,执行create_table的时候,将会抛出异常。

    default的含义

    通过查看上面创建数据表的sql可以发现,default的行为很古怪。查看数据库的schema,所谓的数据表的defalut并不是我们在class中指定的。对于可以为null的字段。生成的schema中的字段的默认值为null,对于非null的字段,生成的默认值为其初始值(零值),即CharField(varchar)为空字串。InterFiled(int)则为0。

    那么指定default的含义有什么用呢?default用于使用orm插入数据的时候,如果没有显示的给字段赋值,那么就会采用default指定的值。使用 create方法插入记录,使用save方法更新记录。

    In [2]: p = Person.create(name='master')
    ('INSERT INTO `person` (`name`, `passwd`, `gender`, `is_admin`) VALUES (%s, %s, %s, %s)', [u'master', u'123456', 1, True])
    
    In [3]: p.save()
    ('UPDATE `person` SET `name` = %s, `passwd` = %s, `gender` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [u'master', u'123456', 1, True, 3])
    Out[3]: 0L
    

    可以看到,我们只指定了name字段的值,因为passwdgender还有is_admin字段都指定default,此时插入的时候,就使用了默认的值。调用save方法的时候,执行是update语句,因此也同样执行了默认值。而没有指定默认值的字段,peewee则直接忽略。

    In [4]: p.email = 'master@g.com'
    
    In [5]: p.save()
    ('UPDATE `person` SET `name` = %s, `passwd` = %s, `email` = %s, `gender` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [u'master', u'123456', u'master@g.com', 1, True, 3])
    Out[5]: 1L
    In [8]: p.passwd = '11'
    
    In [9]: p.save()
    ('UPDATE `person` SET `name` = %s, `passwd` = %s, `email` = %s, `gender` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [None, u'11', u'master@g.com', 1, True, 3])
    Out[9]: 1L
    
    In [8]: p = Person.get(id=1)
    ('SELECT `t1`.`id`, `t1`.`name`, `t1`.`passwd`, `t1`.`email`, `t1`.`gender`, `t1`.`birthday`, `t1`.`is_admin` FROM `person` AS t1 WHERE (`t1`.`id` = %s) LIMIT 1 OFFSET 0', [1])
    
    In [9]: p.save()
    ('UPDATE `person` SET `name` = %s, `passwd` = %s, `email` = %s, `gender` = %s, `birthday` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [u'master', u'11', u'master@g.com', 1, None, True, 1])
    Out[9]: 0L
    

    有默认值的字段,如果指定了值,就使用所赋的值。新读的数据,就不用考虑默认值了,如果读取的值没有变,更新的时候就依然采用这个值,与default值没有关系。

    总结

    1. default的设置与创建数据表的schema没有关系。仅与插入的时候有关系。
    2. 插入的时候,如果字段设置了default值,则会按照default指定的值插入,如果没有指定,同时字段可以为null,则数据库自动初始化值为null,如果字段不能为null,则数据库自动初始化为其零值。
    3. 最佳实践,如果字段为非Null,最好设置default值,同时数据库schema也设置其default值,如果字段为可以为null,那么初始值就设置为null即可。

    快速认识了Peewee,接下来讨论peewee更强大的功能,例如定义关系和增删改查。

    相关文章

      网友评论

      • leeyi:"第二部分为整合到项目(以tornado)为例。" 这个第二部分的文章在哪里呢?
      • 卡萨诺瓦_:你好 peewee的剩下内容还写吗
      • kruuuuuuust:你这个高亮的颜色,简直看不清,建议换个显眼的
        人世间:@krastru 颜色配置是jianshu控制的 看起来确实蛋疼

      本文标题:Python轻量级ORM -- Peewee 笔记(一)

      本文链接:https://www.haomeiwen.com/subject/ktrspttx.html