Pony ORM

传统的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()方法。这样你就可以使用不同的数据库进行测试和制作。

可以连接的数据库:

postgres

db.bind('postgres', user='', password='', host='', database='')

sqlite create_db:如果数据库不存在创建数据库文件

db.bind('sqlite', 'filename', create_db=True)

mysql

db.bind('mysql', host='', user='', passwd='', db='')

Oracle

db.bind('oracle', 'user/password@dsn')

Entity(实体) 类似mvc里面的model

在创建实体实例之前,需要将实体映射到数据库表, 生成映射后,可以通过实体查询数据库并创建新的实例。

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

属性类型分

  1. Required
  2. Optional
  3. PrimaryKey
  4. Set
Required and Optional

通常实体属性分为Required(必须)和Optional(可选)

name = Required(str, unique=True)
picture = Optional(buffer)
PrimaryKey(主键)

默认每个实体都有一个主键,默认为添加了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)
Set

定义了一对一,一对多,多对多等数据结构

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)
Composite keys(复合主键)
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`)
)
CONSTRAINT 外键约束
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`)
)
Composite indexes(复合索引)
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`)

属性数据类型

格式为
属性名 = 属性类型(数据类型)

  • str
  • unicode
  • int
  • float
  • Decimal
  • datetime
  • date
  • time
  • timedelta
  • bool
  • buffer - used for binary data in Python 2 and 3
  • bytes - used for binary data in Python 3
  • LongStr - used for large strings
  • LongUnicode - used for large strings
  • UUID
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.

实例(inheritance)

每个实例对应于数据库表中的一行
比如有一个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, before_update, before_delete hooks

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.

你可能感兴趣的:(ponyorm,python,orm)