2012-06-05 11 views
5

Mi è stato assolutamente tormentato da questo, e non riesco a capire come risolvere il problema. Si prega di notare che ho tagliato un sacco di campi irrilevanti fuori dei miei modelliEreditarietà + chiavi estere

Io sono nel mezzo di codifica i miei modelli SQL-Alchemy, e ho incontrato il seguente problema:

causa di molteplici sistemi di fatturazione, ogni con attributi completamente diversi, e a causa dei diversi tipi di attributi di abbonamento (es. nomi utente, posizioni dei nodi, ecc.), ho dovuto scendere la strada dell'ereditarietà polimorfica.

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'} 

Funziona tutto bene, tranne una cosa.

dire che ho eseguito il seguente:

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

riceverò il seguente errore.

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

Capisco cosa sta dicendo, e ho provato quanto segue.

>>> a.billing_system = b.id 

Ciò solo memorizza l'ID, e quando provo retreive l'oggetto associato, ho invece ricevuto un numero intero. Ciò implica che devo fare una query aggiuntiva. Mi aspetterei che questa non sia la strada da percorrere.

ho anche provato ad aggiungere chiavi esterne per tutti gli ID di sistema di fatturazione al Product1 modello

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) 

Questa, inoltre, non ha funzionato affatto, e ho ricevuto la stessa eccezione ProgrammingError affermando che il tipo può adattarsi

ho spulciato il manuale, e non riesco a trovare il modo di farlo, ma ho bisogno di una qualche forma di opzione magico per permettere che ciò accada:

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

Invece di:

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

Qualcuno è in grado di fare luce su come posso interrogare il mio set di modelli polimorfici per un ID e ottenere solo la classe del modello base?

Sento che questo risolverà il mio problema, im non sono sicuro di come risolverlo.

Grazie per aver trovato il tempo di leggere questo, e mi piacerebbe davvero scoprire dove sto andando male (sono stato anche sveglio troppo a lungo, quindi non aiuta).

Cheers, Rhys

+3

Questo probabilmente non sta per essere una risposta super-utile per voi, ma sembra che, al fine di scoprire se posso aiutarti, dovrò leggere un enorme quantità di codice e capire piccoli dettagli. Il modo migliore per ottenere una risposta su questo sito è quello di abbattere il codice fino a quando non è incredibilmente semplice, ma dà ancora lo stesso errore. Quindi puoi fare una domanda molto specifica: "perché la riga 3 di queste 4 righe di codice genera un errore?" Come bonus aggiuntivo di solito in realtà * risolvi il problema * quando passi attraverso questo processo. In bocca al lupo! –

+1

Un po 'di riposo e il tuo consiglio mi ha aiutato molto! Grazie :) –

risposta

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') 

Quindi ciò che ho fatto è:

1) ha spostato la chiave esterna Subscription.billing_system_id fino alla Base Abbonamento modello 2) Aggiunto nella sottoscrizione.billing_system Relazione

Così ora sto facendo questo:

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

che si traduce in:

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

Quindi, a meno che sto facendo qualcosa di sbagliato qui, sembra funzionare. Non riesco a passare l'oggetto BillingSystem effettivo, in realtà devo impostare l'ID. È ancora applicato in modo referenziale quando il modello viene salvato, quindi non vedo troppi problemi con esso.

Grazie per il vostro aiuto :)