Daten von XML importieren mit SQLalchemy/Elixir

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hi, ich erstelle einen Datensatz für eine Datenbank in XML und möchte das mit SQLAlchemy bzw Elixir in eine Datenbank importieren. Der erste Datensatz besteht nur aus Tags mit Attributen

Code: Alles auswählen

<?xml version="1.0"?>
<TAGS>
    <tag ttype="k" tname="Mikrocontroller" tproperty="Kategorie" />
    <tag ttype="t" tname="Controller" tproperty="Obergruppe" />
    <tag ttype="t" tname="Bausatz" tproperty="Obergruppe" />
    <tag ttype="t" tname="Controller Set" tproperty="Obergruppe" />
    <tag ttype="t" tname="Prototype Board" tproperty="Obergruppe" />
    <tag ttype="t" tname="USB" tproperty="Obergruppe" />
</TAGS>
Wenn ich das mit lxml lese erhalte ich für jeden Tag ein Dictionary.

Code: Alles auswählen

{'tname': 'Mikrocontroller', 'ttype': 'k', 'tproperty': 'Kategorie'}
{'tname': 'Controller', 'ttype': 't', 'tproperty': 'Obergruppe'}
{'tname': 'Bausatz', 'ttype': 't', 'tproperty': 'Obergruppe'}
{'tname': 'Controller Set', 'ttype': 't', 'tproperty': 'Obergruppe'}
{'tname': 'Prototype Board', 'ttype': 't', 'tproperty': 'Obergruppe'}
{'tname': 'USB', 'ttype': 't', 'tproperty': 'Obergruppe'}
Kann man das Dictionary direkt an SQLAlchemy übergeben? Attributsnamen und die Namen der Tabellenspalten stimmen überein.

Mein Versuch sieht so aus. Ich bekomme aber eine Fehlermeldung

Code: Alles auswählen

def import_tags_from_xml():
    f = open('shop/model/tags.xml', 'r')
    tree = etree.parse(f)
    root = tree.getroot()
    for child in root:
        attr = Tag(child.attrib)
        session.add(attr)
    session.commit()
Traceback (most recent call last):
File "test.py", line 66, in <module>
tags.import_tags_from_xml()
File "/home/burli/dev/flask/shop1/shop/model/tags.py", line 124, in import_tags_from_xml
attr = Tag(child.attrib)
File "/usr/local/lib/python2.7/dist-packages/Elixir-0.7.1-py2.7.egg/elixir/entity.py", line 829, in __call__
return type.__call__(cls, *args, **kwargs)
File "<string>", line 4, in __init__
File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.6.5-py2.7.egg/sqlalchemy/orm/state.py", line 105, in initialize_instance
return manager.events.original_init(*mixed[1:], **kwargs)
File "/usr/local/lib/python2.7/dist-packages/Elixir-0.7.1-py2.7.egg/elixir/entity.py", line 42, in __init__
old_init(self, *args, **kwargs)
TypeError: __init__() takes exactly 1 argument (2 given)
Ich weiß jetzt nicht, ob das ein Problem von Elixir ist oder generell mit SQLAlchemy auf diesem Weg nicht geht. Gibt es da eine direkte Möglichkeit?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Tag ist Deine Mapping-Klasse?

Dann sollte es so gehen:

Code: Alles auswählen

attr = Tag(**child.attrib)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Wow, nur zwei Sternchen daneben. Gar nicht mal schlecht für einen Noob :D

Danke, hab in der SQLALchemy Doku echt nix gefunden.

Eine Frage noch dazu: kann man damit auch Relationen zu anderen Tabellen herstellen?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

burli hat geschrieben: Danke, hab in der SQLALchemy Doku echt nix gefunden.
Das ist ja auch Python(Basis-)Wissen! Lies dazu mal im Tutorial ab Abschnitt 4.7.2 nach.
Eine Frage noch dazu: kann man damit auch Relationen zu anderen Tabellen herstellen?
Ich kapiere da Deine Frage nicht wirklich? Da müßtest Du mal näher ausformulieren / ein Beispiel geben, was Du damit meinst.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hyperion hat geschrieben:Das ist ja auch Python(Basis-)Wissen! Lies dazu mal im Tutorial ab Abschnitt 4.7.2 nach.
Mhm, an der falschen stelle gesucht. Ok, ich bin halt noch kein Experte. Viele Sachen hab ich sicher schonmal gesehen, aber sind mangels Praxis noch nicht hängen geblieben. Ich übe noch. Danke für den Hinweis, werde es lesen.
Ich kapiere da Deine Frage nicht wirklich? Da müßtest Du mal näher ausformulieren / ein Beispiel geben, was Du damit meinst.
Naja, OneToMany oder ManyToMany eben. Die Verknüpfung zwischen zwei Tabellen. Ich schnippel mal ein Beispiel zusammen.

Code: Alles auswählen

class Tag(Entity):
    using_options(tablename="tags")
    
    ttype = Field(Unicode(1))
    tname = Field(Unicode(30))
    tunit = Field(Unicode(10), default="")
    tproperty = Field(Unicode(20), default="")
    articles = ManyToMany('Article')

class Article(Entity):
    using_options(tablename="articles")
    match = Field(Unicode(30), unique=True)
    tags = ManyToMany('Tag')

hAtmel = Tag(ttype="h", tname="Atmel", tproperty="Hersteller")
kMikrocontroller = Tag(ttype="k", tname="Mikrocontroller", tproperty="Kategorie")
tAVR = Tag(ttype="t", tname="AVR")
tATmega = Tag(ttype="t", tname="megaAVR")

ATmega816PU = Article(match="ATmega8-16PU", \
                    tags=[kMikrocontroller, tAVR, hAtmel, tATmega])

session.commit()
So sieht mein aktueller Daten Import aus. Nur das es derzeit an die 900 Zeilen sind und etliche Teile fehlen. Das möchte ich jetzt mit XML erstellen.

Wie man sieht erstelle ich zunächst die Tags und übergebe dem Article() Objekt dann eine Liste mit entsprechenden Tags. Die Referenz wird dann automatisch erstellt. In dem Beispiel ist es klar, wie es funktioniert. Ich habe ja von jedem Tag eine Instanz

Ich VERMUTE, dass man für die Referenzierung die Tags erstmal wieder aus der Datenbank holen muss, damit man da dann nach den passenden Instanzen suchen kann. Aber ich habe Python ja schon öfter unterschätzt und es geht vielleicht einfacher
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

burli hat geschrieben: In dem Beispiel ist es klar, wie es funktioniert. Ich habe ja von jedem Tag eine Instanz

Ich VERMUTE, dass man für die Referenzierung die Tags erstmal wieder aus der Datenbank holen muss, damit man da dann nach den passenden Instanzen suchen kann. Aber ich habe Python ja schon öfter unterschätzt und es geht vielleicht einfacher
Wenn Du keine Tag-Objekte mehr im Speicher hast, dann müssen die natürlich erst aus der DB geholt werden. Ich kapiere den Zusammenhang zur Parameterübergabe aber immer noch nicht :-D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Die Artikel XML Datei sieht genauso aus wie wie die Tags

Code: Alles auswählen

    <article match="ATmega8-16PU" tags="Mikrocontroller, Atmel, AVR, megaAVR, ATmega8, Controller" />
    <article match="ATmega8-16AU" tags="Mikrocontroller, Atmel, AVR, megaAVR, ATmega8, Controller" />
Dementsprechend kommt dieses Dict heraus

Code: Alles auswählen

{'match': 'ATmega8-16PU', 'tags': 'Mikrocontroller, Atmel, AVR, megaAVR, ATmega8, Controller'}
{'match': 'ATmega8-16AU', 'tags': 'Mikrocontroller, Atmel, AVR, megaAVR, ATmega8, Controller'}
Die Datenbank nimmt für die Tags aber keinen String an, was ja auch logisch ist. Aber ich muss beim Erstellen der Artikel Tabelle ja irgendwie die Verknüpfung zu der Tag Tabelle herstellen.

Im Prinzip bleibt mir nur, die Tag Tabelle zu lesen, die Tags aus den Artikeln zu extrahieren, in der Tag Tabelle suchen und die Referenz an die Artikel Tabelle zu übergeben. Es sei denn, irgendwo kann gezaubert werden
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

burli hat geschrieben: Im Prinzip bleibt mir nur, die Tag Tabelle zu lesen, die Tags aus den Artikeln zu extrahieren, in der Tag Tabelle suchen und die Referenz an die Artikel Tabelle zu übergeben. Es sei denn, irgendwo kann gezaubert werden
Richtig!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

So, es läuft. Nochmal Danke für die Hilfe. Hier mal der Code, falls es noch jemand gebrauchen kann

Code: Alles auswählen

def import_tags_from_xml():
    f = open('shop/model/tags.xml', 'r')
    for child in etree.parse(f).getroot():
        attr = Tag(**child.attrib)
        session.add(attr)
    session.commit()

def import_articles_from_xml():
    f = open('shop/model/article.xml', 'r')
    for child in etree.parse(f).getroot():
        taglist = child.attrib['tags'].split('+')
        tags = Tag.query.filter(Tag.tname.in_(taglist)).all()
        del child.attrib['tags']
        attr = Article(**child.attrib)
        attr.tags = tags
        session.add(attr)
    session.commit()
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Sag mal wieso schließt du die Dateien nicht wieder ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Xynon1 hat geschrieben:Sag mal wieso schließt du die Dateien nicht wieder ?
:oops:
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Naja, mit dem Idiom

Code: Alles auswählen

with open() as handler:
    # handler ist file objekt und wird automatisch geschlossen.
bräuchte man sich nicht drum kümmern ;-)

@burli: Wozu erhoffst Du Dir von:

Code: Alles auswählen

del child.attrib['tags']
?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hyperion hat geschrieben: @burli: Wozu erhoffst Du Dir von:

Code: Alles auswählen

del child.attrib['tags']
?
Der 'tags' Key muss aus dem Dict gelöscht werden. Das ist ja ein String, mit dem die Funktion da nix anfangen kann. Dafür hole ich in der Zeile darüber die Tag Objekte aus der Datenbank. Vielleicht etwas verwirrend, dass ich in beiden Fällen "tags" verwendet hab.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ah... jetzt hab ichs auch gesehen... allerdings würde ich in diesem Falle doch das eine verbliebene Attribut explizit zu übergeben, anstatt den Parse-tree zu verändern.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

auch wieder wahr
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Antworten