1

我开始玩一些对象关系映射器(特别是Pony ORM)。

Pony中,所有实体定义都继承自db.Entity该类。然而,为了做到这一点,当然db需要先在某个地方创建对象。(db.Entity有点类似于sqlalchemy中的声明性基础,所以我相信我下面的问题对sqlalchemy也同样有效)

我在 Pony ORM 文档中看到的所有示例都提供了内联示例,其中在db声明实体之前在解释器提示中简单地声明了 Database 对象。

这给我留下了一个问题:我应该在“真实”项目中在哪里创建我的 db 对象?

特别考虑我想将我的实体定义与我实际使用这些实体的位置分开的情况(比如我只想构建一个好的 ORM 包装器包来访问数据库,然后应该在多个不同的其他项目中使用它)。然后我可能希望用户提供根据db他们的需要配置的自己的对象,以便访问数据库。

一些示例代码:

假设我有一个存储人员地址的数据库,我的包my_orm应该为数据库提供一个 ORM,然后将用于app.py

我的_orm/初始化.py

from my_orm.person import Person
from my_orm.address import Address

my_orm/person.py:

from pony.orm import Required

class Person(db.Entity): # Where should `db` be defined?
    name = Required(str)
    age = Required(int)

my_orm/address.py:

from pony.orm import Required

class Address(db.Entity): # Where should `db` be defined?. Must be the same `db` object as in person.py
    street_name = Required(str)
    zip_code = Required(int)

应用程序.py

from pony.orm import Database
db = Database()
import my_orm

除了已经看起来很丑,因为它将导入与数据库的创建混合在一起,这也会抛出错误NameError: name 'db' is not defined。那么我应该怎么做呢?

4

2 回答 2

3

有几种组织代码的方法。

1. 将所有实体放在一个文件中

这是简单和中型项目的便捷方式。这是最简单的,可能你可以这样开始。您可以Database在实体定义之前在此文件中定义对象:

模型.py

from pony.orm import Database, Required, Optional

db = orm.Database()

class Person(db.Entity):
    name = Required(str)
    addresses = Set('Address') # or Set(lambda: Address)

class Address(db.Entity):
    street_name = Required(str)
    persons = Set('Person')

主文件

from models import db, Person, Address
from settings import db_params
from pony.orm import db_session, select

db.bind(**db_params)
db.generate_mapping(create_tables=True)

with db_session:
    persons = select(p for p in Person if p.age > 20)[:]

这种方式简单,适合中型项目,可以入手

2.在函数内部定义实体

Database如果您想连接到同一程序中的多个不同实例,这可能很有用

模型.py

from pony.orm import Required, Optional

def define_entities(db):
    class Person(db.Entity):
        name = Required(str)
        addresses = Set('Address')

    class Address(db.Entity):
        street_name = Required(str)
        persons = Set('Person')

主文件

from models import define_entities
from settings import db_params
from pony.orm import Database, db_session, select

db = Database()
define_entities(db)
db.bind(**db_params)
db.generate_mapping(create_tables=True)

with db_session:
    persons = select(p for p in db.Person if p.age > 20)[:]

请注意,可以将实体类作为数据库对象的属性进行访问:db.Person. 这可能很方便,因为不需要导入Person实体——访问对象就足够了db。缺点是像 PyCharm 这样的 IDE 不理解什么db.Person是属性,也不为Person.name.

也可以在从不同文件导入的几个函数之间拆分实体定义:

模型1.py

from pony.orm import Required, Optional

def define_entities(db):
    class Person(db.Entity):
        name = Required(str)
        addresses = Set('Address') # or: Set(lambda: db.Address)

模型2.py

from pony.orm import Required, Optional

def define_entities(db):
    class Address(db.Entity):
        street_name = Required(str)
        persons = Set('Person')  # or: Set(lambda: db.Person)

主文件

import models1, models2
from settings import db_params
from pony.orm import Database, db_session, select

db = Database()
models1.define_entities(db)
models2.define_entities(db)
db.bind(**db_params)
db.generate_mapping(create_tables=True)

with db_session:
    persons = select(p for p in db.Person if p.age > 20)[:]

这可能有点矫枉过正,但有时可以用于可插拔架构,即在应用程序启动后动态定义精确的实体集。

3.在单独的文件中定义实体(不在函数内部)

您可以按照我在相应答案中描述的架构: PonyORM - 多个模型文件

于 2018-12-13T15:27:11.647 回答
1

您可以使用元类来定义实体。

文件 1:

class LazyEntityMeta(type):
    def __new__(mcs, name, bases, attrs):
        entity = mcs._entities[name] = LazyEntity(bases, attrs)
        return entity

    @classmethod
    def attach(mcs, db):
        for name, lazy in mcs._entities.items():
            lazy.entity = type(name, lazy.bases + (db.Entity,), attrs)

    _entities = {}

class LazyEntity:
    def __init__(self, bases, attrs):
        self.bases = bases
        self.attrs = attrs

文件 2:

class A(metaclass=LazyEntityMeta):
    id = PrimaryKey(int, auto=True)

文件 3:

db = Database()

LazyEntityMeta.attach(db)
db.bind('sqlite', ':memory:')
db.generate_mapping(create_tables=True)

with db_session:
    a1 = db.A()
    a2 = A.entity()
于 2018-12-14T08:44:06.310 回答