Python ORM框架Pony —— Pony入门

安装

要安装Pony我们首先要安装好pip,假设我们已经安装完成pip,请在dos命令窗口下键入以下命令:

pip install pony

之后pip会自动将我们的Pony安装到Python 2.7或Python 3上

要确保已成功安装Pony,请以交互方式启动Python编译器并键入:

>>> from pony.orm import *

这将导入使用Pony所需的整个(而不是非常大)的类和函数集。最终您可以选择要导入的内容,但我们建议您首先使用import *。

如果您不想将所有内容导入全局命名空间,则可以只导入orm包:

>>> from pony import orm

在这种情况下,您不会将所有Pony的函数加载到全局命名空间中,但它会要求您使用orm作为任何Pony的函数和装饰器的前缀。

创建数据库对象

Pony中的实体连接到数据库。这就是我们需要首先创建数据库对象的原因。在Python解释器中,键入:

>>> db = Database()

定义实体

现在,让我们创建两个实体 -  Person和Car。实体Person有两个属性 - 名称和年龄,Car有属性make和model。在Python解释器中,键入以下代码:

>>> class Person(db.Entity):
...     name = Required(str)
...     age = Required(int)
...     cars = Set('Car')
...
>>> class Car(db.Entity):
...     make = Required(str)
...     model = Required(str)
...     owner = Required(Person)
...
>>>

凡继承Database.Entity的类都是实体类,这意味着这两个不是普通的类,而是实体类。实体类实例存储在数据库中,该数据库绑定到db变量。

在实体Person中,我们创建了三个属性 - 名称,年龄和汽车。名称和年龄是必需属性。换句话说,他们这些属性不能具有None值。名称是字符串属性,而age是数字。

cars属性声明为Set并具有Car类型。也就是声明了Car类型的Set集合属性,这说明了一种关系可以是多对一,或多对多,因为在这个集合中可以存入多个Car类型的对象,并通过这个集合与其他对象进行关联。

这里得注意一点,在声明Person类中的cars时首先其类型为字符串类型,因为在声明Person时Car类还未被定义

Car实体有三个必需属性:make和model是字符串,owner属性是一对多关系的另一边。Pony中的关系总是由两个属性定义,这两个属性代表关系的两个方面。如果我们需要在两个实体之间创建多对多关系,我们应该在两端声明两个Set属性。Pony自动创建中间数据库表。

str类型用于表示Python 3中的unicode字符串.Python 2有两种类型的字符串 -  str和unicode。从Pony Release 0.6开始,您可以使用str或unicode作为字符串属性,它们都表示unicode字符串。我们建议对字符串属性使用str类型,因为它在Python 3中看起来更自然。

如果需要在交互模式下检查实体定义,可以使用show()函数。将实体类或实体实例传递给此函数以打印出定义:

>>> show(Person)
class Person(Entity):
    id = PrimaryKey(int, auto=True)
    name = Required(str)
    age = Required(int)
    cars = Set(Car)

您可能会注意到该实体获得了一个名为id的额外属性。为什么会这样?

每个实体必须包含一个主键,允许区分一个实体与另一个实体。由于我们尚未手动设置主键属性,因此它是自动创建的。如果主键是自动创建的,则将其命名为id并且是intl类型和自增,当然你也可以手动将auto设置为false.如果手动创建主键属性,则可以指定所选的名称和类型。Pony还支持复合主键。

如果您正在使用其他数据库,则需要安装特定的数据库适配器。对于PostgreSQL,Pony使用psycopg2。对于MySQL MySQLdb或pymysql适配器。对于Oracle Pony,使用cx_Oracle适配器。

# SQLite
db.bind(provider='sqlite', filename=':memory:')
# or
db.bind(provider='sqlite', filename='database.sqlite', create_db=True)

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

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

# Oracle
db.bind(provider='oracle', user='', password='', dsn='')

 

将实体映射到数据库表

现在我们需要创建数据库表来保存数据。为此,我们需要在Database对象上调用generate_mapping()方法:

>>> db.generate_mapping(create_tables=True)

参数create_tables = True表示如果实体指向的表尚不存在,则使用CREATE TABLE命令创建它们。必须在调用generate_mapping()方法之前定义连接到数据库的所有实体。

使用调试模式

使用set_sql_debug()函数,您可以看到Pony发送到数据库的SQL命令。要打开调试模式,请键入以下内容:

>>> set_sql_debug(True)

如果在调用generate_mapping()方法之前执行此命令,则在创建表期间,您将看到用于生成它们的SQL代码。

创建实体实例

现在,让我们创建五个描述三个人和两个汽车的对象,并将这些信息保存在数据库中:

>>> p1 = Person(name='John', age=20)
>>> p2 = Person(name='Mary', age=22)
>>> p3 = Person(name='Bob', age=30)
>>> c1 = Car(make='Toyota', model='Prius', owner=p2)
>>> c2 = Car(make='Ford', model='Explorer', owner=p3)
>>> commit()

Pony不会立即在数据库中保存对象。只有在调用commit()函数后才会保存这些对象。如果打开调试模式,那么在commit()期间,您将看到发送到数据库的五个insert命令。

 db_session()

与数据库交互的代码必须放在数据库会话中。当您使用Python的交互式shell时,您不必担心数据库会话,因为它是由Pony自动维护的。但是当您在应用程序中使用Pony时所有数据库交互都应该在数据库会话中完成。为此,您需要使用db_session()装饰器包装使用数据库的函数:

@db_session
def print_person_name(person_id):
    p = Person[person_id]
    print p.name
    # database session cache will be cleared automatically
    # database connection will be returned to the pool

@db_session
def add_car(person_id, make, model):
    Car(make=make, model=model, owner=Person[person_id])
    # commit() will be done automatically
    # database session cache will be cleared automatically
    # database connection will be returned to the pool

 db_session()装饰器对退出函数执行以下操作:

  • 如果函数引发异常,则执行事务回滚
  • 如果数据已更改且未发生异常,则提交事务
  • 返回连接池的数据库连接
  • 清除数据库会话缓存

即使函数只是读取数据并且没有进行任何更改,它也应该使用db_session()来返回到连接池的连接。

实体实例仅在db_session()中有效。如果需要使用这些对象呈现HTML模板,则应在db_session()中执行此操作。

使用数据库的另一个选择是使用db_session()作为context manager 而不是装饰器:

with db_session:
    p = Person(name='Kate', age=33)
    Car(make='Audi', model='R8', owner=p)
    # commit()将自动完成
    # 数据库会话缓存将自动清除
    # 数据库连接将返回池中

编写查询

现在我们已经在其中保存了五个对象的数据库,我们可以尝试一些查询。例如,这是一个返回超过二十岁的人员列表的查询:

>>> select(p for p in Person if p.age > 20)
<pony.orm.core.Query at 0x105e74d10>

select()函数将Python生成器转换为SQL查询并返回Query类的实例。一旦我们开始迭代查询,这个SQL查询将被发送到数据库。获取对象列表的方法之一是将切片运算符[:]应用于它:

>>> select(p for p in Person if p.age > 20)[:]

SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
WHERE "p"."age" > 20

[Person[2], Person[3]]

结果,您可以看到发送到数据库的SQL查询的文本以及提取的对象列表。当我们打印出查询结果时,实体实例由实体名称及其主键写在方括号中表示,例如Person[2].。

要对结果列表进行排序,可以使用Query.order_by()方法。如果只需要结果集的一部分,则可以使用切片运算符,与在Python列表上执行的操作完全相同。例如,如果要按名称对所有人进行排序并提取前两个对象,则可以这样做:

>>> select(p for p in Person).order_by(Person.name)[:2]

SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
ORDER BY "p"."name"
LIMIT 2

[Person[3], Person[1]]

Sometimes, when working in the interactive mode, you might want to see the values of all object attributes. For this purpose, you can use the Query.show() method:

>>> select(p for p in Person).order_by(Person.name)[:2].show()

SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
ORDER BY "p"."name"
LIMIT 2

id|name|age
--+----+---
3 |Bob |30
1 |John|20

Query.show()方法不显示“to-many”属性,因为它需要对数据库进行额外查询,并且可能很庞大。这就是为什么你看不到上面相关汽车的信息。但是如果实例具有“一对一”关系,那么它将显示:

>>> Car.select().show()
id|make  |model   |owner
--+------+--------+---------
1 |Toyota|Prius   |Person[2]
2 |Ford  |Explorer|Person[3]

如果您不想获取对象列表,但需要迭代生成的序列,则可以在不使用切片运算符的情况下使用for循环:

>>> persons = select(p for p in Person if 'o' in p.name)
>>> for p in persons:
...     print p.name, p.age
...
SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
WHERE "p"."name" LIKE '%o%'

John 20
Bob 30

在上面的示例中,我们获取名称属性包含字母“o”的所有Person对象,并显示该人的姓名和年龄。

查询不一定必须返回实体对象。例如,您可以获取一个列表,其中包含object属性:

>>> select((p, count(p.cars)) for p in Person)[:]

SELECT "p"."id", COUNT(DISTINCT "car-1"."id")
FROM "Person" "p"
  LEFT JOIN "Car" "car-1"
    ON "p"."id" = "car-1"."owner"
GROUP BY "p"."id"

[(Person[1], 0), (Person[2], 1), (Person[3], 1)]

获取对象

要通过主键获取对象,您需要在方括号中指定主键值:

>>> p1 = Person[1]
>>> print p1.name
John

您可能会注意到没有向数据库发送任何查询。发生这种情况是因为此对象已存在于数据库会话高速缓存中。缓存减少了需要发送到数据库的请求数。

要通过其他属性检索对象,可以使用Entity.get()方法:

>>> mary = Person.get(name='Mary')

SELECT "id", "name", "age"
FROM "Person"
WHERE "name" = ?
[u'Mary']

>>> print mary.age
22

在这种情况下,即使对象已经加载到缓存中,仍然必须将查询发送到数据库,因为name属性不是唯一键。仅当我们通过主键或唯一键查找对象时,才会使用数据库会话高速缓存。

您可以将实体实例传递给show()函数,以显示实体类和属性值:

>>> show(mary)
instance of Person
id|name|age
--+----+---
2 |Mary|22

修改对象

>>> mary.age += 1
>>> commit()

Pony会跟踪所有已更改的属性。执行commit()函数时,在当前事务期间更新的所有对象都将保存在数据库中。Pony仅保存在数据库会话期间更改的那些属性。

编写原始SQL查询

如果需要通过原始SQL查询选择实体,可以这样做:

>>> x = 25
>>> Person.select_by_sql('SELECT * FROM Person p WHERE p.age < $x')

SELECT * FROM Person p WHERE p.age < ?
[25]

[Person[1], Person[2]]

如果要直接使用数据库,避免使用实体,可以使用Database.select()方法:

>>> x = 20
>>> db.select('name FROM Person WHERE age > $x')
SELECT name FROM Person WHERE age > ?
[20]

[u'Mary', u'Bob']

猜你喜欢

转载自blog.csdn.net/qq_40929531/article/details/84969837
今日推荐