详解python的ORM中Pony用法


Posted in Python onFebruary 09, 2018

Pony是Python的一种ORM,它允许使用生成器表达式来构造查询,通过将生成器表达式的抽象语法树解析成SQL语句。它也有在线ER图编辑器可以帮助你创建Model。

示例分析

Pony语句:

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

Pony语句:

select(c for c in Customer
     if sum(c.orders.price) > 1000)

翻译成sql语句就是:

SELECT "c"."id"
FROM "Customer" "c"
 LEFT JOIN "Order" "order-1"
  ON "c"."id" = "order-1"."customer"
GROUP BY "c"."id"
HAVING coalesce(SUM("order-1"."total_price"), 0) > 1000

安装Pony

pip install pony

使用Pony

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import datetime
import pony.orm as pny
import sqlite3

# conn = sqlite3.connect('D:\日常python学习PY2\Pony学习\music.sqlite')
# print conn

# database = pny.Database()
# database.bind("sqlite","music.sqlite",create_db=True)

# 路径建议写绝对路径。我这边开始写相对路径报错 unable to open database file
database = pny.Database("sqlite","D:\日常python学习PY2\Pony学习\music.sqlite",create_db=True)

########################################################################
class Artist(database.Entity):
  """
  Pony ORM model of the Artist table
  """
  name = pny.Required(unicode)
  #被外键关联
  albums = pny.Set("Album")

########################################################################
class Album(database.Entity):
  """
  Pony ORM model of album table
  """
  #外键字段artlist,外键关联表Artist,Artist表必须写Set表示被外键关联
  #这个外键字段默认就是index=True,除非自己指定index=False才不会创建索引,索引名默认为[idx_表名__字段](artist)
  artist = pny.Required(Artist)
  title = pny.Required(unicode)
  release_date = pny.Required(datetime.date)
  publisher = pny.Required(unicode)
  media_type = pny.Required(unicode)

# turn on debug mode
pny.sql_debug(True)   # 显示debug信息(sql语句)

# map the models to the database
# and create the tables, if they don't exist
database.generate_mapping(create_tables=True)    # 如果数据库表没有创建表

运行之后生成sqlite如下:

上述代码对应的sqlite语句是:

GET CONNECTION FROM THE LOCAL POOL
PRAGMA foreign_keys = false
BEGIN IMMEDIATE TRANSACTION
CREATE TABLE "Artist" (
 "id" INTEGER PRIMARY KEY AUTOINCREMENT,
 "name" TEXT NOT NULL
)
 
CREATE TABLE "Album" (
 "id" INTEGER PRIMARY KEY AUTOINCREMENT,
 "artist" INTEGER NOT NULL REFERENCES "Artist" ("id"),
 "title" TEXT NOT NULL,
 "release_date" DATE NOT NULL,
 "publisher" TEXT NOT NULL,
 "media_type" TEXT NOT NULL
)
 
CREATE INDEX "idx_album__artist" ON "Album" ("artist")
 
SELECT "Album"."id", "Album"."artist", "Album"."title", "Album"."release_date", "Album"."publisher", "Album"."media_type"
FROM "Album" "Album"
WHERE 0 = 1
 
SELECT "Artist"."id", "Artist"."name"
FROM "Artist" "Artist"
WHERE 0 = 1
 
COMMIT
PRAGMA foreign_keys = true
CLOSE CONNECTION

插入/增加数据

源码地址:https://github.com/flowpig/daily_demos

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import datetime
import pony.orm as pny
from models import Album, Artist
from database import PonyDatabase


# ----------------------------------------------------------------------
@pny.db_session
def add_data():
  """"""

  new_artist = Artist(name=u"Newsboys")
  bands = [u"MXPX", u"Kutless", u"Thousand Foot Krutch"]
  for band in bands:
    artist = Artist(name=band)

  album = Album(artist=new_artist,
         title=u"Read All About It",
         release_date=datetime.date(1988, 12, 01),
         publisher=u"Refuge",
         media_type=u"CD")

  albums = [{"artist": new_artist,
        "title": "Hell is for Wimps",
        "release_date": datetime.date(1990, 07, 31),
        "publisher": "Sparrow",
        "media_type": "CD"
        },
       {"artist": new_artist,
        "title": "Love Liberty Disco",
        "release_date": datetime.date(1999, 11, 16),
        "publisher": "Sparrow",
        "media_type": "CD"
        },
       {"artist": new_artist,
        "title": "Thrive",
        "release_date": datetime.date(2002, 03, 26),
        "publisher": "Sparrow",
        "media_type": "CD"}
       ]

  for album in albums:
    a = Album(**album)


if __name__ == "__main__":
  db = PonyDatabase()
  db.bind("sqlite", "D:\日常python学习PY2\Pony学习\music.sqlite", create_db=True)
  db.generate_mapping(create_tables=True)


  add_data()

  # use db_session as a context manager
  with pny.db_session:
    a = Artist(name="Skillet")


'''
您会注意到我们需要使用一个装饰器db_session来处理数据库。 
它负责打开连接,提交数据并关闭连接。 你也可以把它作为一个上
下文管理器,with pny.db_session
'''

更新数据

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import pony.orm as pny

from models import Artist, Album
from database import PonyDatabase

db = PonyDatabase()
db.bind("sqlite", "D:\日常python学习PY2\Pony学习\music.sqlite", create_db=True)
db.generate_mapping(create_tables=True)


with pny.db_session:
  band = Artist.get(name="Newsboys")
  print band.name

  for record in band.albums:
    print record.title

  # update a record
  band_name = Artist.get(name="Kutless")
  band_name.name = "Beach Boys"
  
  #使用生成器形式查询
  '''
  result = pny.select(i.name for i in Artist)
  result.show()
  
  结果:
  i.name       
  --------------------
  Newsboys      
  MXPX        
  Beach Boys     
  Thousand Foot Krutch
  Skillet       

  '''

删除记录

import pony.orm as pny
from models import Artist
with pny.db_session:
  band = Artist.get(name="MXPX")
  band.delete()

Pony补充

可以连接的数据库:

##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) # 如果数据库表没有创建表

属性类型

  • Required
  • Optional
  • PrimaryKey
  • Set

Required and Optional

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

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 User(db.Entity):
  name = Required(str)
  cart = Optional("Cart") #必须Optional-Required or Optional-Optional

class Cart(db.Entity):
  user = Required("User")
  
# 多对多
class Student(db.Entity):
  name = pny.Required(str)
  courses = pny.Set("Course")

class Course(db.Entity):
  name = pny.Required(str)
  semester = pny.Required(int)
  students = pny.Set(Student)
  pny.PrimaryKey(name, semester)   #联合主键

pny.sql_debug(True)   # 显示debug信息(sql语句)
db.generate_mapping(create_tables=True)   # 如果数据库表没有创建表
#-------------------------------------------------------
#一对多
class Artist(database.Entity):
  """
  Pony ORM model of the Artist table
  """
  name = pny.Required(unicode)
  #被外键关联
  albums = pny.Set("Album")

class Album(database.Entity):
  """
  Pony ORM model of album table
  """
  #外键字段artlist,外键关联表Artist,Artist表必须写Set表示被外键关联
  #这个外键字段默认就是index=True,除非自己指定index=False才不会创建索引,索引名默认为[idx_表名__字段](artist)
  artist = pny.Required(Artist)    #外键字段(数据库显示artist)
  title = pny.Required(unicode)
  release_date = pny.Required(datetime.date)
  publisher = pny.Required(unicode)
  media_type = pny.Required(unicode)

# Compositeindexes(复合索引)
class Example1(db.Entity):
  a = Required(str)
  b = Optional(int)
  composite_index(a, b)
  #也可以使用字符串composite_index(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

#字符串长度,不写默认为255
name = Required(str,40)   #VARCHAR(40)

#整数的大小,默认2bit
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

# 小数和精度
price = Required(Decimal, 10, 2)    #DECIMAL(10,2)

# 时间
dt = Required(datetime,6)

# 其它参数
unique  是否唯一
auto  是否自增
default   默认值
sql_default 
created_at = Required(datetime, sql_default='CURRENT_TIMESTAMP')
index  创建索引
index='index_name' 指定索引名称
lazy  延迟加载的属性加载对象
cascade_delete   关联删除对象
column   映射到数据库的列名
columns Set(多对多列名)
table  多对多中间表的表名字
nullable  允许该列为空
py_check  可以指定一个函数,检查数据是否合法和修改数据

class Student(db.Entity): 
  name = Required(str) 
  gpa = Required(float, py_check=lambda val: val >= 0 and val <= 5)

实例操作

# 获取实例

p = Person.get(name="Person")  #返回单个实例,如同
Django ORM的get
#------------------------------
# 查询
persons = Person.select()
'''
select并没有连接数据库查询,只是返回一个Query object,调用persons[:]返回所有Person实例
'''

# limit
persons [1:5]

# show
persons.show()

# 生成器表达式查询,然后解析AST树的方式构造SQL语句

select(p for p in Person) 
#和Person.select()一样返回Query object

select((p.id, p.name) for p in Person)[:]

# 带where条件查询
select((p.id, p.name) for p in Person if 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.page = 1000
 commit()
 
# 删除
@db_session
def delete_persons():
  p = Person.get(id=2)
  p.delete()
  commit()

pony使用还可以使用游标操作(这样就可以写原生sql语句了)

result = db.execute('''select name from Artist''')
print result.fetchall()

类似Django ORM的save函数

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.

例如:

class Message(db.Entity):
  title = Required(str)
  content = Required(str)
  def before_insert(self):
    print("Before insert! title=%s" % self.title)

 

Python 相关文章推荐
python实现划词翻译
Apr 23 Python
Python多线程学习资料
Dec 19 Python
Python程序设计入门(5)类的使用简介
Jun 16 Python
Python实现新浪博客备份的方法
Apr 27 Python
python绘制条形图方法代码详解
Dec 19 Python
在CMD命令行中运行python脚本的方法
May 12 Python
终端命令查看TensorFlow版本号及路径的方法
Jun 13 Python
详解Python读取yaml文件多层菜单
Mar 23 Python
Python chardet库识别编码原理解析
Feb 18 Python
python GUI库图形界面开发之pyinstaller打包python程序为exe安装文件
Feb 26 Python
Python应用实现处理excel数据过程解析
Jun 19 Python
python和C/C++混合编程之使用ctypes调用 C/C++的dll
Apr 29 Python
python监控键盘输入实例代码
Feb 09 #Python
Python with语句上下文管理器两种实现方法分析
Feb 09 #Python
Python遍历pandas数据方法总结
Feb 09 #Python
python中的闭包函数
Feb 09 #Python
基于Python socket的端口扫描程序实例代码
Feb 09 #Python
利用python 更新ssh 远程代码 操作远程服务器的实现代码
Feb 08 #Python
Tensorflow 利用tf.contrib.learn建立输入函数的方法
Feb 08 #Python
You might like
dede全站URL静态化改造[070414更正]
2007/04/17 PHP
解析smarty 截取字符串函数 truncate的用法介绍
2013/06/20 PHP
php查看一个变量的占用内存的实例代码
2020/03/29 PHP
js jquery做的图片连续滚动代码
2008/01/06 Javascript
简单通用的JS滑动门代码
2008/12/19 Javascript
jqplot通过ajax动态画折线图的方法及思路
2013/12/08 Javascript
jquery对元素拖动排序示例
2014/01/16 Javascript
js判断iframe内的网页是否滚动到底部触发事件
2014/03/18 Javascript
jQuery设置和获取HTML、文本和值示例
2014/07/08 Javascript
下拉框select的绑定示例
2014/09/04 Javascript
jQuery设置Cookie及删除Cookie实例分析
2016/04/15 Javascript
jQuery获取元素父节点的方法
2016/06/21 Javascript
Vue.js每天必学之过渡与动画
2016/09/06 Javascript
JS实现的自动打字效果示例
2017/03/10 Javascript
微信小程序 navbar实例详解
2017/05/11 Javascript
把JavaScript代码改成ES6语法不完全指南(分享)
2017/09/10 Javascript
学习JS中的DOM节点以及操作
2018/04/30 Javascript
微信小程序tabbar底部导航
2018/11/05 Javascript
js Math数学简单使用操作示例
2020/03/13 Javascript
如何在postman测试用例中实现断言过程解析
2020/07/09 Javascript
[02:51]DOTA2英雄基础教程 风暴之灵
2013/12/23 DOTA
[02:05]DOTA2完美大师赛趣味视频之看我表演
2017/11/18 DOTA
[52:37]完美世界DOTA2联赛循环赛 Forest vs DM BO2第一场 10.29
2020/10/29 DOTA
python使用urllib2模块获取gravatar头像实例
2013/12/18 Python
Python入门篇之面向对象
2014/10/20 Python
Python连接DB2数据库
2016/08/27 Python
python实现的二叉树定义与遍历算法实例
2017/06/30 Python
使用WingPro 7 设置Python路径的方法
2019/07/24 Python
解决jupyter notebook import error但是命令提示符import正常的问题
2020/04/15 Python
python3爬虫中多线程的优势总结
2020/11/24 Python
秘鲁购物网站:Linio秘鲁
2017/04/07 全球购物
联想法国官方网站:Lenovo法国
2018/10/18 全球购物
一年级家长会邀请函
2014/01/25 职场文书
颁奖典礼主持词
2014/03/25 职场文书
2014年依法行政工作总结
2014/11/19 职场文书
SpringBoot生成License的实现示例
2021/06/16 Java/Android