2012-07-26 3 views
12

Quiero hacer una función que, dado el nombre de una tabla, devuelva el modelo con ese nombre de tabla. Ej:SQLAlchemy: obtener el modelo del nombre de la tabla. Esto puede implicar agregar alguna función a un constructor de metaclase por lo que puedo ver

class Model(Base): 
    __tablename__ = 'table' 
    ...a bunch of Columns 

def getModelFromTableName(tablename): 
    ...something magical 

por lo getModelFromTableName ('mesa') debe devolver la clase de modelo.

Mi objetivo es utilizar la función en un generador de formulario simple que estoy haciendo ya que FormAlchemy no funciona con python3.2 y quiero que maneje muy bien las claves externas.

¿Alguien puede darme algún consejo sobre cómo hacer que getModelFromTableName funcione?

Aquí hay una idea que he (que podría ser totalmente equivocado, no he trabajado con las clases de meta antes ...)

¿Y si tuviera que hacer mis clases heredan de Modelo Base, así como alguna otra clase (TableReg) y tienen la clase meta del modelo de tienda TableReg. nombre de tabla en algún diccionario global o Singleton.

Me doy cuenta de que esto podría ser totalmente inactivo porque la metaclase de Base hace algunas cosas muy importantes y totalmente ingeniosas que no quiero romper, pero supongo que tengo que haber una forma de agregar un poco de código de constructor a la clase meta de mis modelos. O no entiendo

+3

Usted puede hacer que al iteración a través de 'Base._decl_class_registry' – sayap

Respuesta

19

Inspirado por Eevee 's comentario:

def get_class_by_tablename(tablename): 
    """Return class reference mapped to table. 

    :param tablename: String with name of table. 
    :return: Class reference or None. 
    """ 
    for c in Base._decl_class_registry.values(): 
    if hasattr(c, '__tablename__') and c.__tablename__ == tablename: 
     return c 
+4

Funciona muy bien: para el frasco-sqlalchemy, reemplace Base con db.Model ya que son lo mismo (más o menos). – pip

+0

tuvo que cambiar '__tablename__' a' __table__' para hacer que esto funcione –

+0

Simply MAGICCC !!! –

-1

Iba a eliminar esto, pero creo que la discusión en los comentarios podría ser útil para las personas que desean conocer algunas buenas prácticas. Toma esta respuesta con una pizca de sal.


algo como esto hace el truco:

def getModelFromTableName(sTable): 
    """ 
    return the Model class with the given __tablename__ 
    """ 
    globals = globals() 
    for k in globals: 
     if type(globals[k]) == sqlalchemy.ext.declarative.DeclarativeMeta: 
      try: 
       if globals[k].__tablename__ == sTable: 
        return globals[k] 
      except: 
       pass 
    return None 
+0

oh mi Dios. esto es frágil por todos lados, pero _si nada más_: ¡no es necesario que pongas una cadena a la clase para probar qué es! simplemente marque 'issubclass (globals [k], Base)'! – Eevee

+0

@Eevee: hey, funciona. El comentario de sayap tiene sentido ... ¿puedes decirme cómo es quebradizo? Sé que un intento ... excepto que, sin excepción, el tipo es generalmente una mala práctica, y no es necesario cambiar las cosas (lo sacan). ¿Qué más? – Sheena

+0

'type (...) == ...' también es innecesario; use 'issubclass' con su clase base o' isinstance' con la metaclase. podría iterar sobre 'globals.items()' en lugar de hacer una búsqueda de teclas varias veces. una función como esta debería generar una excepción en caso de falla.pero lo más importante, tan pronto como un archivo de modelo como este se vuelve más grande que un par de docenas de tablas, la mayoría de los desarrolladores se inclinarán a dividirlo, lo que romperá este código de una forma que puede no ser inmediatamente obvia. – Eevee

4

función de utilidad para esto se ha añadido a SQLAlchemy-Utils. Ver get_class_by_table docs para más información. La solución en SQLAlchemy-Utils también puede cubrir escenarios de herencia de tabla única.

import sqlalchemy as sa 


def get_class_by_table(base, table, data=None): 
    """ 
    Return declarative class associated with given table. If no class is found 
    this function returns `None`. If multiple classes were found (polymorphic 
    cases) additional `data` parameter can be given to hint which class 
    to return. 

    :: 

     class User(Base): 
      __tablename__ = 'entity' 
      id = sa.Column(sa.Integer, primary_key=True) 
      name = sa.Column(sa.String) 


     get_class_by_table(Base, User.__table__) # User class 


    This function also supports models using single table inheritance. 
    Additional data paratemer should be provided in these case. 

    :: 

     class Entity(Base): 
      __tablename__ = 'entity' 
      id = sa.Column(sa.Integer, primary_key=True) 
      name = sa.Column(sa.String) 
      type = sa.Column(sa.String) 
      __mapper_args__ = { 
       'polymorphic_on': type, 
       'polymorphic_identity': 'entity' 
      } 

     class User(Entity): 
      __mapper_args__ = { 
       'polymorphic_identity': 'user' 
      } 


     # Entity class 
     get_class_by_table(Base, Entity.__table__, {'type': 'entity'}) 

     # User class 
     get_class_by_table(Base, Entity.__table__, {'type': 'user'}) 


    :param base: Declarative model base 
    :param table: SQLAlchemy Table object 
    :param data: Data row to determine the class in polymorphic scenarios 
    :return: Declarative class or None. 
    """ 
    found_classes = set(
     c for c in base._decl_class_registry.values() 
     if hasattr(c, '__table__') and c.__table__ is table 
    ) 
    if len(found_classes) > 1: 
     if not data: 
      raise ValueError(
       "Multiple declarative classes found for table '{0}'. " 
       "Please provide data parameter for this function to be able " 
       "to determine polymorphic scenarios.".format(
        table.name 
       ) 
      ) 
     else: 
      for cls in found_classes: 
       mapper = sa.inspect(cls) 
       polymorphic_on = mapper.polymorphic_on.name 
       if polymorphic_on in data: 
        if data[polymorphic_on] == mapper.polymorphic_identity: 
         return cls 
      raise ValueError(
       "Multiple declarative classes found for table '{0}'. Given " 
       "data row does not match any polymorphic identity of the " 
       "found classes.".format(
        table.name 
       ) 
      ) 
    elif found_classes: 
     return found_classes.pop() 
    return None 
+1

Esta función toma el objeto de tabla (exactamente el mismo, 'c .__ table__ es table'), no un nombre de tabla. – dequis

4

se guardan del OrangeTux respuesta no toma en cuenta los esquemas. Si tiene homónimos de mesa en los diferentes esquemas de empleo:

def get_class_by_tablename(table_fullname): 
    """Return class reference mapped to table. 

    :param table_fullname: String with fullname of table. 
    :return: Class reference or None. 
    """ 
    for c in Base._decl_class_registry.values(): 
    if hasattr(c, '__table__') and c.__table__.fullname == table_fullname: 
     return c 

fullname es una tabla de atributos, véase: https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/sql/schema.py#L455

Cuestiones relacionadas