2011-04-23 13 views
5

Actualmente estoy tratando de crear el siguiente esquema de base de datos con SQLAlchemy (usando ext.declarative):SQLAlchemy: La herencia múltiple con 'association_proxy' dinámico función de creador

tengo una clase base MyBaseClass que proporciona cierta funcionalidad común para todos de mis clases de acceso público, una clase mixin MetadataMixin que proporciona funcionalidad para consultar los metadatos de imdb y almacenarlos. Cada clase que subclases MetadataMixin tiene un campo persons que proporciona una relación M: N a instancias de la clase Person, y un campo persons_roles que proporciona una relación 1: N a un objeto (uno para cada subclase) que almacena el role un hormigón La persona juega en la instancia de la subclase.

Esta es una versión abreviada de lo que mi código es el siguiente en la actualidad

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 

Lo que estoy tratando de hacer es crear una función genérica para creatorassociation_proxy que crea ya sea un PersonMovieRole o una PersonShowRole objeto, dependiendo de la clase de la instancia concreta a la que se agrega Person. En lo que estoy estancado en este momento es que no sé cómo pasar la clase de llamada a la función de creador. ¿Es esto posible, o tal vez hay una manera más fácil para lo que estoy tratando de lograr?

Respuesta

3

Por el momento se define su campo persons, realmente no se puede saber qué clase que va a terminar en. Pitón ocupa diccionarios listas de miembros de la clase y crea clases fuera de ellos (a través de type.__new__), pero cuando ocurre, los los miembros ya están completamente definidos.

Así que debe proporcionar la información requerida directamente a la mezcla y tolerar la pequeña duplicación que creará en su código. Me gustaría optar por la interfaz similar a éste:

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

(no se puede tener MetadataMixin(Movie) o bien, por exactamente las mismas razones: Movie requiere sus clases base para ser completamente definidas por la vez que se crea la clase).

Para implementar tal "clase parametrizada", basta con utilizar una función:

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 

Esto funciona porque lo que estamos buscando en Base._decl_class_registry - el registro de todas las clases que descienden desde su base declarativa - no es el clase final (por ejemplo, Movie), pero el objeto de asociación (por ejemplo, PersonMovieRole).