2011-04-23 69 views
5

目前我試圖創建SQLAlchemy的(使用ext.declarative)以下數據庫模式:的SQLAlchemy:動態「association_proxy」生成器功能的多重繼承

我有一個基類MyBaseClass它提供了一些常用的功能爲所有我的公開可訪問類,混合類MetadataMixin,提供從imdb查詢元數據並存儲它的功能。 子類MetadataMixin每個類都有一個字段persons它提供了一個M:N至Person類的實例的關係,以及場persons_roles它提供了一個1:其存儲role混凝土N的關係的一個對象(一個用於每個子類)人在子類的實例中播放。

這是我的代碼看起來像此刻的縮寫形式:

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 

我想要做的是創造association_proxy通用creator函數創建無論是PersonMovieRole或PersonShowRole對象,具體取決於將Person添加到的具體實例的類別。我現在堅持的是我不知道如何將調用類傳遞給創建者函數。 這是可能的,還是有可能更簡單的方法來實現我想要實現的目標?

回答

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)。