经验拾忆(纯手工)=> Python-ORM之peewee:模型-字段-索引-约束-事务(一)

前言

去github搜 “python orm”,最高star居然不是sqlalchemy,而是peewee
后来得知peewee,比sqlalchemy简单好用。值得一学哦!!
我总体感觉(peewee像 Django-ORM的分离版,,但比Django-ORM和SqlAlchemy 小巧,简单,文档也友好)

还有一个更重要的感觉就是, peewee 的 API方法名 和 SQL语句 的 单词 基本相似。
例如对比一下(关键词语法都是 update 和 where):

SQL语句:update Lang set name='Python' where name='Java';
Peewee:Lang.update(name='Python').where(Lang.name == 'Java')

这种良心的API,可以大大降低我们的学习成本,还可以巩固我们对SQL的记忆!!!!!!
总官档地址:http://docs.peewee-orm.com/en…
官方Github地址:https://github.com/coleifer/p…

安装和导入

pip install peewee
from peewee import *
# peewee的模块很结构化,都在peewee中,如果懒就都导入进来。 当然你也可以熟了,按需导入
# 后面无特殊情况,就都是这样导入的。我就不提了。

数据库

postgresql 和 sqlite

peewee 只支持 sqlite, mysql 和 postgresql 数据库, 如果你有需求用oracle等,请绕行。。。
如需sqlite 和 postgresql,配置请参考 http://docs.peewee-orm.com/en…

mysql

当然我经常用MySQL,以后的所有都围绕mysql来讲,如下是基本配置

mysql_db = MySQLDatabase(
    'lin',                 # 数据库
    user='root',           # 用户名
    password='123',        # 密码
    host='IP',             # IP
    port=3306,             # 端口
    charset='utf8mb4'      # 字符集类型, utf8mb4 是 utf8的大哥
)

peewee的mysql引擎默认优先使用pymysql。
如果你没安装pymysql, 他就会去寻找 MySQLdb。 都没有就会报错。
嗯,都啥年代了,python3的时代,所以我们用 pymysql模块即可,若没安装,跳出来安装下即可

pip install pymysql

既然用的pymysql驱动,MySQLDatabase() 里面的写法 和 pymysql对象实例化的参数配置是一样的。
如果我给的例子的参数不够用,你可以来下面的链接自己选吧:https://github.com/PyMySQL/Py…

建立数据库连接

print(mysql_db.connect())

关闭数据库连接

print(mysql_db.close())

测试数据库连接是否关闭

mysql_db.is_closed()

列出数据库的所有表:

mysql_db.get_tables()   

列出所有字段的详细信息:

print(db.get_columns('owner'))    # 假设 owner是表名,下面同理

列出所有主键的字段:

print(db.get_primary_keys('owner'))

列出所有索引字段的详细信息:

print(db.get_indexes('owner'))

列出所有外键的字段:

print(db.get_foreign_keys('owner'))

Python 各种 web框架嵌入使用 peewee 案例传送门:
官档-Web案例:http://docs.peewee-orm.com/en…

表-记录-字段

ORM语法 和 数据库的 (表-记录-字段)对应关系如下:

ORM结构数据库
实例(对象)记录
类属性

默认自增主键ID

定义一个类,继承了peewee模块的Model类,这个类就可以当作Model来用了
首先建立一张”空表”

mysql_db = MySQLDatabase('lin_test', user='root', password='123',
                     host='ip', port=3306, charset='utf8mb4')
class Owner(Model):
    class Meta:             
        database=mysql_db   # 这里是"必须" 要指定的, 指定哪一数据库
mysql_db.create_tables([Owner])    # 注意,源码是取出参数遍历,所以这里参数用列表

上述代码就可以建立一张”空表”。 为什么”空表” 用引号括起来呢??

这是关于peewee orm的机制,"你若不指定(primary key)",它就会"自动"为你创建一个
"名为 id", "类型为 int", 并设置为 "primary" 的 "自增(auto_increment)" 的字段

但 一旦你把一个自定义的字段,设为主键,默认的id字段就会被覆盖:

name = CharField(primary_key=True)   # name设为了主键, 原有的默认id就没了 

官档也说明:如果你想自己建立一个自增主键,并覆盖默认id。你可以用AutoField字段:

new_id = AutoField()    # 这句话直接就为你 设置为 int型 和 主键 和自增。 
"这是官档最推荐覆盖id的方法,  而不是自己弄一个 Integer,再设主键"

自增id就讲完了, 不过你是否发现每个 类下都有

class Meta:
    database= xxx   # 这是为每张表指定数据库,必须要指定的。不然它不知道你这个表在哪个数据库

既然这样,若我们要在一个数据库中创建很多很多表,那岂不是每次都需要给每张表指定一个数据库??
就像这样:

class User(Model):
    class Meta:
        database = mysql_db

class Owner(Model):
    class Meta:
        database = mysql_db

这样有点烦,但我们可以定义一个基类指定好数据库, 然后其他子类模型继承它就好了。

class BaseModel(Model):
    name = CharField(max_length=10)    # 定义一个 name 字段
    class Meta:
        database = mysql_db
        
class User(BaseModel):    # 继承基类
    pass
class Owner(BaseModel):   # 继承基类
    pass
    
mysql_db.create_tables([User, Owner])    # 正式创建表, 基类不需要,可以不放进来

像上述代码CharField, 更多类型字段定义,官档给的很详细了,我不再赘述了。
官档-字段-参数:http://docs.peewee-orm.com/en…
但下面我还会挑一些主要常用(有一点点点难特别)的说一下。。。

外键字段(ForeignKeyField)

普通外键

class BaseModel(Model):    # 基类
    name = CharField(max_length=10)
    class Meta:
        database = mysql_db

class Owner(BaseModel):   # 主人类
    pass

class Pet(BaseModel):     # 宠物类
    owner = ForeignKeyField(
        Owner, 
        backref='owner_conn',  # 通过引用名获取对象。"主人,你可以通过这个名字调用我"
        on_delete='Cascade',   # 级联删除
            # 默认为None, 这时,你想删主人是删不掉的。会报错。 必须先删宠物再删主人。
            # 设为 Cascade后, 你可以直接删主人。 他的宠物也会随之自动删除。 这就是级联删除
        on_update=Cascade,     # 级联更新,原理同 on_delete
    )

层级外键(通常用于层级分类,自关联查询):

class Category(BaseModel):
    name = CharField()
    parent = ForeignKeyField('self', null=True, backref='children') 
    注: "self" 字符串是固定语法, 下一篇还会将,自关联查询

日期字段(DateTimeField)

import datetime
......
date_time= DateTimeField(default=datetime.datetime.now) 

表属性(Meta)

表属性就是可以 改表名,设置主键,联合主键,设置索引,联合索引等操作。不再赘述,见官档。
官档 Meta: http://docs.peewee-orm.com/en…

索引 和 约束

设置索引有3种方法:

  1. 通过定义字段的参数:
    普通索引

    name = CharField(index=True)

    唯一索引

    name = CharField(unique=True)
  2. 通过定义表属性Meta:
    联合唯一索引

    class Meta:
        indexes = (
            (('字段1', '字段2'), True),    # 字段1与字段2整体作为索引,True 代表唯一索引
            (('字段1', '字段2'), False),   # 字段1与字段2整体作为索引,False 代表普通索引
        )
    需要注意的是,上面语法,三层元组嵌套, 元组你懂得, 一个元素时需要加个 , 逗号。 别忘了。
  3. 索引API:
    官档:http://docs.peewee-orm.com/en…

设置约束有2种方法:

  1. 通过定义字段的参数:
    ——-通常用来单一字段主键:

    name = CharField(primary_key=True)
  2. 通过定义表属性Meta
    ——-通常用作联合主键:

    class Meta:
        primary_key = CompositeKey('字段1', '字段2')
        # primary_key = False      # 也可以不使用主键(不覆盖,也 取消 创建默认id字段)
    

事务

支持with上下文语法,支持事务嵌套,注意嵌套事务 只会回滚 离它最近 的一层之间的代码。
包裹在with语句中的代码,只要存在异常,就会回滚。嵌套的事务,也是有一处异常,所有层事务都会回滚。
当然你也可以手动 rollback()来回滚。
嵌套事务示例如下:

with mysql_db.atomic() as transaction1:    # 第一层事务。  atomic(), 固定语法就不说了。 
    User.create(username='Tom')
    with mysql_db.atomic() as transaction2: # 第二层事务
        User.create(username='Jerry')
        User.create(username='Spike')
        transaction2.rollback()            # 就近原则, 第二层的rollback()回滚
    User.create(username='Butch')
    
# 如果真的出现回滚,那么 从 第二层的 with() 开始算 事务内容, 到 rollback() 结束
#     形象例子: 顶部 面包片从 第二层的with()开始夹,  底部 面包片 夹到 rollback()

# 注意一点,虽然是嵌套事务,但是每层with事务都有对应的名字(就是with as 之后变量)。 
# 所以回滚写在哪层事务里面, 就要用哪层事务的名字(就近原则)。 不然会报错的。
# 错误实例: 倒数第二行的: transaction2.rollback()  写成 transaction1.rollback()。 错误!

带有commit()的嵌套事务示例如下:(缩小事务的代码范围, 就像 “面包里夹的东西变少了” 的意思)

with mysql_db.atomic() as transaction1:      # 第一层事务
    User.create(username='Tom')
    with mysql_db.atomic() as transaction2:  # 第二层事务
        User.create(username='Jerry')
        transaction2.commit()                # 就这里变了, 插入了一行 commit
        User.create(username='Spike')
        transaction2.rollback()  # rollback()回滚
    User.create(username='Butch')

# commit(),加入了这一行,就意味着 从 这行开始算 回滚内容,到 rollback() 结束
#     形象例子: (顶部 面包片 从commit() 这里开始夹, 底部 面包片 夹到 rollback() )

上面无论哪个事务例子, 都必须注意:

  1. 每层事务,只管自己层内的 rollback(),才有效, 不能管其他层的。
  2. 就算你用 commit() 夹, 如果自己层内没有 rollback(), 那么你的 commit()是无效的(夹不住)

事务就差不多这些,官档还有一些用法和语法,但最终功能结果都是一样的。选一种(我的例子)就行。
官档-事务: http://docs.peewee-orm.com/en…

闲杂用法

查看ORM对应的原生SQL语句:

.....ORM语句.sql()       # 后缀 .sql() 打印对应原生sql

执行原生SQL:

# 注意,传数据用参数,不要用字符串拼接(防SQL注入)
for owner in Owner.raw('select * from owner where name=%s', 'Alice'):
    print(owner.name)

更原生的执行原生SQL:

print(mysql_db.execute_sql('select * from user').fetchall())
# sql,可以传位置参数(防注入),就像使用 pymysql一样。

表改名:

注:我说的改名只是查询时的临时名

下一篇文章查询,会提到 字段改名, 格式:  字段.alias('新字段名')
那表改名也差不多,有2种方式:
    方式1:
        格式: 表类.alias('新表名')
    方式2:
        格式: 新表名 = 表类.alias()

未结束语

本篇写了一些入门性的模型的建立,数据库,事务,索引,算是比较基本的。
当然还有更常用,更重要的CRUD等,会在下一篇介绍。
下一篇传送门:https://segmentfault.com/a/11…

    原文作者:Cython_lin
    原文地址: https://segmentfault.com/a/1190000020265149
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞