peewee操作mysql
前言
推荐查看官方文档:https://www.osgeo.cn/peewee/
安装peewee
pip3 install peewee
• 在学习peewee之前先了解下ORM(Object Relational Mapping)对象关系映射,解决面向对象与关系数据库不匹配的技术。
peewee 是什么?
• peewee是一种轻量级的python ORM
• 可以理解为MongoDB与NoSQL的关系
• 在学习peewee时,发现其中大量使用了内部类,就重新补充了一下关于内部类的知识点
• 关于使用内部类的优势
• 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象那个的信息相互独立;
• 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类;
• 创建内部类对象的时刻并不依赖于外围类对象的创建;
• 内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体。
• 安装mysql
• 从官网安装最新版本
• 注意的是安装完成后需要修改密码才能进行外部连接
常用操作
操作 | 解释 |
---|---|
Db.is_closed() | 判断数据库是不是连接 |
Db.connect() | 连接数据库 |
Db.create_tables([Person,]) | 建表 |
Person.create_tables() | 建表(Person是一个类名) |
关于主键和约束 #都是在类的内部类中定义
class Person(Model):
first = CharField()
last = CharField()
class Meta:
primary_key = CompositeKey('first', 'last')
class Pet(Model):
owner_first = CharField()
owner_last = CharField()
pet_name = CharField()
class Meta:
constraints = [SQL('FOREIGN KEY(owner_first, owner_last) '
'REFERENCES person(first, last)')]
插入数据
P=Person.create(name=’name’,sex=’sex’);
p.save();
或者使用
Person.insert(name=’name’,sex=’sex’).execute();
关于从一个表查数据快速插到另一个表 还需再思考下
修改数据
Tweet.update(message='这是修改过的数据').where(Tweet.id==3).execute();
注意的是,在peewee中条件中使用‘==’。更新的数据不用加类名,但是条件中的字段必须加类名。
数据删除
Tweet.update(message='这是修改过的数据3').where(Tweet.id==3).execute();
数据查询
User.get(id=1);#一条
User.get_by_id(1);
ts=Tweet.filter(user_id=1);#所有迭代器
注意:使用get()并且没有参数返回的是查询的id
复合查询
q=User.select().where((User.id==1)|(User.username=='tom')).get();
q=User.select().where((User.id==1)&(User.username=='tom')).get();
模糊查询
#select * from Tweet where message like ’%数据%’;
print(Tweet.select().where(Tweet.message ** "%数据%").get());
print(Tweet.select().where(Tweet.message.contains('数据')).get());
in 查询
print(Tweet.select().where(Tweet.id.in_([1, 2])).get());
Order by; Limit; Distinct; Group by; Having
query = (Person.select(Person.name).order_by(Person.name).limit(10).distinct()) # 几乎和sql一模一样
Person.select().order_by(Person.birthday.desc()) # 日期排序
query = (Booking
.select(Booking.facid, fn.SUM(Booking.slots))
.group_by(Booking.facid) # group_by
.order_by(Booking.facid))
query = (Booking
.select(Booking.facility, fn.SUM(Booking.slots))
.group_by(Booking.facility)
.having(fn.SUM(Booking.slots) > 1000) # having
.order_by(Booking.facility))
Tweet.select().order_by(-Tweet.created_date)
# 返回查到了多少条记录
Tweet.select().where(Tweet.id > 50).count()
连表查询
g1 = Person.select().join(Pet).where(Pet.name == "dog2")
实例讲解
参考(https://www.cnblogs.com/fnng/p/6879779.html)
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
from peewee import *;
import datetime;
#建立连接mysql时的必要参数
db = MySQLDatabase('peewee_learn',host ='127.0.0.1',user='root',passwd='123456');
#连接数据库
db.connect();
class BaseModel(Model):
#内部类
class Meta:
database=db;#每一个继承BaseModel类的子类都是连接db表
class User(BaseModel):
username=CharField(unique=True);#每个属性都是一个表字段
class Tweet(BaseModel):
user = ForeignKeyField(User,related_name='tweets');
message = TextField();
created_date = DateTimeField(default=datetime.datetime.now);
is_published = BooleanField(default=True);
if __name__ == '__main__':
#创建表
User.create_table();#创建User表
Tweet.create_table();#创建Tweet表
#插入数据
#user=User.create(username='tom');
#Tweet.create(user=user,message='这是一段文字');
#Tweet.create(user_id=1,message='这是第二段文字');
#查询数据get()
#t = Tweet.get(message="这是一段文字")
#print(t.user_id)
#print(t.created_date)
#print(t.is_published)
#filter
#ts=Tweet.filter(user_id=1);
#for t in ts:
#print(t.message);
首先,导入peewee库下面的所有方法,这个当然需要。
然后,通过MySQLDatabase连接数据库,把数据连接的几个必要参数一一填写。通过connect()方法与MySQL数据库建立链接。
接下来就是表的创建,创建BaseModel类,在该类下创建子类Meta,Meta是一个内部类,它用于定义peewee的Model类的行为特性。指定dabatase 为 前面定义的db。
再接下来就是表的创建了,我们在SQL语句创建表时一般需要知道以下信息。表的名字,表有哪些字段?这些字段分别是什么类型?是否允许为空,或自增?哪个字段是主键是?哪个是外键?
ORM用编程语言里的概念帮我们映射了这些东西。
创建 User 和 Tweet类做为表名。在类下面定义的变量为字段名,如username、message、created_date等。通过CharField、DateTimeField、BooleanField表示字段的类型。ForeignKeyField 建立外键。 主键呢? 建表时不都要有个主键id嘛,不用!peewee默认已经为我们加上这个id了。
最后,执行create_table()方法创建两张表。
通过数据库工具,查看生成的两张表。
插入数据
要想操作数据,首先表里需要有数据。先来看看如何添加数据。
if name == “main”:
# …
user = User.create(username=‘tom’)
Tweet.create(user=user, message=“这是一段文字”)
这样就要User表里添加一个tom的用户,这用户发了一条Tweet,在Tweet表里。但这个用户兴致来了,想继续发第二条Tweet。于是:
if name == “main”:
# …
Tweet.create(user_id=1, message=“这是第二段文字”)
咦~!?不对,我们没有创建user_id字段啊!但是,如果你查询Tweet表,就会发现有这个字段,用它来关联User表的id
查询数据
if name == “main”:
# …
# 查询 1
t = Tweet.get(message=“这是一段文字”)
print(t.user_id)
print(t.created_date)
print(t.is_published)
查询结果
1
2017-05-19 15:44:32
True
不过,get()方法只能查询一条,且是唯一的一条数据;通过查询条件不能查询出多条,也不能查询出0条。
if name == “main”:
# …
# 查询 2
ts = Tweet.filter(user_id=1)
for t in ts:
print(t.message)
运行结果
这是一段文字
这是第二段文字
而,filter()方法,就会更加灵活,可以查询多条结果,并把结果循环输出。
删除数据
参考https://www.cnblogs.com/fnng/p/6879779.html
https://www.cnblogs.com/yxi-liu/p/8514763.html
模型定义
模型类,字段和模型实例都映射到数据库的概念:
对象 | 对应于 |
---|---|
模型类 | 数据库表 |
字段实例 | 表上的行 |
模型实例 | 数据库表中的行 |
当使用Peewee启动项目时,通常最好从数据模型开始,通过定义一个或多个 Model 类别:
from peewee import *
db = SqliteDatabase('people.db')
class Person(Model):
name = CharField()
birthday = DateField()
class Meta:
database = db # This model uses the "people.db" database.
注解:
Peewee将自动从类的名称推断数据库表名。您可以通过指定 table_name 内部“meta”类中的属性(
与 database 属性)。要进一步了解peewee如何生成表名,请参阅 表名 部分。
还要注意,我们为模型命名 Person 而不是 People . 这是您应该遵循的一个惯例——即使表将包含多个人,
我们总是使用单数形式命名类。
有很多 field types 适用于存储各种类型的数据。Peewee手柄在 pythonic 值那些数据库使用的值,这样您就可以在代码中使用Python类型,而无需担心。
外键的使用
有很多 field types 适用于存储各种类型的数据。Peewee手柄在 pythonic 值那些数据库使用的值,这样您就可以在代码中使用Python类型,而无需担心。
当我们使用 foreign key relationships . 这对于Peewee很简单:
class Pet(Model):
owner = ForeignKeyField(Person, backref='pets')
name = CharField()
animal_type = CharField()
class Meta:
database = db # this model uses the "people.db" database
既然我们有了模型,我们就连接到数据库。尽管不需要显式打开连接,但这是一种好的做法,因为它会立即显示数据库连接中的任何错误,而不是在执行第一个查询之后的某个任意时间。完成连接后关闭连接也很好——例如,Web应用程序可能在收到请求时打开连接,并在发送响应时关闭连接。
db.connect()
我们将首先在数据库中创建存储数据的表。这将创建具有适当列、索引、序列和外键约束的表:
db.create_tables([Person, Pet])
增加数据
让我们先用一些人填充数据库。我们将使用 save() 和 create() 方法添加和更新人员记录。
from datetime import date
uncle_bob = Person(name='Bob',birthday = date(1960,1,15))
uncle_bob.save()# bob is now stored in the database
# Returns: 1
注解:
Returns: 1
当你调用 save() ,返回修改的行数。
您也可以通过 create() 方法,返回模型实例:
grandma = Person.create(name= 'Grandma', birthday=date(1935,3,1))
herb = Person.create(name='Herb',birthday=date(1950,5,5))
更新数据
要更新行,请修改模型实例并调用 save() 坚持改变。在这里,我们将更改奶奶的姓名,然后将更改保存到数据库中:
grandma.name = 'Grandma L.'
grandma.save() # Update grandma's name in the database.
# Returns: 1
现在我们在数据库中存储了3个人。让我们给他们一些宠物。奶奶不喜欢家里的动物,所以她没有,但赫伯是个动物爱好者:
bob_kitty = Pet.create(owner=uncle_bob, name='Kitty', animal_type='cat')
herb_fido = Pet.create(owner=herb, name='Fido', animal_type='dog')
herb_mittens = Pet.create(owner=herb, name='Mittens', animal_type='cat')
herb_mittens_jr = Pet.create(owner=herb, name='Mittens Jr', animal_type='cat')
删除数据
在漫长的一生之后,宠物会生病死亡。我们需要将他从数据库中删除:
herb_mittens.delete_instance() # he had a great life
# Returns: 1
注解
的返回值 delete_instance() 是从数据库中删除的行数。
修改数据
鲍勃叔叔认为在赫伯家里有太多动物死亡,所以他收养了fido:
herb_fido.owner = uncle_bob
herb_fido.save()
查询数据
我们数据库的真正优势在于它允许我们通过 查询. 关系数据库非常适合进行即席查询。
查询单个数据
让我们从数据库中检索奶奶的记录。要从数据库中获取单个记录,请使用 Select.get() :
grandma = Person.select().where(Person.name == ‘Grandma L.’).get()
grandma = Person.select().where(Person.name == 'Grandma L.').get()
我们也可以使用等价的速记法 Model.get() :
grandma = Person.get(Person.name == 'Grandma L.')
查询全部数据
让我们列出数据库中的所有人员:
for person in Person.select():
print(person.name)
-----打印结果如下:---------
# Bob
# Grandma L.
# Herb
让我们列出所有的猫和它们的主人的名字:
query = Pet.select().where(Pet.animal_type == 'cat')
for pet in query:
print(pet.name, pet.owner.name)
-----打印结果如下:---------
# Kitty Bob
# Mittens Jr Herb
注意
前一个查询有一个大问题:因为我们正在访问 pet.owner.name 我们在原始查询中没有选择这个关系,
Peewee需要执行一个额外的查询来检索宠物的主人。这种行为被称为 N+1 一般应该避免。
有关处理关系和联接的深入指南,请参阅 本人的下一篇
我们可以通过同时选择这两个选项来避免额外的查询 Pet 和 人, 添加一个 join.
我们可以通过同时选择这两个选项来避免额外的查询 Pet 和 人, 添加一个 join.
query = (Pet
.select(Pet, Person)
.join(Person)
.where(Pet.animal_type == 'cat'))
for pet in query:
print(pet.name, pet.owner.name)
-----打印结果如下:---------
# Kitty Bob
# Mittens Jr Herb
让我们把鲍勃所有的宠物都买下来:(查询鲍勃的所有宠物)
for pet in Pet.select().join(Person).where(Person.name == 'Bob'):
print(pet.name)
-----打印结果如下:---------
# Kitty
# Fido
通过模型实例对象来查询鲍勃的所有宠物
我们可以在这里做另一件很酷的事情来得到鲍勃的宠物。因为我们已经有了一个对象来代表Bob,所以我们可以这样做:
for pet in Pet.select().where(Pet.owner == uncle_bob):
print(pet.name)
分组 order_by( )
for pet in Pet.select().where(Pet.owner == uncle_bob).order_by(Pet.name):
print(pet.name)
-----打印结果如下:---------
# Fido
# Kitty
现在让我们列出所有人,从最年轻到最年长:
for person in Person.select().order_by(Person.birthday.desc()):#desc()降序方法
print(person.name, person.birthday)
-----打印结果如下:---------
# Bob 1960-01-15
# Herb 1950-05-05
# Grandma L. 1935-03-01
分组筛选表达式
Peewee支持任意嵌套表达式。让我们查询生日是的人:
1940年以前(奶奶)
1959年以后(鲍勃)
query = (Person
.select()
.where(Person.birthday<d1940) | (Person.birthday<d1960))
for person in query:
print(person.name,person.birthday)
-----打印结果如下:---------
# Bob 1960-01-15
# Grandma L. 1935-03-01
现在让我们做相反的事情。1940年至1960年生日的人:
query = (Person
.select
.where(Person.birthday.between(d1940,d1960)))
for person in query:
print(person.name,person.birthday)
-----打印结果如下:---------
# Herb 1950-05-05
聚合和预取
让我们列出所有人 and 他们有多少宠物:
for person in Person.select():
print (person.name,person.pets.count(),'pets')
-----打印结果如下:---------
# Bob 2 pets
# Grandma L. 0 pets
# Herb 1 pets
我们又碰到了一个经典的例子 N+1 查询行为。在本例中,我们对 Person 原件返还 SELECT !我们可以通过执行 JOIN 并使用SQL函数聚合结果。
以名字排序查询出每个人有多少只宠物
query = (Person
.select(Person, fn.COUNT(Pet.id).alias('pet_count')
.join(Pet,JOIN.LEFT_OUTER) #包括没有宠物的人
.group_by(Person)
.order_by(Person.name))
for person in query:
print(person.name,person.pet_count,'pets') #pet_count是返回模型实例上的一个属性
fn.COUNT(Pet.id).alias(‘pet_count’) 想当于COUNT(pet.id) AS pet_count .
注解
Peewee提供了一个神奇的助手 fn() ,可用于调用任何SQL函数。在上面的例子中, fn.COUNT(Pet.id).alias('pet_count') 会被翻译成 COUNT(pet.id) AS pet_count .
现在让我们列出所有的人和他们所有宠物的名字。你可能已经猜到了,这很容易变成另一个 N+1 如果我们不小心的话。
在深入到代码中之前,请考虑这个示例与前面的示例有何不同,我们在这里列出了所有宠物及其所有者的名称。宠物只能有一个主人,所以当我们从 Pet 到 Person 总有一场比赛。我们加入时情况不同 Person 到 Pet 因为一个人可能没有宠物或者他们可能有几个宠物。因为我们使用的是关系数据库,如果我们要从 Person 到 Pet 然后每个有多只宠物的人都会被重复,每只宠物一次。
如下所示:
query = (Person
.select(Person,Pet)
.join(Pet,JOIN.LEFT_OUTER) # 左外连接
.order_by(Person.name,Pet.name))
# 我们需要检查他们是否有宠物的实例附加属性,不是所有的人们都养猫
for person in query:
if hasattr(person,"pet"):#hasattr()函数用于判断对象是否包含对应的属性。hasattr(object, name)
print(person.name,person.pet.name)
else:
print(person.name,"no pets")
-----打印结果如下:---------
# Bob Fido
# Bob Kitty
# Grandma L. no pets
# Herb Mittens Jr
通常这种类型的复制是不可取的。以适应更常见(和直观)的工作流,即列出一个人并附加 a list 在那个人的宠物中,我们可以使用一种特殊的方法: prefetch()预取 :
query = Person.select().order_by(Person.name).prefech(Pet)
for person in query:
print(person.name)
for pet in person.pets:
print(' *',pet.name)
-----打印结果如下:---------
# Bob
# * Kitty
# * Fido
# Grandma L.
# Herb
# * Mittens Jr
SQL函数
模糊查询
最后一个查询。这将使用SQL函数查找名称以大写或小写开头的所有人员 G:
expression = fu.Lower(fn.Substr(Person.name,1,1)) == 'g'
for person in Person.select().where(expression):
print(person.name)
-----打印结果如下:---------
# Grandma L.
数据库
数据库处理完毕,让我们关闭连接:
db.close()
在实际的应用程序中,有一些已建立的模式用于管理数据库连接生存期。例如,Web应用程序通常会在请求开始时打开连接,并在生成响应后关闭连接。A connection pool 有助于消除与启动成本相关的延迟。
使用现有数据库
如果已经有了数据库,可以使用 Pwiz,模型发生器 . 例如,如果我有一个名为 charles_blog, 我可能会跑:
python -m pwiz -e postgresql charles_blog > blog_models.py
总结
#存储数据
##方式一:save()
uncle_bob = Person(name='Bob',birthday = date(1960,1,15))
uncle_bob.save()
模型实例名 = 模型类名(字段实例名1=“字段数据”,字段实例名2=“字段数据”)
模型实例名.save()
##方式二:create()
grandma = Person.create(name='Grandma', birthday=date(1935, 3, 1))
模型实例名 = 模型类.create(字段实例名1=“字段数据”,字段实例名2=“字段数据”)
---------------------------------------------
# 更新
grandma.name = 'Grandma L.'
grandma.save()
#模型实例名.字段实例名=“新字段数据”
#模型实例名.save()
----------------------------------------------
# 删除
herb_mittens.delete_instance()
#模型实例名.delete_instance()
----------------------------------------------
# 查询
## 查询单个数据
### 方法一:Select.get()
grandma = Person.select().where(Person.name == 'Grandma L.').get()
#模型实例名 = 模型类.select().where(表名.字段名(列) == ‘字段名数据‘).get()
### 方法二:Model.get()
grandma = Person.get(Person.name == 'Grandma L.')
#模型实例名 = 模型类.get(模型类.字段名(列) == ‘字段名数据‘).get()
## 查询所有数据(列出数据库中的所有人员)
for person in Person.select():
print(person.name)
#个人总结
for i in 模型类.select():
print(i.字段名)
# 以上查询的是列中所有数据
# 查询所有符合条件的数据(列出所有的猫和它们的主人的名字) 方式一:
query = Pet.select().where(Pet.animal_type == 'cat')
for pet in query:
print(pet.name, pet.owner.name)
#个人总结
query = 模型类.select().where(模型类.查询条件)
for i in query:
print(i.字段名1,i.外键名.字段名2)
# 方式二:我们可以通过同时选择这两个选项来避免额外的查询 Pet 和 人, 添加一个 join.
query = (Pet
.select(Pet, Person)
.join(Person)
.where(Pet.animal_type == 'cat'))
for pet in query:
print(pet.name, pet.owner.name)
# 个人总结
query = 模型名1.select(模型类1,模型类2).join(模型类2).where(模型类1.查询条件)
for i in query:
print(i.字段名1,i.外键名.字段名2)
# 查询相关