简体   繁体   English

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

[英]SQLAlchemy: Multiple Inheritance with dynamic 'association_proxy' creator function

I am currently trying to create the following database schema with SQLAlchemy (using ext.declarative): 我目前正在尝试使用SQLAlchemy创建以下数据库模式(使用ext.declarative):

I have a base class MyBaseClass which provides some common functionality for all of my publicly accessible classes, a mixin class MetadataMixin that provides functionality to query metadata from imdb and store it. 我有一个基类MyBaseClass ,它为我所有可公开访问的类提供了一些常用功能,一个mixin类MetadataMixin ,它提供了从imdb查询元数据并存储它的功能。 Every class that subclasses MetadataMixin has a field persons which provides a M:N relationship to instances of the Person class, and a field persons_roles which provides a 1:N relationship to an object (one for each subclass) which stores the role a concrete Person plays in the instance of the subclass. MetadataMixin子类的每个类都有一个字段persons ,它提供与Person类实例的M:N关系,以及字段persons_roles ,它提供与对象(每个子类一个)的1:N关系,该关系将role存储为具体Person在子类的实例中播放。

This is an abbreviated version of what my code looks like at the moment: 这是我的代码目前的缩写版本:

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

What I'm trying to do is to create a generic creator function for association_proxy that creates either a PersonMovieRole or a PersonShowRole object, depending on the class of the concrete instance that a Person is added to. 我要做的是为association_proxy创建一个通用的creator函数,创建PersonMovieRole或PersonShowRole对象,具体取决于添加Person的具体实例的类。 What I'm stuck on at the moment is that I don't know how to pass the calling class to the creator function. 我现在坚持的是,我不知道如何将调用类传递给创建者函数。 Is this possible, or is there maybe even an easier way for what I'm trying to accomplish? 这是可能的,或者甚至可能更容易实现我想要完成的工作?

By the time your persons field is defined, you cannot really know what class it will end up in. Python takes up ready dictionaries of class members and creates classes out of them (via type.__new__ ), but when it happens, those members are already fully defined. 当你的persons字段被定义时,你不可能真正知道它最终会成为什么类.Python占用类成员的现成字典并type.__new__创建类(通过type.__new__ ),但是当它发生时,那些成员是已完全定义。

So you need to provide the required information directly to the mixin, and tolerate the small duplication it will create in your code. 因此,您需要直接向mixin提供所需的信息,并容忍它将在您的代码中创建的小重复。 I'd opt for interface similar to this one: 我选择类似这个的界面:

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

(You cannot have MetadataMixin(Movie) either, for the exact same reasons: Movie requires its base classes to be completely defined by the time the class is created). (您也不能使用MetadataMixin(Movie) ,原因完全相同: Movie要求其基类在创建类时完全定义。

To implement such "parametrized class", simply use a function: 要实现这样的“参数化类”,只需使用一个函数:

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

This works because what we're looking up in Base._decl_class_registry - the registry of all classes descending from your declarative base - is not the final class (eg Movie ), but the association object (eg PersonMovieRole ). 这是有效的,因为我们在Base._decl_class_registry查找的Base._decl_class_registry - 从声明性基础下降的所有类的注册表 - 不是最终类(例如Movie ),而是关联对象(例如PersonMovieRole )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM