SQLAlchemy:具有动态'association_proxy'创建函数的多重继承

时间:2011-04-23 13:34:38

标签: python sqlalchemy multiple-inheritance

我目前正在尝试使用SQLAlchemy创建以下数据库模式(使用ext.declarative):

我有一个基类MyBaseClass,它为我所有可公开访问的类提供了一些常用功能,一个mixin类MetadataMixin,它提供了从imdb查询元数据并存储它的功能。 子类MetadataMixin的每个类都有一个字段persons,它提供与Person类实例的M:N关系,以及一个提供1:N关系的字段persons_roles到一个对象(每个子类一个),它存储role一个具体的Person在子类的实例中播放。

这是我的代码目前的缩写版本:

from sqlalchemy import Column, Integer, Enum, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class MyBaseClass(object):
    """Base class for all publicly accessible classes"""
    id = Column(Integer, primary_key=True)


class Person(MyBaseClass):
    """A Person"""

    name = Column(Unicode)
    movies = association_proxy('movie_roles', 'movie',
                               creator=lambda m: _PersonMovieRole(movie=m))
    shows = association_proxy('show_roles', 'show',
                              creator=lambda s: _PersonShowRole(show=s=))


class _PersonMovieRole(Base):
    """Role for a Person in a Movie"""
    __tablename__ = 'persons_movies'

    id = Column(Integer, primary_key=True)
    role = Column(Enum('none', 'actor', 'writer', 'director', 'producer'),
                  default='none')
    person_id = Column(Integer, ForeignKey('persons.id'))
    person = relationship('Person', backref='movie_roles')
    movie_id = Column(Integer, ForeignKey('movies.id'))
    movie = relationship('Movie', backref='persons_roles')


class _PersonShowRole(Base):
    """Role for a Person in a Show"""
    __tablename__ = 'persons_shows'

    id = Column(Integer, primary_key=True)
    role = Column(Enum('none', 'actor', 'writer', 'director', 'producer'),
                  default='none')
    person_id = Column(Integer, ForeignKey('persons.id'))
    person = relationship('Person', backref='show_roles')
    show_id = Column(Integer, ForeignKey('shows.id'))
    show = relationship('Episode', backref='persons_roles')


class MetadataMixin(object):
    """Mixin class that provides metadata-fields and methods"""

    # ...
    persons = association_proxy('persons_roles', 'person',
                                creator= #...???...#)


class Movie(Base, MyBaseClass, MetadataMixin):
    #....
    pass

我要做的是为creator创建一个通用association_proxy函数,该函数创建PersonMovieRole或PersonShowRole对象,具体取决于{{1}的具体实例的类添加到。}我现在坚持的是,我不知道如何将调用类传递给创建者函数。 这可能吗,或者甚至可能更容易实现我想要完成的任务?

1 个答案:

答案 0 :(得分:3)

当你的persons字段被定义时,你无法真正知道它最终会出现什么类.Python会占用类成员的现成字典并从中创建类(通过type.__new__) ,但一旦发生,那些成员已经完全定义。

因此,您需要直接向mixin提供所需的信息,并容忍它将在您的代码中创建的小重复。我会选择与此类似的界面:

class Movie(Base, MyBaseClass, MetadataMixin('Movie')):
    pass

(您也不能拥有MetadataMixin(Movie),原因完全相同:Movie要求在创建类时完全定义其基类。

要实现这样的“参数化类”,只需使用函数:

def MetadataMixin(cls_name):
    """Mixin class that provides metadata-fields and methods"""
    person_role_cls_name = 'Person%sRole' % cls_name
    person_role_cls = Base._decl_class_registry[person_role_cls_name]

    class Mixin(object):
        # ...
        persons = association_proxy('persons_roles', 'person',
                                    creator=person_role_cls)
    return Mixin

这是有效的,因为我们在Base._decl_class_registry中查找的内容 - 从声明性基础开始的所有类的注册表 - 不是最终类(例如Movie),而是关联对象(例如PersonMovieRole)。