当前位置:   article > 正文

Peewee 使用

Peewee 使用

Peewee是一个简单小巧的Python ORM,它非常容易学习,并且使用起来很直观。

如果想快速入门,请参考官方的Quckstart

基本知识

官方的Quckstart中,Peewee中 Model 类、fields model 实例与数据库的映射关系如下:

也就是说,一个Model类代表一个数据库的表一个Field字段代表数据库中的一个字段而一个model类实例化对象则代表数据库中的一行。至于Peewee的实现原理,我暂时没有看源代码,但觉得和廖雪峰老师的使用元类这个文章的例子实现类似。

安装: pip install peewee

定义Model,建立数据库

在使用的时候,根据需求先定义好Model,然后可以通过 create_tables()创建表

在peewee模块中,如果已经配置好了mysql数据库的信息,而不想定义Model,可以使用execute_sql() 执行一条sql语句

  1. from peewee import *
  2. # 连接数据库
  3. database = MySQLDatabase('test', user='root', host='localhost', port=3306)
  4. or
  5. # settings = {'host': 'localhost', 'password': '', 'port': 3306, 'user': 'root'}
  6. # database = peewee.MySQLDatabase("test",**settings)
  7. database.execute_sql('')

第一种方式:先定义Model,然后通过db.create_tables()创建或Model.create_table()创建表。

例如,我们需要建一个Person表,我们定义的Model如下:

  1. from peewee import *
  2. # 连接数据库
  3. database = MySQLDatabase('test', user='root', host='localhost', port=3306)
  4. # 定义Person
  5. class Person(Model):
  6. name = CharField(verbose_name='姓名', max_length=10, null=False, index=True)
  7. passwd = CharField(verbose_name='密码', max_length=20, null=False, default='123456')
  8. email = CharField(verbose_name='邮件', max_length=50, null=True, unique=True)
  9. gender = IntegerField(verbose_name='姓别', null=False, default=1)
  10. birthday = DateField(verbose_name='生日', null=True, default=None)
  11. is_admin = BooleanField(verbose_name='是否是管理员', default=True)
  12. class Meta:
  13. database = db # 这里是数据库链接,为了方便建立多个表,可以把这个部分提炼出来形成一个新的类
  14.      table_name = 'persons' # 这里可以自定义表名

然后,我们就可以创建表了

  1. # 创建表
  2. Person.create_table()
  3. # 创建表也可以这样, 可以创建多个
  4. # database.create_tables([Person])

其中,CharField、DateField、BooleanField等这些类型与数据库中的数据类型一一对应,我们直接使用它就行,至于CharField => varchar(255)这种转换Peewee已经为我们做好了 。

全部数据类型

Field TypeSqlitePostgresqlMySQL
IntegerFieldintegerintegerinteger
BigIntegerFieldintegerbigintbigint
SmallIntegerFieldintegersmallintsmallint
AutoFieldintegerserialinteger
FloatFieldrealrealreal
DoubleFieldrealdouble precisiondouble precision
DecimalFielddecimalnumericnumeric
CharFieldvarcharvarcharvarchar
FixedCharFieldcharcharchar
TextFieldtexttextlongtext
BlobFieldblobbyteablob
BitFieldintegerbigintbigint
BigBitFieldblobbyteablob
UUIDFieldtextuuidvarchar(40)
DateTimeFielddatetimetimestampdatetime
DateFielddatedatedate
TimeFieldtimetimetime
TimestampFieldintegerintegerinteger
IPFieldintegerbigintbigint
BooleanFieldintegerbooleanbool
BareFielduntypednot supportednot supported
ForeignKeyFieldintegerintegerinteger

全部属性

  1. null = False – 可否为空
  2. index = False – index索引
  3. unique = False – unique索引
  4. column_name = None – string representing the underlying column to use if different, useful for legacy databases
  5. default = None – 默认值,如果callable, 会调用生成!
  6. primary_key = False – 主键
  7. constraints = None - a list of one or more constraints, e.g. [Check('price > 0')]
  8. sequence = None – sequence to populate field (if backend supports it)
  9. collation = None – collation to use for ordering the field / index
  10. unindexed = False – indicate field on virtual table should be unindexed (SQLite-only)
  11. choices = None – an optional iterable containing 2-tuples of value, display
  12. help_text = None – string representing any helpful text for this field
  13. verbose_name = None – string representing the “user-friendly” name of this field

第二种方式:

已经存在过数据库,则直接通过python -m pwiz批量创建Model。
上面我已经创建好了test库,并且创建了Person表那么,我可以使用下面命令:

  1. # 指定mysql,用户为root,host为localhost,数据库为test
  2. python -m pwiz -e mysql -u root -H localhost --password test > testModel.py

然后,输入密码,pwiz脚本会自动创建Model,内容如下:

  1. from peewee import *
  2. database = MySQLDatabase('test', **{'charset': 'utf8', 'use_unicode': True, 'host': 'localhost', 'user': 'root', 'password': ''})
  3. class UnknownField(object):
  4. def __init__(self, *_, **__): pass
  5. class BaseModel(Model):
  6. class Meta:
  7. database = database
  8. class Person(BaseModel):
  9. birthday = DateField()
  10. is_relative = IntegerField()
  11. name = CharField()
  12. class Meta:
  13. table_name = 'person'

链接数据库

  1. db.is_closed() # 判断数据库是不是链接
  2. db.connect() # 数据库链接

操作数据库

一、增

直接创建示例,然后使用save()就添加了一条新数据

  1. # 添加一条数据
  2. p = Person(name='liuchungui', birthday=date(1990, 12, 20), is_relative=True)
  3. p.save()
  4. data = [
  5. {'facid': 9, 'name': 'Spa', 'membercost': 20, 'guestcost': 30,
  6. 'initialoutlay': 100000, 'monthlymaintenance': 800},
  7. {'facid': 10, 'name': 'Squash Court 2', 'membercost': 3.5,
  8. 'guestcost': 17.5, 'initialoutlay': 5000, 'monthlymaintenance': 80}]
  9. query = Facility.insert_many(data) # 插入了多个
  10. with db.atomic(): # 一次链接
  11. for data_dict in data_source:
  12. MyModel.create(**data_dict)
  13. User.insert(username='Mickey').execute() # >>> 返回主键
  14. # insert_from 是指从一个表查数据快速差到另一个表
  15. query = (TweetArchive
  16. .insert_from(
  17. Tweet.select(Tweet.user, Tweet.message),
  18. fields=[Tweet.user, Tweet.message])
  19. .execute())

二、删

使用delete().where().execute()进行删除,where()是条件,execute()负责执行语句。若是已经查询出来的实例,则直接使用delete_instance()删除。

  1. # 删除姓名为perter的数据
  2. Person.delete().where(Person.name == 'perter').execute()
  3. # 已经实例化的数据, 使用delete_instance
  4. p = Person(name='liuchungui', birthday=date(1990, 12, 20), is_relative=False)
  5. p.id = 1
  6. p.save()
  7. p.delete_instance()

三、改

若是,已经添加过数据的的实例或查询到的数据实例,且表拥有primary key时,此时使用save()就是修改数据;若是未拥有实例,则使用update().where()进行更新数据。

  1. # 已经实例化的数据,指定了id这个primary key,则此时保存就是更新数据
  2. p = Person(name='liuchungui', birthday=date(1990, 12, 20), is_relative=False)
  3. p.id = 1
  4. p.save()
  5. # 更新birthday数据
  6. q = Person.update({Person.birthday: date(1983, 12, 21)}).where(Person.name == 'liuchungui')
  7. q.execute()

四、查

单条数据使用Person.get()就行了,也可以使用Person.select().where().get()。若是查询多条数据,则使用Person.select().where(),去掉get()就行了。语法很直观,select()就是查询,where是条件,get是获取第一条数据。

  1. # 查询单条数据
  2. p = Person.get(Person.name == 'liuchungui')
  3. print(p.name, p.birthday, p.is_relative)
  4. # 使用where().get()查询
  5. p = Person.select().where(Person.name == 'liuchungui').get()
  6. print(p.name, p.birthday, p.is_relative)
  7. #字典展示
  8. query = User.select().dicts()
  9. for row in query:
  10. print(row)
  11. # 查询多条数据
  12. persons = Person.select().where(Person.is_relative == True)
  13. for p in persons:
  14. print(p.name, p.birthday, p.is_relative)
  15. #复合条件
  16. query1 = Person.select().where((Person.name == "fff0") | (Person.name == "sss1"))
  17. query2 = Person.select().where((Person.name == "fff") & (Person.is_relative == True))
  18. #去重
  19. Person.select(Person.name).order_by(Person.name).limit(10).distinct()
  20. #聚合函数
  21. Person.select(fn.MAX(Person.birthday))
  1. # 添加一条数据
  2. Person(name='liuchungui', birthday=date(1990, 12, 20), is_relative=True).save()
  3. 或者
  4. data={name:'liuchungui',birthday:date(1990, 12, 20), is_relative:True}
  5. Person.create(**data)
  6. # 删除姓名为perter的数据
  7. Person.delete().where(Person.name == 'perter').execute()
  8. # 更新数据
  9. Person.update(birthday=date(1983, 12, 21)).where(Person.name == 'liuchungui').execute()
  10. 或者
  11. data={name:'liuchungui',birthday:date(1990, 12, 20), is_relative:True}
  12. Person.update(data)
  13. # 查询单条数据
  14. person =Person.get_or_none(Person.name == 'liuchungui')
  15. # 查询多条数据
  16. persons =persons = Person.select().where(Person.is_relative == True)
  17. # 查询name为liuchungui的Person数量, 返回数量为1
  18. num = Person.select().where(Person.name == 'liuchungui').count()
  19. # 按照创建时间降序排序
  20. persons = Person.select().order_by(Person.create_time.desc())
  21. # 按照创建时间升序排序
  22. persons = Person.select().order_by(Person.create_time.asc())
  23. # 按照创建时间升序前5个
  24. persons = Person.select().order_by(Person.create_time.asc()).limit(5)
  25. # 查询湖南和湖北的 (| & 用法), 注意需要用()将Person.province == '湖南'包一层
  26. persons = Person.select().where((Person.province == '湖南') | (Person.province == '湖北'))
  27. #In 查询
  28. query = Facility.select().where(Facility.facid.in_([1, 5]))
  29. # 联表查询
  30. query = (Tweet.select(Tweet.name, Person.username).join(Person, on=(Tweet.id == Person.tweet_id)).order_by(Person.timestamp.desc()))
  31. # %使用,查询省份中含有 湖 字,sql语句:select * from person where province like '%湖%'
  32. persons = Person.select().where(Person.province % '%湖%')
  33. #模糊查询
  34. #SELECT * FROM person WHERE name ILIKE '%tennis%';
  35. Person.select().where(Person.name ** "%fff%")
  36. Person.select().where(Facility.name.contains('tennis'))
  37. # <<使用,查询省份属于湖北和湖南的,对应sql语句:select * from person where province in ('湖南', '湖北')
  38. persons = Person.select().where(Person.province << ['湖南', '湖北'])
  39. # >>使用,查询省份为空的,sql语句: select * from person where province is Null
  40. persons = Person.select().where(Person.province >> None)
  41. # paginate方法使取得某页数据容易,两个参数:page_number, items_per_page desc()倒叙 asc()正序
  42. Person.select().order_by(Person.id.dese()).paginate(2, 10)
  43. # group_by 用法 统计相同名字人的数量 alias起别名 查看数量用obj.num_tweets
  44. Model.select(Model,fn.count(Model.name).alias('num_tweets')).group_by(Model.name)
  45. #执行SQL
  46. database = MySQLDatabase('test', **{'charset': 'utf8', 'use_unicode': True, 'host': 'localhost', 'user': 'root', 'password': ''})
  47. database.execute_sql('')
  48. query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data)
  49. query = MyModel.select().where(SQL('Some SQL expression %s' % user_data))

连接数据库

连接数据库时,推荐使用 playhouse 中的 db_url 模块。db_url 的 connect 方法可以通过传入的 URL 字符串,生成数据库连接。

  1. from playhouse.db_url import connect
  2. mysql_config_url='mysql://root:root@localhost:3306/network'
  3. db = connect(mysql_config_url)

表关系ForeignKey

  1. class Pet(peewee.Model):
  2. name = peewee.CharField()
  3. owner = peewee.ForeignKeyField(Person,related_name="pets",backref="petties")
  4.   # backref是反查的字段,如果有related_name用related_name反查,如果没有直接用petties反查 e.g. [i.name for i in Person.get(name="aaa").petties]
  5. class Meta:
  6. database = db
  7. #自关联
  8. class Category(Model):
  9. name = CharField()
  10. parent = ForeignKeyField('self', null=True, backref='children')
  11. # 注意自关联永远是null = True
  12. #正查
  13. dog1 = Pet.get(name="dog1")
  14. dog1.owner.name
  15. # 反查
  16. aaa = Person.get(name="aaa").pets # pets为related_name字段,如果没写用backref字段
  17. for a in aaa:
  18. print(i.name)

连接池的使用

peewee 的连接池,使用时需要显式的关闭连接。下面先说下为什么,最后会给出推荐的使用方法,避免进坑

Connections will not be closed exactly when they exceed their stale_timeout. Instead, stale connections are only closed when a new connection is requested.

这里引用官方文档的提示。大致说:“超时连接不会自动关闭,只会在有新的请求时是才会关闭

所以,每次操作完数据库就关闭连接实例。

用法1:使用with

  1. def send_rule():
  2.     with db.execution_context():
  3.     # A new connection will be opened or, if using a connection pool,
  4.     # pulled from the pool of available connections. Additionally, a
  5.     # transaction will be started.
  6.         for user in get_all_user():
  7.             user_id = user['id']
  8.             rule = Rule(user_id)
  9.             rule_dict = rule.slack_rule(index)
  10.             .....do something.....

用法2:使用Flask hook

  1. @app.before_request
  2. def _db_connect():
  3.     database.connect()
  4. #
  5. # This hook ensures that the connection is closed when we've finished
  6. # processing the request.
  7. @app.teardown_request
  8. def _db_close(exc):
  9.     if not database.is_closed():
  10.         database.close()
  11. #
  12. #
  13. # 更优雅的用法:
  14. from playhouse.flask_utils import FlaskDB
  15. from dock_fastgear.model.base import db
  16. #
  17. app = Flask(__name__)
  18. FlaskDB(app, db)  # 这样就自动做了上面的事情(具体实现可查看http://docs.peewee-orm.com/en/latest/peewee/playhouse.html?highlight=Flask%20DB#flask-utils)

处理查询结果

这里没有什么大坑,就是有两点需要注意:

首先,查询的结果都是该 Model 的 object,注意不是 dict。如果想让结果为 dict,需要 playhouse 模块的工具方法进行转化:from playhouse.shortcuts import model_to_dict其次,get方法只会返回一条记录

  1. from playhouse.shortcuts import model_to_dict
  2. projects = Model.select().where(*cond).paginate(page_index, page_size)
  3. result = [model_to_dict(project) for project in projects]

 peewee事务

atomic和rollback

  1. with db.atomic() as transaction: # Opens new transaction.
  2. try:
  3. save_some_objects()
  4. except ErrorSavingData:
  5. # Because this block of code is wrapped with "atomic", a
  6. # new transaction will begin automatically after the call
  7. # to rollback().
  8. transaction.rollback()
  9. error_saving = True
  10. #装饰器模式
  11. @db.atomic()
  12. def create_user(username):
  13. # This statement will run in a transaction. If the caller is already
  14. # running in an `atomic` block, then a savepoint will be used instead.
  15. return User.create(username=username)
  16. create_user('charlie')

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号