Sono un nuovo arrivato a SQLAlchemy ORM e sto faticando a realizzare query ish complesse su più tabelle - query che trovo relativamente semplici da fare in Doctrine DQL.Come interrogare più tabelle in SQLAlchemy ORM
Ho oggetti dati di Città, che appartengono a Paesi. Alcune città hanno anche un ID di contea, ma non tutte. Oltre alle chiavi primarie e straniere necessarie, ogni record ha anche un text_string_id, che si collega a una tabella TextStrings che memorizza il nome della città/contea/nazione in diverse lingue. La tabella TextStrings MySQL si presenta così:
CREATE TABLE IF NOT EXISTS `text_strings` (
`id` INT UNSIGNED NOT NULL,
`language` VARCHAR(2) NOT NULL,
`text_string` varchar(255) NOT NULL,
PRIMARY KEY (`id`, `language`)
)
Voglio costruire un breadcrumb per ogni città, della forma:
country_en_name> city_en_name O
country_en_name> county_en_name> city_en_name,
a seconda che sia impostato o meno un attributo County per questa città. In Dottrina questo sarebbe relativamente semplice:
$query = Doctrine_Query::create()
->select('ci.id, CONCAT(cyts.text_string, \'> \', IF(cots.text_string is not null, CONCAT(cots.text_string, \'> \', \'\'), cits.text_string) as city_breadcrumb')
->from('City ci')
->leftJoin('ci.TextString cits')
->leftJoin('ci.Country cy')
->leftJoin('cy.TextString cyts')
->leftJoin('ci.County co')
->leftJoin('co.TextString cots')
->where('cits.language = ?', 'en')
->andWhere('cyts.language = ?', 'en')
->andWhere('(cots.language = ? OR cots.language is null)', 'en');
Con SQLAlchemy ORM, sto lottando per ottenere la stessa cosa. Credo Ho installato gli oggetti in modo corretto - in forma ad esempio:
class City(Base):
__tablename__ = "cities"
id = Column(Integer, primary_key=True)
country_id = Column(Integer, ForeignKey('countries.id'))
text_string_id = Column(Integer, ForeignKey('text_strings.id'))
county_id = Column(Integer, ForeignKey('counties.id'))
text_strings = relation(TextString, backref=backref('cards', order_by=id))
country = relation(Country, backref=backref('countries', order_by=id))
county = relation(County, backref=backref('counties', order_by=id))
Il mio problema è nella interrogazione - ho provato vari approcci per la generazione del breadcrumb, ma nulla sembra funzionare. Alcune osservazioni:
Forse usare cose come CONCAT e IF inline nella query non è molto pitone (è anche possibile con l'ORM?) - quindi ho provato a eseguire queste operazioni al di fuori di SQLAlchemy, in un loop Python del record. Comunque qui ho faticato ad accedere ai singoli campi - ad esempio, i model accessors non sembrano andare in profondità n livelli, ad es. City.counties.text_strings.language non esiste.
Ho anche sperimentato con l'utilizzo di tuple - la più vicina che ho avuto modo di farlo funzionare è stato suddividendo fuori in due query:
# For cities without a county
for city, country in session.query(City, Country).\
filter(Country.id == City.country_id).\
filter(City.county_id == None).all():
if city.text_strings.language == 'en':
# etc
# For cities with a county
for city, county, country in session.query(City, County, Country).\
filter(and_(City.county_id == County.id, City.country_id == Country.id)).all():
if city.text_strings.language == 'en':
# etc
ho diviso fuori in due query perché non potevo capire come rendere la Tuta unirsi opzionale in una sola query. Ma questo approccio è ovviamente terribile e peggio la seconda query non ha funzionato al 100% - non si stava unendo a tutte le diverse city.text_strings per il successivo filtraggio.
Quindi sono perplesso! Sarebbe molto gradito qualsiasi aiuto che tu possa darmi nel percorso giusto per eseguire questo tipo di query ish complesse in SQLAlchemy ORM.
Enorme, grazie Mike per questa risposta: avrei dovuto sapere usare gli alias! Una volta aggiornato il mio SQLAlchemy a una versione più recente il tuo codice ha funzionato bene. Alla fine ho adattato leggermente il tuo codice - incollerò il mio codice qui sotto come risposta separata nel caso qualcuno volesse vederlo. Un ultimo punto: tu dici: "Penso che sia molto più semplice solo per dire: city.text_strings.text_string ..." Ho provato a fare qualcosa di simile, ma questa sintassi non sembrava rispettare i join esterni - cioè le proprietà text_string erano per lingua == 'de' invece della lingua == 'en'. Non sono sicuro se stavo facendo qualcosa di sbagliato! –