Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enhance SQLAlchemy syntax for polymorphic identity

I have a declarative base class Entity which defines the column name as polymorphic, e.g.

class Entity(DeclarativeBase):
    name = Column('name', String(40))
    __mapper_args__ = {'polymorphic_on':name}

In subclasses, I could now say

class Experiment(Entity):
    __mapper_args__ = {'polymorphic_identity': "experiment"}

and be done. However, I would like to ease the creation of subclasses for the users of my library and therefore make the following possible:

  • Have a better syntax for it which is not as convoluted as the default syntax. This could be a simple assignment poly_id = "exp" inside the class or maybe a class decorator.
  • If no polymorphic_identity is given, extract the name from the name of the subclass.

I tried to get this done using metaclasses (only the second part):

from sqlalchemy.ext.declarative import DeclarativeMeta

class Meta(type):
    def __init__(cls, classname, bases, dict_):
        dict_["__mapper_args__"] = {'polymorphic_identity': classname}
        return super(Meta, cls).__init__(classname, bases, dict_)

class CombinedMeta(Meta, DeclarativeMeta):
    pass

class Experiment(Entity):
    __metaclass__ = CombinedMeta

so that, in my opinion, my Meta should set the name before calling the DeclarativeMeta but it does not seem to work. So either the DeclarativeMeta which supposedly sets the polymorphic name never sees the change because I messed up the MRO or what I’m doing is plain wrong anyway. What would I need to change or is there even something like this in SQLAlchemy already?

like image 546
Debilski Avatar asked Dec 16 '10 12:12

Debilski


2 Answers

Shame on me but the problem could easily be solved by not using the dict_ but by setting the attribute directly on cls.

class Meta(DeclarativeMeta):
    def __init__(cls, *args, **kw):
        if getattr(cls, '_decl_class_registry', None) is None:
            return # they use this in the docs, so maybe its not a bad idea
        cls.__mapper_args__ = {'polymorphic_identity': cls.__name__}
        return super(Meta, cls).__init__(*args, **kw)

class Experiment(Entity):
    __metaclass__ = Meta
like image 113
Debilski Avatar answered Nov 14 '22 23:11

Debilski


Recent sqlalchemy docs show ways of using mixin classes for such purposes (I'm not sure whether it only works in versions >= 0.6 or if it is only better documented now)

http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative/mixins.html

like image 38
Steven Avatar answered Nov 15 '22 01:11

Steven