传统的ORM,需要你使用它提供的.query、.filter、.get之类的函数实现查询。而Pony ORM另辟蹊径,直接以generator expression,然后解析AST树的方式构造SQL语句。
举个例子:有一个Person表,需要查询其中age大于20的person。在Pony ORM里只需要
select(p for p in Person if p.age > 20)
翻成sql就是
SELECT `p`.`id`, `p`.`name`, `p`.`age`, `p`.`classtype`, `p`.`mentor`, `p`.`gpa`, `p`.`degree`
FROM `person` `p`
WHERE `p`.`classtype` IN ('Student', 'Professor', 'Person')
AND `p`.`age` > 20
够黑魔法吧?够高大上吧?
db = Database()
下面都使用的这个db对象
db = Database("mysql", host="localhost",
user="root",
passwd="123123",
db="t2")
空的连接往往是更方便做后期使用DB()方法。这样你就可以使用不同的数据库进行测试和制作。
db.bind('postgres', user='', password='', host='', database='')
db.bind('sqlite', 'filename', create_db=True)
db.bind('mysql', host='', user='', passwd='', db='')
db.bind('oracle', 'user/password@dsn')
在创建实体实例之前,需要将实体映射到数据库表, 生成映射后,可以通过实体查询数据库并创建新的实例。
db.Entity 自己定义新的实体必须重db.Entity继承
class Customer(db.Entity):
name = Required(str)
picture = Optional(buffer)
sql_debug(True) # 显示debug信息(sql语句)
db.generate_mapping(create_tables=True) # 如果数据库表没有创建表
输出
GET CONNECTION FROM THE LOCAL POOL
SET foreign_key_checks = 0
CREATE TABLE `customer` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`picture` LONGBLOB
)
SELECT `customer`.`id`, `customer`.`name`, `customer`.`picture`
FROM `customer` `customer`
WHERE 0 = 1
COMMIT
SET foreign_key_checks = 1
CLOSE CONNECTION
通常实体属性分为Required(必须)和Optional(可选)
name = Required(str, unique=True)
picture = Optional(buffer)
默认每个实体都有一个主键,默认为添加了id = PrimaryKey(int, auto=True)属性
class Product(db.Entity):
name = Required(str, unique=True)
price = Required(Decimal)
description = Optional(str)
上面等于下面
class Product(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str, unique=True)
price = Required(Decimal)
description = Optional(str)
定义了一对一,一对多,多对多等数据结构
class Student(db.Entity):
name = Required(str)
courses = Set("Course")
class Course(db.Entity):
name = Required(str)
semester = Required(int)
students = Set(Student)
PrimaryKey(name, semester)
class Example1(db.Entity):
a = Required(int)
b = Required(str)
PrimaryKey(a, b)
建表语句
CREATE TABLE `example1` (
`a` INTEGER NOT NULL,
`b` VARCHAR(255) NOT NULL,
PRIMARY KEY (`a`, `b`)
)
class Example2(db.Entity):
a = Required(str)
b = Optional(int)
composite_key(a, b)
建表语句
CREATE TABLE `example2` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`a` VARCHAR(255) NOT NULL,
`b` INTEGER,
CONSTRAINT `unq_example2__a_b` UNIQUE (`a`, `b`)
)
class Example1(db.Entity):
a = Required(str)
b = Optional(int)
composite_index(a, b)
#也可以使用字符串composite_index(a, 'b')
CREATE TABLE `example1` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`a` VARCHAR(255) NOT NULL,
`b` INTEGER
)
CREATE INDEX `idx_example1__a_b` ON `example1` (`a`, `b`)
格式为
属性名 = 属性类型(数据类型)
attr1 = Required(str)
# 一样
attr2 = Required(unicode)
attr3 = Required(LongStr)
# 一样
attr4 = Required(LongUnicode)
attr1 = Required(buffer) # Python 2 and 3
attr2 = Required(bytes) # Python 3 only
字符串长度
name = Required(str, 40) # VARCHAR(40)
默认为255
整数的大小
attr1 = Required(int, size=8) # 8 bit - TINYINT in MySQL
attr2 = Required(int, size=16) # 16 bit - SMALLINT in MySQL
attr3 = Required(int, size=24) # 24 bit - MEDIUMINT in MySQL
attr4 = Required(int, size=32) # 32 bit - INTEGER in MySQL
attr5 = Required(int, size=64) # 64 bit - BIGINT in MySQL
无符号
attr1 = Required(int, size=8, unsigned=True) # TINYINT UNSIGNED in MySQL
默认32bit INTEGER
小数和精度
price = Required(Decimal, 10, 2) # DECIMAL(10, 2)
时间
dt = Required(datetime, 6)
其他的参数
unique
Boolean 是否唯一
auto
Boolean 是否自增
default
默认值
sql_default
created_at = Required(datetime, sql_default=’CURRENT_TIMESTAMP’)
index
index=True创建的默认索引名称
index=’index_name’指定索引名称.
sql_type
lazy
Boolean 延迟加载的属性加载对象
cascade_delete
关联删除对象
column
映射到数据库的列名
columns
Set(多对多列名)
reverse
Specifies the attribute at the other end which should be used for the relationship.
reverse_column
Used for a symmetric relationship in order to specify the name of the database column for the intermediate table.
reverse_columns
Used for a symmetric relationship if the entity has a composite primary key. Allows you to specify the name of the database columns for the intermediate table.
table
多对多中间表的表名
nullable
允许该列为数据库中的空
volatile
Usually you specify the value of the attribute in Python and Pony stores this value in the database. But sometimes you might want to have some logic in the database which changes the value for a column. For example, you can have a trigger in the database which updates the timestamp of the last object’s modification. In this case you want to have Pony to forget the value of the attribute on object’s update sent to the database and read it from the database at the next access attempt. Set volatile=True in order to let Pony know that this attribute can be changed in the database.
The volatile=True option can be combined with the sql_default=True option if the value for this attribute is going to be both created and updated by the database.
You can get the exception UnrepeatableReadError: Value … was updated outside of current transaction if another transaction changes the value of the attribute which is used in the current transaction. Pony notifies about it because this situation can break the business logic of the application. If you don’t want Pony to protect you from such concurrent modifications you can set volatile=True for an attribute.
sequence_name
Allows you to specify the sequence name used for PrimaryKey attributes for Oracle database.
py_check
可以指定一个函数,检查数据是否合法和修改数据
class Student(db.Entity):
name = Required(str)
gpa = Required(float, py_check=lambda val: val >= 0 and val <= 5)
min
Allows you to specify the minimum allowed value for numeric attributes (int, float, Decimal). If you will try to assign the value that is less than the specified min value, you’ll get the ValueError exception.
max
Allows you to specify the maximum allowed value for numeric attributes (int, float, Decimal). If you will try to assign the value that is greater than the specified max value, you’ll get the ValueError exception.
每个实例对应于数据库表中的一行
比如有一个Person表记录Person的姓名(name)和年龄(age)
class Person(db.Entity):
name = Required(str)
age = Required(int)
sql_debug(True)#显示debug信息(sql语句)在这句代码后的显示debug
db.generate_mapping(create_tables=True)#如果数据库表没有创建表
输出
GET CONNECTION FROM THE LOCAL POOL
SET foreign_key_checks = 0
CREATE TABLE `person` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`age` INTEGER NOT NULL
)
SELECT `person`.`id`, `person`.`name`, `person`.`age`
FROM `person` `person`
WHERE 0 = 1
COMMIT
SET foreign_key_checks = 1
CLOSE CONNECTION
Pony规定与数据库进行交互的代码必须在数据库会话中工作
可以使用@ db_session装修或db_session上下文管理数据库的工作。
当会话结束时,它会做以下操作
使用修饰器
@db_session
def check_user(username):
return User.exists(username=username)
db_session上下文管理器
def process_request():
...
with db_session:
u = User.get(username=username)
...
# -*- coding: utf-8 -*-
from pony.orm import *
db = Database("mysql", host="localhost",
user="root",
passwd="123123",
db="t2")
class Person(db.Entity):
name = Required(str)
age = Required(int)
sql_debug(True)#显示debug信息(sql语句)
db.generate_mapping(create_tables=True)#如果数据库表没有创建表
@db_session
def create_persons():
p1 = Person(name="Person1", age=20)
p2 = Person(name="Person2", age=22)
p3 = Person(name="Person3", age=12)
print p1.id #这里拿不到id,没提交
commit()
print p1.id #这里拿不到已经有id
create_persons()
输出
GET CONNECTION FROM THE LOCAL POOL
SET foreign_key_checks = 0
SELECT `person`.`id`, `person`.`name`, `person`.`age`
FROM `person` `person`
WHERE 0 = 1
COMMIT
SET foreign_key_checks = 1
CLOSE CONNECTION
None
GET NEW CONNECTION
INSERT INTO `person` (`name`, `age`) VALUES (%s, %s)
[u'Person1', 20]
INSERT INTO `person` (`name`, `age`) VALUES (%s, %s)
[u'Person2', 22]
INSERT INTO `person` (`name`, `age`) VALUES (%s, %s)
[u'Person3', 12]
COMMIT
1
RELEASE CONNECTION
数据已经插入
外键必须设置2边的字段,django可以只设置多的一边的关系,
class User(db.Entity):
name = Required(str)
cart = Optional("Cart") #必须Optional-Required or Optional-Optional
class Cart(db.Entity):
user = Required("User")
class Product(db.Entity):
tags = Set("Tag")
class Tag(db.Entity):
products = Set(Product)
学生和教授从Person上继承, 教授有多个学生(一对多的关系)
person保函所有子类的属性,有一个classtype区分是属于哪个对象,默认是class name, 可以添加_discriminator_
属性修改classtype
# -*- coding: utf-8 -*-
from pony.orm import *
from decimal import Decimal
db = Database("mysql", host="localhost",
user="root",
passwd="123123",
db="t2")
db.drop_table("person", with_all_data=True)
class Person(db.Entity):
_discriminator_ = 1 #刻
name = Required(str)
age = Required(int)
class Student(Person):
_discriminator_ = 3
gpa = Optional(Decimal)
mentor = Optional("Professor")
class Professor(Person):
_discriminator_ = 2
degree = Required(str)
students = Set("Student")
sql_debug(True) # 显示debug信息(sql语句)
db.generate_mapping(create_tables=True) # 如果数据库表没有创建表
# @db_session
# def create_persons():
# p1 = Person(name="Person", age=20)
# s = Student(name="Student", age=22, gpa=1.2)
#也可以Professor添加Student
# p2 = Professor(name="Professor", age=12, degree="aaaaaa", students=[s])
# commit()
@db_session
def create_persons():
p1 = Person(name="Person", age=20)
s = Student(name="Student", age=22, gpa=1.2)
p2 = Professor(name="Professor", age=12, degree="aaaaaa", students=[s])
commit()
create_persons()
class Person(db.Entity):
_discriminator_ = 1
name = Required(str)
age = Required(int)
输出
GET NEW CONNECTION
SET foreign_key_checks = 0
CREATE TABLE `person` (
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`age` INTEGER NOT NULL,
`classtype` VARCHAR(255) NOT NULL,
`gpa` DECIMAL(12, 2),
`mentor` INTEGER,
`degree` VARCHAR(255)
)
CREATE INDEX `idx_person__mentor` ON `person` (`mentor`)
SELECT `person`.`id`, `person`.`name`, `person`.`age`, `person`.`classtype`, `person`.`gpa`, `person`.`mentor`, `person`.`degree`
FROM `person` `person`
WHERE 0 = 1
COMMIT
SET foreign_key_checks = 1
CLOSE CONNECTION
GET NEW CONNECTION
INSERT INTO `person` (`name`, `age`, `classtype`) VALUES (%s, %s, %s)
[u'Person', 20, '1']
INSERT INTO `person` (`name`, `age`, `classtype`, `degree`) VALUES (%s, %s, %s, %s)
[u'Professor', 12, '2', u'aaaaaa']
INSERT INTO `person` (`name`, `age`, `classtype`, `gpa`, `mentor`) VALUES (%s, %s, %s, %s, %s)
[u'Student', 22, '3', Decimal('1.2'), 2] #已经把外键添加了
COMMIT
RELEASE CONNECTION
获取单个
如果返回多个实例会报错,只能返回一个实例
p = Person.get(name="Person")
查询
persons = Person.select()
select并没有连接数据库查询,只是返回一个Query object
调用
persons[:] 返回所有Person实例
'''
SELECT `p`.`id`, `p`.`name`, `p`.`age`, `p`.`classtype`, `p`.`mentor`, `p`.`gpa`, `p`.`degree`
FROM `person` `p`
WHERE `p`.`classtype` IN ('3', '2', '1')
[Person[1], Professor[2], Student[3], Person[4]]
'''
limit
persons[1:5]
show()
persons.show()
id|name |age|classtype
--+---------+---+---------
1 |Person |20 |1
2 |Professor|12 |2
3 |Student |22 |3
4 |Person |25 |1
select()
第一次见这查询方法,直接以generator expression,然后解析AST树的方式构造SQL语句
select(p for p in Person) #和Person.select()一样返回Query object
select((p.id, p.name) for p in Person)[:]
SQL
SELECT `p`.`id`, `p`.`name`
FROM `person` `p`
WHERE `p`.`classtype` IN ('3', '2', '1')
带where条件查询
select((p.id, p.name) for p in Person if p.age ==20)[:]
SQL
SELECT `p`.`id`, `p`.`name`
FROM `person` `p`
WHERE `p`.`classtype` IN ('3', '2', '1')
AND `p`.`age` = 20
分组集合查询
select((max(p.age)) for p in Person)[:] #[25]
max(p.age for p in Person) #25
select(p.age for p in Person).max() #25
还有很多具体参考官网
@db_session
def update_persons():
p = Person.get(id=2)
p.age=1000
commit()
update_persons()
@db_session
def delete_persons():
p = Person.get(id=2)
p.delete()
commit()
delete_persons()
before_insert()
Is called only for newly created objects before it is inserted into the database.
before_update()
Is called for entity instances before updating the instance in the database.
before_delete()
Is called before deletion the entity instance in the database.
after_insert()
Is called after the row is inserted into the database.
after_update()
Is called after the instance updated in the database.
after_delete()
Is called after the entity instance is deleted in the database.