PEEWEE基本使用
Content
Ⅰ 安装
Ⅱ 链接数据库
Ⅲ 建表
Ⅳ 增删改
Ⅴ 基础查询
Ⅵ ForeignKey
Ⅷ 事务参考官方文档:http://docs.peewee-orm.com/en/latest/index.html
1. 安装
pip install peewee
2. 链接数据库
以mysql 为例(Peewee提供mysql,postgresql,sqllite)的支持
import peewee
settings = {'host': 'localhost', 'password': '', 'port': 3306, 'user': 'root'} db = peewee.MySQLDatabase("test",**settings)
3. 建表
3.1 表关系
class Person(Model): name = CharField(verbose_name='姓名', max_length=10, null=False, index=True) passwd = CharField(verbose_name='密码', max_length=20, null=False, default='123456') email = CharField(verbose_name='邮件', max_length=50, null=True, unique=True) gender = IntegerField(verbose_name='姓别', null=False, default=1) birthday = DateField(verbose_name='生日', null=True, default=None) is_admin = BooleanField(verbose_name='是否是管理员', default=True) class Meta: database = db # 这里是数据库链接,为了方便建立多个表,可以把这个部分提炼出来形成一个新的类
table_name = 'persons' # 这里可以自定义表名
# 例如:
class BaseModel(Model): class Meta: database = db class Person(BaseModel): name = CharField()
全部数据类型
Field Type | Sqlite | Postgresql | MySQL |
---|---|---|---|
IntegerField |
integer | integer | integer |
BigIntegerField |
integer | bigint | bigint |
SmallIntegerField |
integer | smallint | smallint |
AutoField |
integer | serial | integer |
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 | longtext |
BlobField |
blob | bytea | blob |
BitField |
integer | bigint | bigint |
BigBitField |
blob | bytea | blob |
UUIDField |
text | uuid | varchar(40) |
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 |
null = False – 可否为空 index = False – index索引 unique = False – unique索引 column_name = None – string representing the underlying column to use if different, useful for legacy databases default = None – 默认值,如果callable, 会调用生成! primary_key = False – 主键 constraints = None - a list of one or more constraints, e.g. [Check('price > 0')] sequence = None – sequence to populate field (if backend supports it) collation = None – collation to use for ordering the field / index unindexed = False – indicate field on virtual table should be unindexed (SQLite-only) choices = None – an optional iterable containing 2-tuples of value, display help_text = None – string representing any helpful text for this field verbose_name = None – string representing the “user-friendly” name of this field
3.2 链接数据库
db.is_closed() # 判断数据库是不是链接 db.connect() # 数据库链接
3.3 建库
db.create_tables([Person,]) #建库
# 如果数据表已经存在,执行create_table的时候,将会抛出异常。
3.4 主键和约束
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)')]
class BlogToTag(Model): """A simple "through" table for many-to-many relationship.""" blog = ForeignKeyField(Blog) tag = ForeignKeyField(Tag) class Meta: primary_key = CompositeKey('blog', 'tag')
User._meta.auto_increment = False # turn off auto incrementing IDs
4. 插&改&删
4.1 插
p = Person.create(name='master') ####### 或 ######### p = Person(name="master") p.save()
data = [
{'facid': 9, 'name': 'Spa', 'membercost': 20, 'guestcost': 30,
'initialoutlay': 100000, 'monthlymaintenance': 800},
{'facid': 10, 'name': 'Squash Court 2', 'membercost': 3.5,
'guestcost': 17.5, 'initialoutlay': 5000, 'monthlymaintenance': 80}]
query = Facility.insert_many(data) # 插入了多个
with db.atomic(): # 一次链接
for data_dict in data_source:
MyModel.create(**data_dict)
User.insert(username='Mickey').execute() # >>> 返回主键
# insert_from 是指从一个表查数据快速差到另一个表 query = (TweetArchive .insert_from( Tweet.select(Tweet.user, Tweet.message), fields=[Tweet.user, Tweet.message]) .execute())
4.2 改
p.email = "qq@qq.com" # p 为记录对象 p.save() # 会调用update 但是不要这么写!
query = (Facility .update(membercost=6, guestcost=30) .where(Facility.name.startswith('Tennis')))
query.execute() # 元子操作
4.3 注意
插入的时候,如果字段设置了default值,则会按照default指定的值插入,如果没有指定,同时字段可以为null,则数据库自动初始化值为null,如果字段不能为null,则数据库自动初始化为其零值。 如果字段为非Null,最好设置default值,同时数据库schema也设置其default值,如果字段为可以为null,那么初始值就设置为null即可。
4.4 数据删除
p.delete_instance()
query = Member.delete().where(Member.memid == 37)
# DELETE FROM members WHERE NOT EXISTS (SELECT * FROM bookings WHERE bookings.memid = members.memid);
subq = Booking.select().where(Booking.member == Member.memid)
query = Member.delete().where(~fn.EXISTS(subq)) # 同样这样的操作是被推荐的
5. 基础查询
基本和sql语句相同
User.get(User.id == 1)
User.get_by_id(1) # Same as above.
User[1] # Also same as above.
g = Person.select().where(Person.name == 'Grandma L.').get() # where是查询一个集合, select是查询字段 g = Person.get(Person.name == 'fff.') # get是得到第一个
g = Person.select().where(Person.age > 23).get()
# select 代表sql语句中select后面的语句表示要展示的字段 # where 代表where条件语句 得到一个数据集合,用for循环遍历 # get()代表找第一个
5.1 复合条件
query1 = Person.select().where((Person.name == "fff0") | (Person.name == "sss1")) query2 = Person.select().where((Person.name == "fff") & (Person.is_relative == True))
== x equals y < x is less than y <= x is less than or equal to y > x is greater than y >= x is greater than or equal to y != x is not equal to y << x IN y, where y is a list or query >> x IS y, where y is None/NULL % x LIKE y where y may contain wildcards ** x ILIKE y where y may contain wildcards ^ x XOR y ~ Unary negation (e.g., NOT x)
5.2 模糊查询
#SELECT * FROM person WHERE name ILIKE '%tennis%'; Person.select().where(Person.name ** "%fff%").get() ######### OR ############## query = Facility.select().where(Facility.name.contains('tennis'))
5.3 In 查询
# SELECT * FROM facilities WHERE facid IN (1, 5); query = Facility.select().where(Facility.facid.in_([1, 5]))
person, created = Person.get_or_create( first_name=first_name, last_name=last_name, defaults={'dob': dob, 'favorite_color': 'green'}) # defaults dictionary, which will be used to populate values on newly-created instances.
query = User.select().dicts() for row in query: print(row) # row 是什么字典
5.4 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)
# Similarly you can use "+" to indicate ascending order, though ascending
5.5 聚合函数
# SELECT MAX(birthday) FROM person; query = Person.select(fn.MAX(Person.birthday))
# SELECT name, is_relative FROM person WHERE birthday = (SELECT MAX(birthday) FROM person);
MemberAlias = Member.alias() # 如果一个查询中用了两个表,需要这个Alias作为影子
subq = MemberAlias.select(fn.MAX(MemberAlias.joindate))
query = (Member.select(Person.is_relative, Person.name, ).where(Person.birthday == subq))
5.6 分页&计数
for tweet in Tweet.select().order_by(Tweet.id).paginate(2, 10): print(tweet.message) # paginate两个参数:page_number 和 items_per_page
# 返回查到了多少条记录 Tweet.select().where(Tweet.id > 50).count()
5.7 SQL
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data) query = MyModel.select().where(SQL('Some SQL expression %s' % user_data)) # sql 注入: ensure that any user-defined data is passed in as a query parameter and not part of the actual SQL query
6. ForeignKey
6.1 表关系
class Pet(peewee.Model): name = peewee.CharField() owner = peewee.ForeignKeyField(Person,related_name="pets",backref="petties")
# backref是反查的字段,如果有related_name用related_name反查,如果没有直接用petties反查 e.g. [i.name for i in Person.get(name="aaa").petties]
class Meta: database = db
class Category(Model): name = CharField() parent = ForeignKeyField('self', null=True, backref='children') # 注意自关联永远是null = True
6.2 插
g2 = Person.get(tablesinfo.Person.is_relative == False) d2 = Pet.create(name="dog2",owner=g2)
6.3 正反查
#正查 dog1 = Pet.get(name="dog1") dog1.owner.name # 反查 aaa = Person.get(name="aaa").pets # pets为related_name字段,如果没写用backref字段 for a in aaa: print(i.name)
6.4 连表查询
g1 = Person.select().join(Pet).where(Pet.name == "dog2")
6.5 复杂连表
# SELECT DISTINCT m.firstname, m.surname FROM members AS m2 INNER JOIN members AS m ON (m.memid = m2.recommendedby) ORDER BY m.surname, m.firstname; MA = Member.alias() query = (Member .select(Member.firstname, Member.surname) .join(MA, on=(MA.recommendedby == Member.memid)) # join中用on表示链接方法 .order_by(Member.surname, Member.firstname))
6.6 连多表
User.select().join(Tweet).join(Comment) Artist.select().join(Album).switch(Artist).join(Genre) # 如果连一表多次
6.7 多对多
class BaseModel(Model): class Meta: database = db class Student(BaseModel): name = CharField() class Course(BaseModel): name = CharField() students = ManyToManyField(Student, backref='courses')
StudentCourse = Course.students.get_through_model() # 中间表
huey = Student.get(Student.name == 'Huey') # We can add all the objects represented by a query. english_courses = Course.select().where( Course.name.contains('english')) huey.courses.add(english_courses)
huey = Student.get(Student.name == 'Huey') for course in huey.courses.order_by(Course.name): print(course.name)
huey.courses.remove(Course.select().where(Course.name.startswith('CS'))) engl_101.students.remove(huey)
7 事务
with db.atomic() as transaction: # Opens new transaction. try: save_some_objects() except ErrorSavingData: # Because this block of code is wrapped with "atomic", a # new transaction will begin automatically after the call # to rollback(). transaction.rollback() error_saving = True
db = SqliteDatabase(':memory:') with db.atomic() as txn: # This is the outer-most level, so this block corresponds to # a transaction. User.create(username='charlie') with db.atomic() as nested_txn: # This block corresponds to a savepoint. User.create(username='huey') # This will roll back the above create() query. nested_txn.rollback() User.create(username='mickey') # When the block ends, the transaction is committed (assuming no error # occurs). At that point there will be two users, "charlie" and "mickey".
@db.atomic() def create_user(username): # This statement will run in a transaction. If the caller is already # running in an `atomic` block, then a savepoint will be used instead. return User.create(username=username) create_user('charlie')
# 纯手动用commit()提交,用rollback回滚 with db.transaction() as txn: User.create(username='mickey') txn.commit() # Changes are saved and a new transaction begins. User.create(username='huey') # Roll back. "huey" will not be saved, but since "mickey" was already # committed, that row will remain in the database. txn.rollback() # 半自动用savepoint上下文管理 with db.transaction() as txn: with db.savepoint() as sp: User.create(username='mickey') with db.savepoint() as sp2: User.create(username='zaizee') sp2.rollback() # "zaizee" will not be saved, but "mickey" will be.
8 其他
import uuid class UUIDField(Field): field_type = 'uuid' def db_value(self, value): return str(value) # convert UUID to str def python_value(self, value): return uuid.UUID(value) # convert str to UUID
Person._meta.fields # 用访问_meta访问定义的meta Person._meta.primary_key Person._meta.database # 属性有:database, table_name, table_function, indexes, primary_key, constraints, schema, only_save_dirty, options, table_alias, depends_on, without_rowid
class Article(Model): name = TextField() timestamp = TimestampField() status = IntegerField() flags = IntegerField() # Add an index on "name" and "timestamp" columns. Article.add_index(Article.name, Article.timestamp) # Add a partial index on name and timestamp where status = 1. Article.add_index(Article.name, Article.timestamp, where=(Article.status == 1)) # Create a unique index on timestamp desc, status & 4. idx = Article.index( Article.timestamp.desc(), Article.flags.bin_and(4), unique=True) Article.add_index(idx)
事务autocommit模式(不推荐)
Employee.select(fn.Min(Employee.salary), fn.Max(Employee.salary) ).scalar(as_tuple=True) # You can retrieve multiple scalar values by passing as_tuple=True # scalar 表示结果的第一行第一列
注:原生sql,推荐 https://pypi.python.org/pypi/records/