经验拾忆纯手工-PythonORM之peewee模型字段索引约束事务一

40次阅读

共计 5394 个字符,预计需要花费 14 分钟才能阅读完成。

前言

去 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              # 端口
)

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()

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…

正文完
 0