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

jba*_*ter 5 python sqlalchemy multiple-inheritance

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

我要做的是创建一个泛型creator函数association_proxy,创建PersonMovieRole或PersonShowRole对象,具体取决于Person添加到的具体实例的类.我现在坚持的是,我不知道如何将调用类传递给创建者函数.这是可能的,或者甚至可能更容易实现我想要完成的工作?

Xio*_*ion 3

当你的persons字段被定义时,你无法真正知道它最终会进入哪个类。Python 使用类成员的现成字典并从中创建类(通过type.__new__),但当它发生时,这些成员已经完全定义了。

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

class Movie(Base, MyBaseClass, MetadataMixin('Movie')):
    pass
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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