При использовании Pony ORM или sqlalchemy, где создать объект базы данных? - PullRequest
0 голосов
/ 12 декабря 2018

Я начал немного играть с Object Relational Mappers ( Pony ORM , в частности).

В Pony , все определения сущностей наследуются от db.Entity класс.Однако для этого, конечно, сначала необходимо создать объект db.(db.Entity чем-то похож на декларативную базу в sqlalchemy , поэтому я считаю, что мой вопрос ниже также похож на действительный для sqlalchemy )

Все примеры, которые у меня естьв документах Pony ORM представлены примеры, встроенные, где объект Database db просто объявляется в приглашении интерпретатора до объявления сущностей.

Это оставляет для меня вопрос: Где я долженсоздать мой объект БД в «реальном» проекте?

Особенно рассмотрим случай, когда я хочу отделить определения своих сущностей от тех мест, где я на самом деле использую эти сущности (скажем, я хочу создать хорошийПакет оболочки ORM для доступа к базе данных, которая затем должна использоваться в нескольких других проектах).Тогда я, вероятно, хочу, чтобы пользователи предоставили собственный объект db, настроенный в соответствии с их потребностями для доступа к БД.

Пример кода:

Скажем, у меня есть база данных, в которой хранятся лиц и адреса и мой пакет my_orm должен предоставить ORM для базы данных, которая затем будет использоваться в app.py:

my_orm / init .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)

app.py

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

Помимо того, что выглядело уже некрасиво, потому что это смешивает импорт с созданием БД, это такжекиньте ошибку NameError: name 'db' is not defined.Так что мне делать вместо этого?

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Вы можете использовать метаклассы для определения сущностей.

файл 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()
0 голосов
/ 13 декабря 2018

Существует несколько способов организации кода.

1.Поместите все объекты в один файл

Это удобный способ для простых и средних проектов.Это самый простой и, вероятно, вы можете начать этот путь.Вы можете определить Database объект в этом файле непосредственно перед определениями сущностей:

models.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')

main.py

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 в одной и той же программе

models.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')

main.py

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.Недостатком является то, что IDE, такие как PyCharm, не понимают, что такое db.Person, и не дают предложений для завершения кода таких атрибутов, как Person.name.

Также возможно разделить определения полномочий между несколькими функциями, которыеимпортируются из разных файлов:

models1.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)

models2.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)

main.py

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 - несколько файлов моделей

...