SQLAlchemy join - result

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

Moin,

ich hab da ein kleines Problem mit SQLAlchemy, scheinbar ist mein query zwar korrekt, nur komme ich nicht ans result ran.

In phpmyadmin sieht die Abfrage wie folgt aus

Code: Alles auswählen

SELECT wp_woocommerce_order_itemmeta. * , wp_woocommerce_order_items. *
FROM wp_woocommerce_order_items
JOIN wp_woocommerce_order_itemmeta ON wp_woocommerce_order_itemmeta.order_item_id = wp_woocommerce_order_items.order_item_id
WHERE order_id = "302"
AND meta_key LIKE '_line_total'
ORDER BY wp_woocommerce_order_itemmeta.meta_key
LIMIT 0 , 30
Dort bekomme ich ein nutzbares Ergebnis, meine Umsetzung in Python sieht wie folgt aus:

Code: Alles auswählen

class woo_order_items(Base):
    __tablename__ ='wp_woocommerce_order_items'
    order_item_id = Column(Integer, primary_key=True)
    order_item_name = Column(String)
    order_item_type = Column(String)
    order_id = Column(String)

class woo_order_meta(Base):
    __tablename__ = 'wp_woocommerce_order_itemmeta'
    meta_id = Column(Integer, primary_key=True)
    order_item_id = Column(String, ForeignKey("wp_woocommerce_order_items.order_item_id"))
    meta_key = Column(String)
    meta_value = Column(String)
    
    jtable = s.query(woo_order_items).join(woo_order_meta, \
    woo_order_items.order_item_id ==woo_order_meta.order_item_id).filter\
    (and_(woo_order_items.order_id == ordernr),(woo_order_meta.meta_key == '_line_total')).all()

  for x in jtable:
  	print(x)
Problem ist wie beschrieben das ich mir wie im beispiel oben zwar x ausgeben lassen kann, aber halt nur als:
<__main__.woo_order_items object at 0x7f9f41413d50>
<__main__.woo_order_items object at 0x7f9f41413dd0>
<__main__.woo_order_items object at 0x7f9f41413e50>
<__main__.woo_order_items object at 0x7f9f41413ed0>
<__main__.woo_order_items object at 0x7f9f41413f90>

Aber halt nicht x.meta_id oder die werte andere columns

Ich hab die letzte Stunde alles möglich versucht um an die Ergebnisse des results zu kommen, aber ohne jeden Erfolg.

Die Menge der rows scheint bis jetzt mit denen die ich von phpmyadmin zurückbekomme übereinzustimmen, ebenfalls wenn ich sqlalchemy mit echo=True benutzte sieht der query in Ordnung aus. Ich würde nur echt gerne an die result Werte rankommen.

Hoffe es ist einigermaßen klar was ich meine


NACHTRAG:
Wenn ich aus dem print(x) ein print(x.__table__.columns) mache bekomme ich als result
['wp_woocommerce_order_items.order_item_id', 'wp_woocommerce_order_items.order_item_name', 'wp_woocommerce_order_items.order_item_type', 'wp_woocommerce_order_items.order_id']

Im phpmyadmin bekomme ich allerdings viel mehr infos:
meta_id order_item_id meta_key meta_value order_item_id order_item_name order_item_type order_id
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@taake: Deine Query geht ja auch nur über woo_order_items. Du mußt schon beide Tabellen in einer Query abfragen:

Code: Alles auswählen

q = (s.query(woo_order_items, woo_order_meta).
        filter(woo_order_items.order_item_id ==woo_order_meta.order_item_id).
        filter((woo_order_items.order_id == ordernr) & (woo_order_meta.meta_key == '_line_total'))
    ).all()
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

@Sirus: Danke, der muss bei den unterschiedlichen Test-querys untergegangen sein.
Leider ändert sich am eigentlichem Problem nichts, komme immer noch nicht an die Ergebnisse ran:

Code: Alles auswählen

jtable = s.query(woo_order_items, woo_order_meta).\
            filter(woo_order_items.order_item_id ==woo_order_meta.order_item_id).\
            filter((woo_order_items.order_id == ordernr) & (woo_order_meta.meta_key == '_line_total')).all()
for x in jtable:
    print (x.meta_key)
2016-10-10 13:58:16,457 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2016-10-10 13:58:16,457 INFO sqlalchemy.engine.base.Engine {}
2016-10-10 13:58:16,481 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2016-10-10 13:58:16,481 INFO sqlalchemy.engine.base.Engine {}
2016-10-10 13:58:16,525 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'
2016-10-10 13:58:16,525 INFO sqlalchemy.engine.base.Engine {}
2016-10-10 13:58:16,552 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2016-10-10 13:58:16,552 INFO sqlalchemy.engine.base.Engine {}
2016-10-10 13:58:16,575 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2016-10-10 13:58:16,575 INFO sqlalchemy.engine.base.Engine {}
2016-10-10 13:58:16,598 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin AS anon_1
2016-10-10 13:58:16,598 INFO sqlalchemy.engine.base.Engine {}
2016-10-10 13:58:16,644 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-10-10 13:58:16,645 INFO sqlalchemy.engine.base.Engine SELECT wp_woocommerce_order_items.order_item_id AS wp_woocommerce_order_items_order_item_id, wp_woocommerce_order_items.order_item_name AS wp_woocommerce_order_items_order_item_name, wp_woocommerce_order_items.order_item_type AS wp_woocommerce_order_items_order_item_type, wp_woocommerce_order_items.order_id AS wp_woocommerce_order_items_order_id, wp_woocommerce_order_itemmeta.meta_id AS wp_woocommerce_order_itemmeta_meta_id, wp_woocommerce_order_itemmeta.order_item_id AS wp_woocommerce_order_itemmeta_order_item_id, wp_woocommerce_order_itemmeta.meta_key AS wp_woocommerce_order_itemmeta_meta_key, wp_woocommerce_order_itemmeta.meta_value AS wp_woocommerce_order_itemmeta_meta_value
FROM wp_woocommerce_order_items, wp_woocommerce_order_itemmeta
WHERE wp_woocommerce_order_items.order_item_id = wp_woocommerce_order_itemmeta.order_item_id AND wp_woocommerce_order_items.order_id = %(order_id_1)s AND wp_woocommerce_order_itemmeta.meta_key = %(meta_key_1)s
2016-10-10 13:58:16,645 INFO sqlalchemy.engine.base.Engine {u'meta_key_1': '_line_total', u'order_id_1': '302'}
2016-10-10 13:58:16,685 INFO sqlalchemy.engine.base.Engine ROLLBACK
Traceback (most recent call last):
File "/home/bbh3/scripts/python/wc.py", line 110, in <module>
match_woo_items('302')
File "/home/bbh3/scripts/python/wc.py", line 100, in match_woo_items
print (x.meta_key)
AttributeError: 'result' object has no attribute 'meta_key'
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@taake: muß man Dir jedes Häppchen vorkauen? Entweder liest man sich die Dokumentation durch, was denn ein query über mehrere Tabellen als Ergebnis liefert oder schaut sich einfach mal an, was denn das x ist, das man da zurückbekommt.
BlackJack

@taake: Dann schau Dir doch einfach mal an wie das Ergebnis aussieht und rate nicht wie es aussehen könnte, aber eben nicht tut. Zum Beispiel in der Dokumentation, wo es beschrieben wird und Beispiele sind (http://docs.sqlalchemy.org/en/latest/or ... l#querying) oder in einer interaktiven Shell mal schauen was für Attribute das Ergebniselement tatsächlich hat.

Ich frage mich auch warum Du das ORM verwendest, aber dann nicht das ORM verwendest. Wenn man das ORM verwendet, würde man eher noch eine `relationship()` hinzufügen und dann entweder in der Definition oder bei der Abfrage sagen, dass die jeweiligen Objekte in *einer* Abfrage abgefragt werden sollen. Je nach dem ob man das immer braucht, oder nur manchmal.

Was ist denn das für eine Beziehung zwischen den Tabellen? 1:1 oder 1:n?
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

@BlackJack: Ah vielen Dank für den Denkanstoß mit dem IDE - jetzt kann ich auch sehen was drin steckt.
Allerdings hat mir der Link nicht wirklich weiter geholfen, hatte auch schon allen möglichen Kram ausprobiert, Sachen wie _asdict() usw. nur konnte ich mit keinem raus finden wie das result genau aufgebaut ist.
Und ja ich hätte das ganze auch besser im model umsetzen können, allerdings das Problem das ich sqlalchemy nur selten nutzen bzw im speziellen joins von zwei Datenbanken zum ersten Mal darin umgesetzen wollte.
Die Beziehung ist im Übrigen 1:n
BlackJack

@taake: Der Link ist auf das Tutorial die ersten drei Beispiele zeigen in Wort und Bild, äh, Beispielcode, wie die Ergebnisse aufgebaut sind. Wenn mehr als ein Argument bei `query()` angegeben wird, dann sind die Ergebniselemente „named tuple“ mit einem Element pro Argument. Und der Name entweder der Attributname oder der Klassenname. Das steht da im Text und im Beispielcode. Und sonst steht da nichts weiter was davon grossartig ablenken würde.

Join von zwei Tabellen und nicht zwei Datenbanken. Und das braucht man bei relationalen Datenbanken doch eigentlich ständig. Und man muss bei ORMs bei der Abfrage in der Regel wenig bis gar nichts dafür machen, weil man diese Tabellenbeziehungen ja schon bei der ”Deklaration” der Klassen festlegt.

Komplett ungetestet:

Code: Alles auswählen

class WooOrderItem(Base):
    __tablename__ = 'wp_woocommerce_order_items'
    
    order_item_id = Column(Integer, primary_key=True)
    order_item_name = Column(String)
    order_item_type = Column(String)
    order_id = Column(String)

    # `order_metas` from `WooOrderMeta` relationship.


class WooOrderMeta(Base):
    __tablename__ = 'wp_woocommerce_order_itemmeta'

    meta_id = Column(Integer, primary_key=True)
    order_item_id = Column(String, ForeignKey(WooOrderItem.order_item_id))
    meta_key = Column(String)
    meta_value = Column(String)

    order_item = relationship(WooOrderItem, backref='metas')

# ...
    order_items = (
        session.query(WooOrderItem)
            .join(WooOrderMeta)
            .options(joinedload(WooOrderItem.metas))
            .filter(
                WooOrderItem.order_id == '302',
                WooOrderMeta.meta_key == '_line_total',
            )
            .order_by(WooOrderMeta.meta_key)  # Macht irgendwie keinen Sinn‽
            .all()
    )
    for order_item in order_items:
        print(order_item.order_item_name)
        for meta in order_item.metas:
            print('    {0.meta_key}: {0.meta_value}'.format(meta))
Ich habe die Klassennamen mal so geschrieben wie man das erwarten würde, MixedCase und *einzahl*, weil die Klasse jeweils *ein* Exemplar beschriebt und kein Containertyp ist. Ich würde auch die Attribute anders bennenn, so das sie aus Programmsicht Sinn machen und nicht so viel redundatente Information enthalten. `WooOrderMeta.meta_key`, `order_item.order_item_name`, `meta.meta_value` schreibt und liest sich irgendwie nicht schön und die Wiederholungen von Namensteilen machen in einer Sprache mit Namensräumen keinen Sinn.
Antworten