gpt4 book ai didi

python - 我如何 'index' SQLAlchemy模型属性是主键和关系

转载 作者:行者123 更新时间:2023-11-30 23:44:33 25 4
gpt4 key购买 nike

假设我有一些类 X、Y 和 Z,它们使用 SQLAlchemy 声明性语法来定义一些简单的列和关系

要求:

  1. 在类级别,(X|Y|Z).primary_keys 返回
    的集合相应类的“主键”(InstrumentedAttribute对象)我还希望 (X|Y|Z).relations 引用该类'同样的关系

  2. 在实例级别,我希望引用相同的属性这些属性的实例化值,无论它们是否已使用我自己的构造函数和各个属性进行填充
    setter ,或者 SQLAlchemy 从以下位置检索行时执行的任何操作数据库。

到目前为止,我有以下内容。

import collections 
import sqlalchemy
import sqlalchemy.ext.declarative
from sqlalchemy import MetaData, Column, Table, ForeignKey, Integer, String, Date, Text
from sqlalchemy.orm import relationship, backref

class IndexedMeta(sqlalchemy.ext.declarative.DeclarativeMeta):
"""Metaclass to initialize some class-level collections on models"""
def __new__(cls, name, bases, defaultdict):
cls.pk_columns = set()
cls.relations = collections.namedtuple('RelationshipItem', 'one many')( set(), set())
return super().__new__(cls, name, bases, defaultdict)

Base = sqlalchemy.ext.declarative.declarative_base(metaclass=IndexedMeta)


def build_class_lens(cls, key, inst):
"""Populates the 'indexes' of primary key and relationship attributes with the attributes' names. Additionally, separates "x to many" relationships from "x to one" relationships and associates "x to one" relathionships with the local-side foreign key column"""
if isinstance(inst.property, sqlalchemy.orm.properties.ColumnProperty):
if inst.property.columns[0].primary_key:
cls.pk_columns.add(inst.key)

elif isinstance(inst.property, sqlalchemy.orm.properties.RelationshipProperty):
if inst.property.direction.name == ('MANYTOONE' or 'ONETOONE'):
local_column = cls.__mapper__.get_property_by_column(inst.property.local_side[0]).key
cls.relations.one.add( (local_column, inst.key) )
else:
cls.relations.many.add(inst.key)


sqlalchemy.event.listen(Base, 'attribute_instrument', build_class_lens)

class Meeting(Base):
__tablename__ = 'meetings'
def __init__(self, memo):
self.memo = memo
id = Column(Integer, primary_key=True)
date = Column(Date)
memo = Column('note', String(60), nullable=True)
category_name = Column('category', String(60), ForeignKey('categories.name'))
category = relationship("Category", backref=backref('meetings'))
topics = relationship("Topic",
secondary=meetings_topics,
backref="meetings")

...
...

好吧,这样我就可以在类级别上通过了,尽管我觉得我在用元类做一些愚蠢的事情,并且我遇到了一些奇怪的间歇性错误,据称“sqlalchemy”模块在 build_class_lens 中无法识别 并评估为 Nonetype。

我不太确定应该如何在实例级别继续进行。我研究了事件接口(interface)。我看到 ORM 事件 init,但它似乎在我的模型上定义的 __init__ 函数之前运行,这意味着当时尚未填充实例属性,所以我无法在它们上构建我的“镜头”。我还想知道属性事件 set 是否有帮助。这是我的下一次尝试,尽管我仍然想知道这是否是最合适的方式。

总而言之,我真的想知道我是否缺少一些真正优雅的方法来解决这个问题。

最佳答案

我认为声明性元类的事情就像旧的 XML 所说的那样,“如果你有问题,并使用 XML,那么现在你有两个问题”。 Python 中的元类非常有用,可以作为检测新类构造的钩子(Hook),仅此而已。我们现在有足够的事件,除了声明性已经做的事情之外,不需要使用元类。

在这种情况下,我会更进一步说,尝试主动构建这些集合的方法并不真正值得 - 懒惰地生成它们要容易得多,如下所示:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
import collections
from sqlalchemy.orm.properties import RelationshipProperty

class memoized_classproperty(object):
"""A decorator that evaluates once at the class level,
assigns the new value to the class.
"""

def __init__(self, fget, doc=None):
self.fget = fget
self.__doc__ = doc or fget.__doc__
self.__name__ = fget.__name__

def __get__(desc, self, cls):
result = desc.fget(cls)
setattr(cls, desc.__name__, result)
return result

class Lens(object):
@memoized_classproperty
def pk_columns(cls):
return class_mapper(cls).primary_key

@memoized_classproperty
def relations(cls):
props = collections.namedtuple('RelationshipItem', 'one many')(set(), set())
# 0.8 will have "inspect(cls).relationships" here
mapper = class_mapper(cls)
for item in mapper.iterate_properties:
if isinstance(item, RelationshipProperty):
if item.direction.name == ('MANYTOONE' or 'ONETOONE'):
local_column = mapper.get_property_by_column(item.local_side[0]).key
props.one.add((local_column, item.key))
else:
props.many.add(item.key)
return props

Base= declarative_base(cls=Lens)

meetings_topics = Table("meetings_topics", Base.metadata,
Column('topic_id', Integer, ForeignKey('topic.id')),
Column('meetings_id', Integer, ForeignKey('meetings.id')),
)
class Meeting(Base):
__tablename__ = 'meetings'
def __init__(self, memo):
self.memo = memo
id = Column(Integer, primary_key=True)
date = Column(Date)
memo = Column('note', String(60), nullable=True)
category_name = Column('category', String(60), ForeignKey('categories.name'))
category = relationship("Category", backref=backref('meetings'))
topics = relationship("Topic",
secondary=meetings_topics,
backref="meetings")

class Category(Base):
__tablename__ = 'categories'
name = Column(String(50), primary_key=True)

class Topic(Base):
__tablename__ = 'topic'
id = Column(Integer, primary_key=True)

print Meeting.pk_columns
print Meeting.relations.one

# assignment is OK, since prop is memoized
Meeting.relations.one.add("FOO")

print Meeting.relations.one

关于python - 我如何 'index' SQLAlchemy模型属性是主键和关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10064896/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com