2012-06-05 13 views
5

He estado pensando en esto por completo, y parece que no se me ocurre cómo solucionar el problema. Tenga en cuenta que he cortado una gran cantidad de campos irrelevantes fuera de mis modelosHerencia + Claves foráneas

estoy en el medio de la codificación de seguridad de mis modelos SQL-Alchemy, y he encontrado el problema siguiente:

Debido a múltiples sistemas de facturación, cada con atributos completamente diferentes, y debido a los diferentes tipos de atributos de Suscripción (es decir, nombres de usuario, ubicaciones de nodos, etc.), he tenido que bajar por la ruta de herencia polimórfica.

class Subscription(Base): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(255)) 
    secret = db.Column(postgresql.BYTEA) 
    type = db.Column(SubscriptionType.db_type()) 
    status = db.Column(StatusType.db_type()) 
    subscription_id = db.Column(db.Integer) 

    __tablename__ = 'subscription' 
    __table_args__ = {'schema':'public'} 

    __mapper_args__ = {'polymorphic_on' : type} 

    def __repr__(self): 
     return '<Subscription: %r>' % self.id 

class Product1(Subscription): 
    __mapper_args__ = {'polymorphic_identity' : SubscriptionType.product1}  
    id = db.Column(db.Integer, db.ForeignKey('public.subscription.id'), 
     primary_key = True) 
    billing_system = db.Column(
     db.Integer, 
     db.ForeignKey('public.billing_system.id') 
    ) 

class BillingSystem(Base): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(255)) 
    type = db.Column(BillingSystemType.db_type()) 

    __tablename__ = 'billing_system' 
    __table_args__ = {'schema':'public'} 

    __mapper_args__ = {'polymorphic_on' : type} 

    def __repr__(self): 
     return '<Subscription: %r>' % self.id 

class BillingSystem1(BillingSystem): 
    __mapper_args__ = {'polymorphic_identity' : BillingSystemType.billingsystem1}  
    id = db.Column(db.Integer, db.ForeignKey('public.billing_system.id'), 
     primary_key = True) 
    billing_system = db.Column(
     db.Integer, 
     db.ForeignKey('public.billing_system.id') 
    ) 
    foo = db.Column(db.Integer) 
    bar = db.Column(db.Integer) 

class BillingSystem2(BillingSystem): 
    __mapper_args__ = {'polymorphic_identity' : BillingSystemType.billingsystem2}  
    id = db.Column(db.Integer, db.ForeignKey('public.billing_system.id'), 
     primary_key = True) 
    billing_system = db.Column(
     db.Integer, 
     db.ForeignKey('public.billing_system.id') 
    ) 
    bing = db.Column(db.Integer) 
    boo = db.Column(db.Integer) 


    __tablename__ = 'billing_system_product2' 
    __table_args__ = {'schema':'public'} 

Todo funciona bien, excepto por una cosa.

decir que corro el siguiente:

>>> a = Product1() 
>>> b = BillingSystem.objects.get(1) 
>>> a.billing_system = b 
>>> session.add(a) 
>>> session.commit() 

que recibirá el siguiente error.

sqlalchemy.exc.ProgrammingError: (ProgrammingError) can't adapt type 'BillingSystem1' 'INSERT INTO 

Entiendo lo que está diciendo, y he intentado lo siguiente.

>>> a.billing_system = b.id 

Esto sólo almacena la identificación, y cuando intento retreive el objeto asociado, I en vez recibo un número entero. Esto implica que tengo que hacer una consulta adicional. Esperaría que este no sea el camino a seguir.

También he intentado añadir claves externas para todos los ID de sistema de facturación a la producto1 Modelo

class BillingSystem1(BillingSystem): 
    __mapper_args__ = {'polymorphic_identity' : BillingSystemType.billingsystem1}  
    id = db.Column(db.Integer, db.ForeignKey('public.billing_system.id'), 
     primary_key = True) 
    billing_system = db.Column(
     db.Integer, 
     db.ForeignKey('public.billing_system.id'), 
     db.ForeignKey('public.billing_system1.id'), 
     db.ForeignKey('public.billing_system2.id'), 
    ) 
    foo = db.Column(db.Integer) 
    bar = db.Column(db.Integer) 

Esto también no funcionaba en absoluto, y he recibido la misma excepción ProgrammingError indicando que el tipo puede se adapta

He rastreado el manual, y no puedo encontrar la manera de hacerlo, pero necesito algún tipo de opción mágico para permitir que esto suceda:

>>> a = BillingSystem.query.get(1) 
>>> type(a) 
BillingSystem 

En lugar de:

>>> a = BillingSystem.query.get(1) 
>>> type(a) 
BillingSystem1 

¿Alguien puede arrojar alguna luz sobre cómo puedo consultar mi conjunto de modelos polimórficos para obtener una ID, y solo obtener la clase del modelo base?

Creo que esto resolverá mi problema, simplemente no estoy seguro de cómo resolverlo.

Gracias por tomarse el tiempo para leer esto, y realmente me gustaría saber dónde estoy yendo mal (también he estado despierto demasiado tiempo, por lo que no está ayudando).

Saludos, Rhys

+3

Esto probablemente no va a ser una respuesta súper útil para usted, pero parece que con el fin de averiguar si te puedo ayudar, voy a tener que leer una gran cantidad de tu código y resuelve pequeños detalles. La mejor manera de obtener una respuesta en este sitio es reducir el código hasta que sea increíblemente simple, pero igual da el mismo error. Luego puede hacer una pregunta muy específica: "¿por qué la línea 3 de estas 4 líneas de código arroja un error?" Como una ventaja adicional, en realidad * soluciona el problema * cuando pasa por este proceso. ¡Buena suerte! –

+1

¡Descanse un poco y su consejo ayudó mucho! Gracias :) –

Respuesta

1
class Subscription(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(255)) 
    secret = db.Column(postgresql.BYTEA) 
    type = db.Column(SubscriptionType.db_type()) 
    status = db.Column(StatusType.db_type()) 
    subscription_id = db.Column(db.Integer) 

    billing_system_id = db.Column(
     db.Integer, 
     db.ForeignKey('public.billing_system.id') 
    ) 
    billing_system = db.relationship('BillingSystem', backref='subscriptions') 

Así que lo que he hecho es:

1) desplazado la clave externa Subscription.billing_system_id hasta el modelo base suscripción 2) Añadido en la suscripción.billing_system Relación

Así que ahora estoy haciendo esto:

>>> o = BillingSystem.query.get(1) 
>>> a = Product1() 
>>> a.billing_system_id = o.id 

que se traduce en:

>>> a.billing_system.subscriptions 
[<cPanel Hosting Reseller: 2>] 
>>> a.billing_system_id 
2 

Así que a menos que estoy haciendo algo mal aquí, parece que funciona. Simplemente no puedo pasar el objeto BillingSystem real, de hecho tengo que configurar la ID. Sin embargo, todavía se aplica obligatoriamente cuando se guarda el modelo, por lo que no veo demasiados problemas con él.

Gracias por su ayuda :)

Cuestiones relacionadas