前言
去github搜 "python orm",最高star居然不是sqlalchemy,而是peewee 后来得知peewee,比sqlalchemy简单好用。值得一学哦!! 我总体感觉(peewee像 Django-ORM的分离版,,但比Django-ORM和SqlAlchemy 小巧,简单,文档也友好)
还有一个更重要的感觉就是, peewee 的 API方法名 和 SQL语句 的 单词 基本相似。 例如对比一下(关键词语法都是 update 和 where):
代码语言:javascript复制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...
安装和导入
代码语言:javascript复制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来讲,如下是基本配置
代码语言:javascript复制mysql_db = MySQLDatabase(
'lin', # 数据库
user='root', # 用户名
password='123', # 密码
host='IP', # IP
port=3306, # 端口
charset='utf8mb4' # 字符集类型, utf8mb4 是 utf8的大哥
)
peewee的mysql引擎默认优先使用pymysql。 如果你没安装pymysql, 他就会去寻找 MySQLdb。 都没有就会报错。 嗯,都啥年代了,python3的时代,所以我们用 pymysql模块即可,若没安装,跳出来安装下即可
代码语言:javascript复制pip install pymysql
既然用的pymysql驱动,MySQLDatabase() 里面的写法 和 pymysql对象实例化的参数配置是一样的。 如果我给的例子的参数不够用,你可以来下面的链接自己选吧:https://github.com/PyMySQL/Py...
建立数据库连接
代码语言:javascript复制print(mysql_db.connect())
关闭数据库连接
代码语言:javascript复制print(mysql_db.close())
测试数据库连接是否关闭
代码语言:javascript复制mysql_db.is_closed()
列出数据库的所有表:
代码语言:javascript复制mysql_db.get_tables()
列出所有字段的详细信息:
代码语言:javascript复制print(db.get_columns('owner')) # 假设 owner是表名,下面同理
列出所有主键的字段:
代码语言:javascript复制print(db.get_primary_keys('owner'))
列出所有索引字段的详细信息:
代码语言:javascript复制print(db.get_indexes('owner'))
列出所有外键的字段:
代码语言:javascript复制print(db.get_foreign_keys('owner'))
Python 各种 web框架嵌入使用 peewee 案例传送门: 官档-Web案例:http://docs.peewee-orm.com/en...
表-记录-字段
ORM语法 和 数据库的 (表-记录-字段)对应关系如下:
ORM结构 | 数据库 |
---|---|
类 | 表 |
实例(对象) | 记录 |
类属性 | 列 |
默认自增主键ID
定义一个类,继承了peewee模块的Model类,这个类就可以当作Model来用了 首先建立一张"空表"
代码语言:javascript复制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]) # 注意,源码是取出参数遍历,所以这里参数用列表
上述代码就可以建立一张"空表"。 为什么"空表" 用引号括起来呢??
代码语言:javascript复制这是关于peewee orm的机制,"你若不指定(primary key)",它就会"自动"为你创建一个
"名为 id", "类型为 int", 并设置为 "primary" 的 "自增(auto_increment)" 的字段
但 一旦你把一个自定义的字段,设为主键,默认的id字段就会被覆盖:
代码语言:javascript复制name = CharField(primary_key=True) # name设为了主键, 原有的默认id就没了
官档也说明:如果你想自己建立一个自增主键,并覆盖默认id。你可以用AutoField字段:
代码语言:javascript复制new_id = AutoField() # 这句话直接就为你 设置为 int型 和 主键 和自增。
"这是官档最推荐覆盖id的方法, 而不是自己弄一个 Integer,再设主键"
自增id就讲完了, 不过你是否发现每个 类下都有
代码语言:javascript复制class Meta:
database= xxx # 这是为每张表指定数据库,必须要指定的。不然它不知道你这个表在哪个数据库
既然这样,若我们要在一个数据库中创建很多很多表,那岂不是每次都需要给每张表指定一个数据库?? 就像这样:
代码语言:javascript复制class User(Model):
class Meta:
database = mysql_db
class Owner(Model):
class Meta:
database = mysql_db
这样有点烦,但我们可以定义一个基类指定好数据库, 然后其他子类模型继承它就好了。
代码语言:javascript复制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)
普通外键
代码语言:javascript复制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
)
层级外键(通常用于层级分类,自关联查询):
代码语言:javascript复制class Category(BaseModel):
name = CharField()
parent = ForeignKeyField('self', null=True, backref='children')
注: "self" 字符串是固定语法, 下一篇还会将,自关联查询
日期字段(DateTimeField)
代码语言:javascript复制import datetime
......
date_time= DateTimeField(default=datetime.datetime.now)
表属性(Meta)
表属性就是可以 改表名,设置主键,联合主键,设置索引,联合索引等操作。不再赘述,见官档。 官档 Meta: http://docs.peewee-orm.com/en...
索引 和 约束
设置索引有3种方法:
- 通过定义字段的参数: 普通索引 name = CharField(index=True) 唯一索引 name = CharField(unique=True)
- 通过定义表属性Meta: 联合唯一索引 class Meta: indexes = ( (('字段1', '字段2'), True), # 字段1与字段2整体作为索引,True 代表唯一索引 (('字段1', '字段2'), False), # 字段1与字段2整体作为索引,False 代表普通索引 ) 需要注意的是,上面语法,三层元组嵌套, 元组你懂得, 一个元素时需要加个 , 逗号。 别忘了。
- 索引API: 官档:http://docs.peewee-orm.com/en...
设置约束有2种方法:
- 通过定义字段的参数: -------通常用来单一字段主键: name = CharField(primary_key=True)
- 通过定义表属性Meta -------通常用作联合主键: class Meta: primary_key = CompositeKey('字段1', '字段2') # primary_key = False # 也可以不使用主键(不覆盖,也 取消 创建默认id字段)
事务
支持with上下文语法,支持事务嵌套,注意嵌套事务 只会回滚 离它最近 的一层之间的代码。 包裹在with语句中的代码,只要存在异常,就会回滚。嵌套的事务,也是有一处异常,所有层事务都会回滚。 当然你也可以手动 rollback()来回滚。 嵌套事务示例如下:
代码语言:javascript复制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()的嵌套事务示例如下:(缩小事务的代码范围, 就像 "面包里夹的东西变少了" 的意思)
代码语言:javascript复制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() )
上面无论哪个事务例子, 都必须注意:
- 每层事务,只管自己层内的 rollback(),才有效, 不能管其他层的。
- 就算你用 commit() 夹, 如果自己层内没有 rollback(), 那么你的 commit()是无效的(夹不住)
事务就差不多这些,官档还有一些用法和语法,但最终功能结果都是一样的。选一种(我的例子)就行。 官档-事务: http://docs.peewee-orm.com/en...
闲杂用法
查看ORM对应的原生SQL语句:
代码语言:javascript复制.....ORM语句.sql() # 后缀 .sql() 打印对应原生sql
执行原生SQL:
代码语言:javascript复制# 注意,传数据用参数,不要用字符串拼接(防SQL注入)
for owner in Owner.raw('select * from owner where name=%s', 'Alice'):
print(owner.name)
更原生的执行原生SQL:
代码语言:javascript复制print(mysql_db.execute_sql('select * from user').fetchall())
# sql,可以传位置参数(防注入),就像使用 pymysql一样。
表改名:
代码语言:javascript复制注:我说的改名只是查询时的临时名
下一篇文章查询,会提到 字段改名, 格式: 字段.alias('新字段名')
那表改名也差不多,有2种方式:
方式1:
格式: 表类.alias('新表名')
方式2:
格式: 新表名 = 表类.alias()
未结束语
本篇写了一些入门性的模型的建立,数据库,事务,索引,算是比较基本的。 当然还有更常用,更重要的CRUD等,会在下一篇介绍。 下一篇传送门:https://segmentfault.com/a/11...