2009-05-13 3 views
30

Questo potrà sembrare piuttosto polemico, ma ho appena passato attraverso SQLAlchemy di ORM tutorial e si è conclusa con il seguente codice:SQLAlchemy è contorto?

from sqlalchemy import create_engine 
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker 

engine = create_engine('sqlite:///:memory:', echo=True) 

metadata = MetaData() 
users_table = Table('users', metadata, 
    Column('id', Integer, primary_key=True), 
    Column('name', String), 
    Column('fullname', String), 
    Column('password', String) 
) 

metadata.create_all(engine) 

Base = declarative_base() 
class User(Base): 
    __tablename__ = 'users' 

    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    fullname = Column(String) 
    password = Column(String) 

    def __init__(self, name, fullname, password): 
     self.name = name 
     self.fullname = fullname 
     self.password = password 

    def __repr__(self): 
     return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password) 

users_table = User.__table__ 
metadata = Base.metadata 

Session = sessionmaker(bind=engine) 
Session = sessionmaker() 
Session.configure(bind=engine) # once engine is available 
session = Session() 

# actually using the ORM isn't too bad.. 
ed_user = User('ed', 'Ed Jones', 'edspassword') 
session.add(ed_user) 

our_user = session.query(User).filter_by(name='ed').first() 
print our_user 

session.add_all([ 
    User('wendy', 'Wendy Williams', 'foobar'), 
    User('mary', 'Mary Contrary', 'xxg527'), 
    User('fred', 'Fred Flinstone', 'blah')]) 

ed_user.password = 'f8s7ccs' 

print session.dirty 
print session.new 
session.commit() 

for instance in session.query(User).order_by(User.id): 
    print instance.name, instance.fullname 

for name, fullname in session.query(User.name, User.fullname): 
    print name, fullname 

Questo sembra incredibilmente complicato per efficacemente un tavolo Ciao Mondo, soprattutto rispetto al codice SQLObject approssimativamente simile:

from sqlobject import SQLObject, StringCol, sqlhub, connectionForURI 

sqlhub.processConnection = connectionForURI('sqlite:/:memory:') 

class Person(SQLObject): 
    fname = StringCol() 
    mi = StringCol(length=1, default=None) 
    lname = StringCol() 

Person.createTable() 

p = Person(fname="John", lname="Doe") 
p.mi = 'Q' 
p2 = Person.get(1) 
print p2 
print p2 is p 

capisco SQLAlchemy è "più potente", ma che il potere sembra venire ad un costo, o mi sto perdendo qualcosa?

+8

L'alimentazione ha un costo? Che dici? –

+12

L'alimentazione di SQLAlchemy ha un costo di semplicità d'uso? – dbr

+3

Prova Elixir come indicato di seguito, il tuo Hello World sarà molto simile a SQLObject e sarai comunque in grado di accedere al livello SQLAlchemy. Ho usato SQLObject e sono rimasto frustrato dai suoi limiti, e sono molto contento di Elixir finora (manca un po 'di documentazione e supporto, ma purtroppo la base di utenti sembra limitata). –

risposta

85

Bene, c'è una cosa che ti manca: il tutorial che hai menzionato non "crea" un esempio completo, i diversi frammenti di codice non sono pensati per essere concatenati in un unico file sorgente. Piuttosto, descrivono i diversi modi in cui la biblioteca può essere utilizzata. Non c'è bisogno di provare a fare la stessa cosa più e più volte.

Tralasciando il fatto-con-la-orm parte dal vostro esempio, il codice potrebbe essere la seguente:

from sqlalchemy import * 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker, scoped_session 

engine = create_engine('sqlite:///:memory:', echo=True) 
Base = declarative_base(bind=engine) 
Session = scoped_session(sessionmaker(engine)) 

class User(Base): 
    __tablename__ = 'users' 

    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    fullname = Column(String) 
    password = Column(String) 

Base.metadata.create_all() 

L'estensione "dichiarativo" si occupa di definire il tavolo e la mappatura per la classe , quindi non è necessario dichiarare lo users_table da soli. La classe User consentirà anche l'istanziazione con argomenti di parole chiave, come User(name="foo") (ma non gli argomenti posizionali). Ho anche aggiunto l'uso di scoped_session, il che significa che puoi usare direttamente Session senza doverlo istanziare (creerà un'istanza di una nuova sessione se non ce n'è già una presente nel thread corrente o riutilizzerà altrimenti quella esistente)

+2

Sembra un po 'più sensibile del codice con cui ho finito. Grazie! – dbr

+1

Sono molto riluttante a usare 'scoped_session' per gran parte di tutto; A meno che non si sappia il motivo per cui è necessario archiviare l'archiviazione locale, è necessario creare un'istanza esplicita della sessione e trasferirla secondo necessità. – SingleNegationElimination

+1

@TokenMacGuy Non concordo completamente. 'scoped_session' prende tutte le supposizioni e le discussioni inutili che passano dall'uso delle sessioni. Basta definire la classe 'scoped_session' all'inizio, quindi istanziarla ovunque sia necessario accedere alla sessione e il sistema fa il resto. Ho trovato estremamente utile per tutte le app Web che ho scritto. – CoreDumpError

0

dici "contorto" .... qualcun altro potrebbe dire "flessibile". A volte ne hai bisogno a volte no. Non è fantastico che tu abbia una scelta?

+0

Certo, ma io dico "contorto" perché sembra costringerti a usare molte delle sue funzioni per fare cose di base. La flessibilità è ottima, ma non se coinvolge cinque linee di importazione solo per iniziare! – dbr

1

Bene, SQLAlchemy è diviso in diverse parti, la parte principale principale gestisce semplicemente il DB, trasformando le query create in python nel linguaggio SQL appropriato per il DB sottostante. Poi c'è il supporto per le sessioni, l'orm e la nuova sintassi dichiarativa.

Sembra SQLObject (non posso dirlo con certezza, non l'ho usato da molti anni, e anche allora, solo una volta) ignora la maggior parte e fa subito la parte ORM. Questo spesso rende le cose più facili per i dati semplici (che puoi farla franca nella maggior parte dei casi), ma SQLAlchemy consente layout di db più complessi, e si abbassa e si sporca con il db se ne hai davvero bisogno.

+0

SQLObject sembra supportare le cose più avanzate, come le relazioni one-to-many/many-to-one/many-to-many (che riguardano ogni layout di database che ho visto) e le transazioni. – dbr

10

Gli esempi di codice forniti non sono mele-mele. La versione SQLAlchemy potrebbe essere sbucciato giù un po ':

from sqlalchemy import create_engine 
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker 

engine = create_engine('sqlite:///:memory:', echo=True) 
Base = declarative_base() 

class User(Base): 
    __tablename__ = 'users' 

    id = Column('id', Integer, primary_key=True) 
    name = Column('name', String) 
    fullname = Column('fullname', String) 
    password = Column('password', String) 

    def __repr__(self): 
     return "" % (self.name, self.fullname, self.password) 

Base.metadata.create_all(engine) 

Session = sessionmaker(bind=engine) 
session = Session() 

# actually using the ORM isn't too bad.. 
ed_user = User(name='ed', fullname='Ed Jones', password='edspassword') 
session.add(ed_user) 

our_user = session.query(User).filter_by(name='ed').first() 

session.add_all([ 
    User(name='wendy', fullname='Wendy Williams', password='foobar'), 
    User(name='mary', fullname='Mary Contrary', password='xxg527'), 
    User(name='fred', fullname='Fred Flinstone', password='blah')]) 

ed_user.password = 'f8s7ccs' 

session.flush() 

for instance in session.query(User).order_by(User.id): 
    print instance.name, instance.fullname 

for name, fullname in session.query(User.name, User.fullname): 
    print name, fullname 

Si potrebbe anche trovare Elixir più come SQLObject (ma visto che non ho usato sia, questa è solo una supposizione).

Non avendo affatto utilizzato SQLObject, non posso commentare ciò che SA fa meglio. Ma ho avuto grandi esperienze con SA, specialmente quando si tratta di schemi legacy complicati, reali. Fa un buon lavoro di venire con buone query SQL per impostazione predefinita e ha molti modi per ottimizzarle.

Ho trovato che l'autore di SQLAlchemy elevator pitch regge abbastanza bene nella pratica.

1

Dopo aver utilizzato SQLObject (e letto solo su SQLAlchemy), posso dire che uno dei punti di forza di SQLObject è la facilità e la semplicità con cui è possibile ottenere risultati.Inoltre, il gruppo di e-mail (https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss) fornisce un supporto eccellente che ti risponde in modo abbastanza rapido.

1

Prova Quick ORM, è ancora più semplice:

from quick_orm.core import Database 
from sqlalchemy import Column, String 

class User(object): 
    __metaclass__ = Database.DefaultMeta 
    name = Column(String(30)) 

if __name__ == '__main__': 
    database = Database('sqlite://') 
    database.create_tables() 

    user = User(name = 'Hello World') 
    database.session.add_then_commit(user) 

    user = database.session.query(User).get(1) 
    print 'My name is', user.name 

Quick ORM è costruito su SQLAlchemy, quindi si potrebbe dire che SQLAlchemy potrebbe essere semplice come SQLObject.

+0

Mi dispiace che quick_orm non venga più mantenuto. –