本文整理自网络,侵删。
目录
- 一、常见orm数据库框架
- 1、peewee 简单demo
- 二、Model 和 Field 关系
- 三、Model 模型
- 四、Filed 字段
- 1、字段初始化参数
- 2、字段特有参数
- 3、字段默认参数
- 4、索引
- 五、基本操作 增删改查
- 1、创建
- 2、删除
- 3、更新
- 4、查询
- 5、事务
- 6、过滤
- 7、记录分类
- 8、计数
- 9、分页
- 六、聚合查询
- 七、Scalar
- 八、窗口
- 九、复杂筛选
- 1、查询中支持的筛选运算符
- 2、筛选方法
- 3、联合查询逻辑操作
- 十、SQL 方法
- 1、SQL helper
- 2、安全和SQL注入
一、常见orm数据库框架
Django ORM
peewee
SQLAlchemy
Django ORM
优点 :
易用,学习曲线短
和Django
紧密集合,用Django
时使用约定俗成的方法去操作数据库
缺点 :
QuerySet
速度不给力,会逼我用Mysqldb
来操作原生sql语句。
Peewee
优点 :
Django
式的API
,使其易用
轻量实现,很容易和任意web框架集成
缺点 :
不支持自动化 schema
迁移
不能像Django
那样,使线上的mysql
表结构生成结构化的模型。
SQLAlchemy
优点 :
巨牛逼的API,使得代码有健壮性和适应性
灵活的设计,使得能轻松写复杂查询
缺点 :
工作单元概念不常见
重量级 API,导致长学习曲线
1、peewee 简单demo
import datetime from peewee import * db = MySQLDatabase( "test", host="127.0.0.1", port=3306, user="root", passwd="123456" ) db.connect() class BaseModel(Model): class Meta: database = db class Person(BaseModel): name = CharField() age = IntegerField() height = IntegerField() sex = BooleanField(default='male') if __name__ == "__main__": Person.create_table() # 创建 Person.create(name='tom', age=30, height=177) # 查询 res = Person.select().where(Person.name=='tom') print(res) print(res[0]) print(res[0].name) print(res[0].age) print(res[0].height) print(res[0].sex) >>>> SELECT `t1`.`id`, `t1`.`name`, `t1`.`age`, `t1`.`High`, `t1`.`sex` FROM `person` AS `t1` WHERE (`t1`.`name` = 'ljk') 1 tom 30 177 True
二、Model 和 Field 关系
在ORM对象关系数据库中 Model是一个类,映射到数据库表中就是一个表。Filed是字段,映射到表中就是字段。model
实例就是数据库中的一条记录。在peewee中Model
和Field的关系如下:
Thing | 对应关系 |
---|---|
Model 类 | 表 |
Field 实例 | 表中字段 |
Model 实例 | 表中数据 |
数据库连接和model类定义的 典型使用
import datetime from peewee import * db = SqliteDatabase('my_app.db') class BaseModel(Model): class Meta: database = db class User(BaseModel): username = CharField(unique=True) class Tweet(BaseModel): user = ForeignKeyField(User, backref='tweets') message = TextField() created_date = DateTimeField(default=datetime.datetime.now) is_published = BooleanField(default=True)
创建一个数据库实例
db = SqliteDatabase('my_app.db')
创建一个基础model类
class BaseModel(Model): class Meta: database = db
定义一个用于建立数据库连接的基模类是一种推荐的做法,因为将不必为后续表指定数据库。
定义一个普通 model 类
class User(BaseModel): username = CharField(unique=True)
模型定义使用的是其他流行的orm(如SQLAlchemy
或Django
)中看到的声明式风格。因为User继承了BaseModel
类,所以User类可以继承数据库连接。
User已经明确定义了一个具有唯一约束的用户名列。因为我们没有指定主键,peewee 会自动添加一个自增整数主键字段,名为 id。没有指定主键的表peewee会自动创建一个名字为id的自增主键。
三、Model 模型
为了不污染model的命名空间,model的配置放在特殊的元属性类中。这是从Django的框架中借鉴过来的。
contacts_db = SqliteDatabase('contacts.db') class Person(Model): name = CharField() class Meta: database = contacts_db
在简单model示例中,你会注意到,我们创建了一个定义数据库的BaseModel,然后扩展了它。这是定义数据库和创建模型的首选方法。
你可以通过 ModelClass._meta
来使用:
Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Person' has no attribute 'Meta' >>> Person._meta <peewee.modeloptions object="" at="" 0x7f51a2f03790="">
ModelOptions
实现了几个查看model metadata的方法:
{'id': <peewee.autofield object="" at="" 0x7f51a2e92750="">, 'name': <peewee.charfield object="" at="" 0x7f51a2f0a510="">} >>> Person._meta.primary_key <peewee.autofield object="" at="" 0x7f51a2e92750=""> >>> Person._meta.database <peewee.sqlitedatabase object="" at="" 0x7f519bff6dd0="">
Model
在ORM数据中就是一张表,那么表的属性可以有如下选项。它们是被定义在Meta中元数据。
Option | Meaning | 是否可继承? |
---|---|---|
database | 指定表创建依附的数据库 | yes |
table_name | 表名 | no |
table_function | 生成表名的函数 | yes |
indexes | 多行索引 | yes |
primary_key | 主键 | yes |
constraints | 表约束的列表 | yes |
schema | 模型的数据库架构 | yes |
only_save_dirty | 调用model.save()时,仅保存脏字段,指定字段? | yes |
options | 创建表扩展的选项字典 | yes |
table_settings | 在右括号后设置字符串的列表 | yes |
temporary | 指示临时表 | yes |
legacy_table_names | 使用旧表名生成(默认情况下启用) | yes |
depends_on | 指示此表依赖于另一个表进行创建 | no |
without_rowid | 指示表不应具有rowid(仅限SQLite) | no |
strict_tables | 指示严格的数据类型(仅限SQLite,3.37+) | yes |
四、Filed 字段
Field
类是用来将Model属性映射到数据库列。每个字段类型都有一个相应的SQL存储类,将python
数据类型转化为基本的存储类型。
当创建Model类时,fields被定义成类的属性。它看起来和django的数据库框架很类似。
class User(Model): username = CharField() join_date = DateTimeField() about_me = TextField()
在上面的例子中,因为没有field有主键属性primary_key=True,所以会创建一个名字是id的自增主键。
peewee中可用的字段包括:
字段类型 | Sqlite | Postgresql | MySQL |
---|---|---|---|
AutoField | integer | serial | integer |
BigAutoField | integer | bigserial | bigint |
IntegerField | integer | integer | integer |
BigIntegerField | integer | bigint | bigint |
SmallIntegerField | integer | smallint | smallint |
IdentityField | not supported | int identity | not supported |
FloatField | real | real | real |
DoubleField | real | double precision | double precision |
DecimalField | decimal | numeric | numeric |
CharField | varchar | varchar | varchar |
FixedCharField | char | char | char |
TextField | text | text | text |
BlobField | blob | bytea | blob |
BitField | integer | bigint | bigint |
BigBitField | blob | bytea | blob |
UUIDField | text | uuid | varchar(40) |
BinaryUUIDField | blob | bytea | varbinary(16) |
DateTimeField | datetime | timestamp | datetime |
DateField | date | date | date |
TimeField | time | time | time |
TimestampField | integer | integer | integer |
IPField | integer | bigint | bigint |
BooleanField | integer | boolean | bool |
BareField | untyped | not supported | not supported |
ForeignKeyField | integer | integer | integer |
1、字段初始化参数
所有字段类型接受的参数及其默认值
null = False
允许空值index = False
创建索引unique = False
创建唯一索引column_name = None
显式指定数据库中的列名default = None
默认值,可以使任意值或可调用对象primary_key = False
指明主键constraints = None
约束条件sequence = None
序列名字(如果数据库支持)collation = None
排序字段unindexed = False
虚表上的字段不应该被索引choices = None
两种可选项:value display
help_text = None
帮助说明字段。表示此字段的任何有用文本的字符串verbose_name = None
表示此字段的用户友好名称的字符串index_type = None
索引类型
2、字段特有参数
在一些字段中有些自己特有的参数,如下:
字段类型 | 特有参数 |
---|---|
CharField | max_length |
FixedCharField | max_length |
DateTimeField | formats |
DateField | formats |
TimeField | formats |
TimestampField | resolution, utc |
DecimalField | max_digits, decimal_places, auto_round, rounding |
ForeignKeyField | model, field, backref, on_delete, on_update, deferrable lazy_load |
BareField | adapt |
3、字段默认参数
peewee
可以为每一个字段提供默认值,比如给intergerField 默认值0而不是NULL。你可以申明字段时指定默认值:
class Message(Model): context = TextField() read_count = IntegerField(default=0)
在某些情况下,默认值是动态的会更有意义。一个可能的场景就是当前时间。Peewee
允许您在这些情况下指定一个函数,该函数的返回值将在创建对象时使用。注意,使用时只提供了函数,并不需要实际调用它。
class Message(Model): context = TextField() timestamp = DateTimeField(default=datetime.datetime.now)
如果你正在使用一个接受可变类型(list, dict等)的字段,并想提供一个默认值。将默认值包装在一个简单的函数中是个好主意,这样,多个模型实例就不会共享对同一底层对象的引用。
def house_defaults(): return {'beds': 0, 'baths': 0} class House(Model): number = TextField() street = TextField() attributes = JSONField(default=house_defaults)
4、索引
peewee可以通过单列索引和多列索引。可选地包括UNIQUE
约束。Peewee还支持对模型和字段的用户定义约束。
单列索引
单列索引使用字段初始化参数定义。下面的示例在用户名字段上添加一个惟一索引,在电子邮件字段上添加一个普通索引
class User(Model): username = CharField(unique=True) email = CharField(index=True)
在列上添加用户定义的约束。你可以使用constraints参数。例如,您可能希望指定一个默认值,或者添加一个CHECK约束
class Product(Model): name = CharField(unique=True) price = DecimalField(constraints=[Check('price < 10000')]) created = DateTimeField( constraints=[SQL("DEFAULT (datetime('now'))")])
多列索引
可以使用嵌套元组将多列索引定义为元属性。每个表的索引是一个2元组,第一部分是索引字段名称的元组,可以有多个字段,第二部分是一个布尔值,指示索引是否应该唯一。
class Transaction(Model): from_acct = CharField() to_acct = CharField() amount = DecimalField() date = DateTimeField() class Meta: indexes = ( # create a unique on from/to/date (('from_acct', 'to_acct', 'date'), True), # create a non-unique on from/to (('from_acct', 'to_acct'), False), )
记住,如果索引元组只包含一项,则添加末尾逗号
五、基本操作 增删改查
peewee
中关于增删改查的基本操作方法如下:
增 :
- create():最常用创建,返回创建实例
- save():第一次执行的save是插入,第二次是修改
- insert: 插入数据,不创建数据库实例。返回id
- insert_many: 批量插入
- bulk_create:批量插入,类似于insert_many。可指定单次插入的数量
- batch_commit: 自动添加了一个事务,然后一条条的插入
- insert_from: 从另一个表中查询的数据作为插入的数据
删除 :
- delete().where().execute()
- delete_instance() 直接执行删除了,不用调用execute() 方法
修改 :
- save(): 第一次执行的save是插入,第二次是修改
- update() 用于多字段更新
查询 :
- Model.get(): 检索与给定查询匹配的单个实例。报 Model.DoesNotExist 异常。如果有多条记录满足条件,则返回第一条
- get_or_none() :与get使用方法相同。区别是找不到结果时不会报错
- get_by_id() :通过主键查找,是一种快捷方式
- Model['id_num']: 和上面的get_by_id一样是通过主键查找。
- get_or_create(): 首先查询,如果查不到将创建一个新的记录
- select() 查询多条数据
1、创建
单条插入
你可以用 Model.create()
创建一个新的实例。这个方法接收关键字参数,参数要和表定义的字段一致。返回值是新的实例
>>> User.create(username='Charlie') <__main__.User object at 0x2529350>
批量插入
有几种方法可以快速加载大量数据,缺乏经验的做法是在循环中调用Model.create来创建
data_source = [ {'field1': 'val1-1', 'field2': 'val1-2'}, {'field1': 'val2-1', 'field2': 'val2-2'}, # ... ] for data_dict in data_source: MyModel.create(**data_dict)
上面的方法比较慢的原因有几个:
- 如果没有在事务中装饰循环,那么每个对
create
()的调用都发生在它自己的事务中。这将会非常缓慢 - 必须生成每个
InsertQuery
并将其解析为SQL - 需要原生SQL语句传入到数据库中解析
- 检索最后一
个insert id
,这在某些情况下会导致执行额外的查询
可以通过一个简单的装饰: atomic
来大幅度提高速度
# This is much faster. with db.atomic(): for data_dict in data_source: MyModel.create(**data_dict)
上面的代码仍然没有解决2、3、4这三点。我们可以通过 insert_many
带来一个大的速度提升。这个方法接收多列元组或字典,然后在一次SQL语句中插入多行数据。
data_source = [ {'field1': 'val1-1', 'field2': 'val1-2'}, {'field1': 'val2-1', 'field2': 'val2-2'}, # ... ] # Fastest way to INSERT multiple rows. MyModel.insert_many(data_source).execute()
insert_many()
方法还接收多行元组,同时需要提供一个对应的字段。
# We can INSERT tuples as well... data = [('val1-1', 'val1-2'), ('val2-1', 'val2-2'), ('val3-1', 'val3-2')] # But we need to indicate which fields the values correspond to. MyModel.insert_many(data, fields=[MyModel.field1, MyModel.field2]).execute()
在装饰中批量插入是一个好的方法。
# You can, of course, wrap this in a transaction as well: with db.atomic(): MyModel.insert_many(data, fields=fields).execute()
插入大量数据
在大量数据的插入场景下,根据数据源中的行数,您可能需要将其分解为多个块。SQLite
通常有999
或32766
的限制
您可以编写一个循环来将数据批处理成块(在这种情况下,强烈建议您使用事务)
# Insert rows 100 at a time. with db.atomic(): for idx in range(0, len(data_source), 100): MyModel.insert_many(data_source[idx:idx+100]).execute()
peewwee
提供了一个chunked
函数帮助你高效的将普通可迭代对象拆分成为可批处理对象。
from peewee import chunked # Insert rows 100 at a time. with db.atomic(): for batch in chunked(data_source, 100): MyModel.insert_many(batch).execute()
Model.bulk_create()
的行为有点像insert_many()
,但是可以用来插入没有保存的数据库实例,并且可以指定每次插入的数量。如一共插入345,如果指定了一次插入100条记录,那么就是4次插入,3 * 100 + 1 * 45
什么叫没有保存的数据库实例呢?就是类似于 User(username='kk')
,创建的数据库实例。
# Read list of usernames from a file, for example. with open('user_list.txt') as fh: # Create a list of unsaved User instances. users = [User(username=line.strip()) for line in fh.readlines()] # Wrap the operation in a transaction and batch INSERT the users # 100 at a time. with db.atomic(): User.bulk_create(users, batch_size=100)
bulk_update()
和 bulk_create
类似,可以用来插入没有保存的数据库实例,自动添加了一个事务,然后一条条的插入
# List of row data to insert. row_data = [{'username': 'u1'}, {'username': 'u2'}, ...] # Assume there are 789 items in row_data. The following code will result in # 8 total transactions (7x100 rows + 1x89 rows). for row in db.batch_commit(row_data, 100): User.create(**row)
从另一个表批量装载
Model.insert_from()
如果要批量插入的数据存储在另一个表中,还可以创建源为SELECT查询的INSERT查询。
res = (TweetArchive .insert_from( Tweet.select(Tweet.user, Tweet.message), fields=[TweetArchive.user, TweetArchive.message]) .execute())
2、删除
要删除单个模型实例,可以使用model.delete_instance()快捷方式。delete_instance()将删除给定的模型实例,并且可以选择递归地删除任何依赖对象(通过指定recursive=True)。
删除一个记录:Model.delete_instance()
删除任意记录:Model.delete()
3、更新
save()
:单个更新
一旦模型实例有了主键,随后对save()的任何调用都将导致一个UPDATE而不是另一个INSERT。模型的主键不会改变
>>> user.save() # save() returns the number of rows modified. 1 >>> user.id 1 >>> user.save() >>> user.id 1 >>> huey.save() 1 >>> huey.id 2
update
:批量更新
相关阅读 >>
Sqlite 入门教程四 增删改查 有讲究
更多相关阅读请进入《Sqlite》频道 >>

数据库系统概念 第6版
机械工业出版社
本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。
转载请注明出处:木庄网络博客 » python轻量级orm框架 peewee常用功能速查详情
相关推荐
评论
管理员已关闭评论功能...