{"id":257,"date":"2008-12-22T22:29:18","date_gmt":"2008-12-22T20:29:18","guid":{"rendered":"http:\/\/robert.kolatzek.org\/wblog\/?p=257"},"modified":"2008-12-22T22:29:18","modified_gmt":"2008-12-22T20:29:18","slug":"relationale-datenbanken-fur-objekt-orientiert-geschadigte","status":"publish","type":"post","link":"https:\/\/blog.kolatzek.org\/wblog\/257\/relationale-datenbanken-fur-objekt-orientiert-geschadigte","title":{"rendered":"Relationale Datenbanken f\u00fcr Objekt-orientiert-Gesch\u00e4digte"},"content":{"rendered":"<p>Es gibt auf dieser Welt Entwickler, die von Oben nach Unten, von A nach Z oder von Pontius zum Pilatus programmieren. Man reiht die abzuarbeitenden Aufgaben als Code-Schnipsel hintereinander ein&#8230;<\/p>\n<p>Dann gibt es noch die anderen Entwickler, die mit Java oder \u00e4hnlichen Sprachen gro\u00df geworden sind. Ohne &#8222;class&#8220;, &#8222;package&#8220; und &#8222;import&#8220; l\u00e4uft da gar nichts&#8230; Sie bilden Ihre Welt mit Hilfe von Klassen ab und &#8222;verwirklichen&#8220; sie, indem sie davon Objekte ableiten. Dabei kann ein Objekt andere Objekte beinhalten.<\/p>\n<p>Im Vergleich zu einem solchen Denksystem ist ein Haufen von Tabellen mit Spalten etc flach wie die Welt von Lactantius (seine Philosophie teilte die katholische Kirche noch nie &#8211; auch wenn die &#8222;Halbgebildeten&#8220; das immer behaupten). Wie soll man da Abhilfe schaffen? Mit einem <a title=\"SQLObject - SQL und doch Objekte\" href=\"http:\/\/www.sqlobject.org\/\" target=\"_self\">SQLObject<\/a>!<!--more--><\/p>\n<p>SQLObject ist ein sog. objektrelationale Wrapper. Das bedeutet: man leitet von dieser Klasse eigene Klassen ab &#8211; beschreibt dabei nicht nur die Eigenschaften der Klasse selbst, sondern auch ihre Zusammenh\u00e4nge untereinander: die Relationen. SQLObject ist f\u00fcr Python geschrieben und erm\u00f6glich die Verwendung mehrerer relationalen Datenbanken, ohne SQL-Kenntnisse. SQLObject ist der integrale Bestandteil von <a title=\"turbogears - front to back web development framework\" href=\"http:\/\/turbogears.org\/\">Turbogears<\/a>, dem Framework f\u00fcr &#8211; wie es so sch\u00f6n hei\u00dft &#8211; rapid web development. (Insgesamt ist Turbogears mit seinem Prinzip &#8222;Front to back development&#8220; sehr interessant &#8211; die genaue Umkehrung des \u00fcblichen Vorgehens in der Entwicklung: ich wei\u00df, was rauskommen soll und fange damit an!)<\/p>\n<p>Die heilige Kuh &#8211; die Import-Anweisung l\u00e4sst sich nicht umgehen, deshalb:<\/p>\n<pre>from sqlobject import *<\/pre>\n<p>damit python und SQLObject wissen, was die Datenbank ist (an der gearbeitet wird), muss sie angegeben werden. Dies geschieht mit einem String wie diesem:<\/p>\n<pre class=\"literal-block\">scheme:\/\/[user[:password]@]host[:port]\/database[?parameters]<\/pre>\n<p>Es erkl\u00e4rt sich fast von selbst. Als schema wird eine der m\u00f6glichen Datenbanktypen bezeichnet: <tt class=\"docutils literal\"><span class=\"pre\">sqlite<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">mysql<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">postgres<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">firebird<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">interbase<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">maxdb<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">sapdb<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">mssql<\/span><\/tt>, <tt class=\"docutils literal\"><span class=\"pre\">sybase<\/span><\/tt>. Ganz sch\u00f6n viele oder?<\/p>\n<pre>connection = connectionForURI(connection_string)\nsqlhub.processConnection = connection<\/pre>\n<p>stellt die Verbindung her. Jetzt k\u00f6nnen wir anfangen!<\/p>\n<p>SQLObject stellt und einige Datentypen zur Verf\u00fcgung, die er intern auch auf die richtige Spaltentypen f\u00fcr die verwendete Datenbank (schema) abbildet. M\u00f6glich sind:<\/p>\n<ul>\n<li>StringCol<\/li>\n<li>UnicodeCol (wenn man nicht nur latainisch \/ englisch schreiben will)<\/li>\n<li>IntCol (f\u00fcr Ganzzahlen)<\/li>\n<li>FloatCol (f\u00fcr Zahlen mit Komma)<\/li>\n<li>EnumCol (f\u00fcr Aufz\u00e4hlungen &#8211; eine begrenzte Auswahl)<\/li>\n<li>DateCol (Datum = ein datetime-Objekt)<\/li>\n<li>TimeCol (Uhrzeit  = ein datetime-Objekt)<\/li>\n<li>DateTimeCol (f\u00fcr Datum und Zeit = ein datetime-Objekt)<\/li>\n<li>BoolCol (wahr oder nicht?)<\/li>\n<\/ul>\n<p>Andere sind an ein bestimmtes Schema gebunden (wie BLOBCol bei MySQL, PostgreSQL und SQLite oder CurrencyCol bzw. DecimalCol f\u00fcr Preise &#8211; 10-Stellig mit 2 Stellen hinter&#8217;m Komma) und sind hier irrelevant. Interessant sind vielmehr:<\/p>\n<ul>\n<li>ForeignKey<\/li>\n<li>SingleJoin<\/li>\n<li>MultipleJoin<\/li>\n<li>RelatedJoin<\/li>\n<\/ul>\n<p>Die Grundtypen k\u00f6nnen wir weiter begrenzen indem wir in den Klammern z. B. die L\u00e4nge begrenzen:<\/p>\n<pre>   name = UnicodeCol(length=100)<\/pre>\n<p>Wenn wir unsere Autos beschreiben wollten, m\u00fcssten wir schon mal in Klassen denken, um SQL-Objekte zu bekommen. Deshalb fangen wir an.<\/p>\n<pre>class Auto(SQLObject):\n    marke = StringCol()\n    typ = StringCol(length=50, alternateID=True)\n    werke = MultipleJoin('Werk')\n    farben = RelatedJoin('Farbe')\n    sprit = RelatedJoin('Sprit')\n    logo = ForeignKey('Logo')<\/pre>\n<p class=\"doctest-block\">Was ist dazu zu sagen? Nun: Ein Auto-Typ hat immer eine Bezeichnung, kann aber in mehreren Werken produziert werden (mehr als ein Werk m\u00f6glich: MultipleJoin). Trotzdem besitzt es immer das EINE gleiche Logo (aber das Logo wird auf mehreren Typen desselben Herselelers zu sehen sein) &#8211; deshalb verlangt der Autotyp nach einem schon existierendem Logo. Farbe und Sprit sind ebenso wie Werk oder Logo ganz eigene Klassen, nur das ihre Objekte (rot, gr\u00fcn, Diesel, Benzin) von mehreren Autotypen verwendet werden &#8211; aber auch die Autotypen k\u00f6nnen potenziell mehr als eine Farbe oder Spritart eigen haben. Eine solche Relation (m-zu-n = auf beiden Seiten die Zahl kann h\u00f6her als 1 sein) wird durch RelatedJoin (auf beiden Seiten) realisiert: eine Farbe kann mehreren Objekten des Typs Auto zugeordnet werden, so wie Auto mehreren Objekten des Typs Farbe zugeordnet werden kann:<\/p>\n<pre class=\"doctest-block\">class Farbe(SQLObject):\n    name = UnicodeCol()\n    autos = RelatedJoin('Auto')<\/pre>\n<p>Man gibt in Klammern (und Anf\u00fchrungszeichen) den Namen der anderen Klasse an. Das ist die ganze Magie! (Genauso sieht auch die Sprit-Klasse aus)<\/p>\n<pre>class Logo(SQLObject):\n\u00a0\u00a0\u00a0 beschreibung = UnicodeCol()\n\u00a0\u00a0\u00a0 autos = MultipleJoin('Auto')<\/pre>\n<p>Und schon ist eine 1-zu-m-Relation realisiert!<\/p>\n<p>Wenn man der Einfachheit halber annimmt, dass ein Werk nur einen Typ produziert, stimmt unser Entwurf, der um &#8222;Werk&#8220; vervollst\u00e4ndigt wird.<\/p>\n<pre>class Werk(SQLObject):\n    ort = UnicodeCol()\n    plz = IntCol()\n    erbaut = DateCol()\n    auto = ForeignKey('Auto')<\/pre>\n<p>Eines ist uns entgangen: &#8222;Typ&#8220; ist so eindeutig, dass sie fast als ID dienen k\u00f6nnte. Doch das haben wir schon ber\u00fccksichtigt: alternateID=True besagt, dass es nicht nur einfach &#8218;unique&#8216; ist. Nur deshalb k\u00f6nnen wir ein Objekt abrufen indem wir sagen: Auto.byTyp(&#8218;Golf&#8216;)&#8230;<\/p>\n<h4>Apropos: wie kriegt man die denn die Objekte aus der Datenbank wieder heraus?<\/h4>\n<p>Welche Objekte? Wir haben die Tabellen noch gar nicht erstellt! Das erledigen wir schnell. Zuerst die Klassen, von denen die anderen Abh\u00e4ngen:<\/p>\n<pre>Farbe.createTable()\nSprit.createTable()\nWerk.createTable()\nLogo.createTable()\nAuto.createTable()<\/pre>\n<p>Fertig!<\/p>\n<p>Jetzt die Bef\u00fcllung mit Werten:<\/p>\n<pre>l1 = Logo(beschreibung=\"Vier Sschr\u00e4gstriche\")\na1 = Auto(marke=\"Fiat\", typ=\"500\", logo=l1)\nw1 = Werk(ort=\"Rom\", plz=1111, erbaut=DateTimeCol.now())\na1.werk=w1\nf1 = Farbe(name=\"rot\")\nl2=Logo(beschreibung=\"Vier Kreise\")\na2 = Auto(marke=\"Audi\", typ=\"A4\", logo=l2)\n# relationen: Rot k\u00f6nnen Audi's und Fiat's sein, grau nur Audi's\nf1.addAuto(a1)\nf1.addAuto(a1)\nf2 = Farbe(name=\"grau\")\na2.addFarbe(f2)\n# Sorry, rote Audi's gibt es nicht!\na2.removeFarbe(f1)<\/pre>\n<p>Das ist neu! Man kann kein Auto-Objekt ohne Logo erzeugen. Daf\u00fcr gibt es von Anfang an addX und removeX (wobei X eine MultipleJoin-Relation ist d.h. ein Auto kann mehrere Farben haben oder mehrere Sprit-Arten Fahren). Und man muss nicht ausdr\u00fccklich speichern. Das geschieht automatisch! Will man etwas loswerden, sagt man a&#8217;d\u00e9l zum Objekt: &#8222;del a2&#8220;.<\/p>\n<p>Jetzt fassen wir einige Zeilen zusammen und machen Fiat in einem Schritt in grau und rot verf\u00fcgbar:<\/p>\n<pre>a2.farbe=(a1,a2)<\/pre>\n<p>Einfach oder? Noch einfacher kann man etwas Abrufen:<\/p>\n<pre>myAuto = Auto.get(1)<\/pre>\n<pre>myAuto.farbe\n(&lt;Farbe 1 name=u'rot'&gt;, &lt;Farbe 2 name=u'grau'&gt;)<\/pre>\n<p>Ist das nicht toll?! Ich habe in &#8222;myAuto&#8220; einen Fiat 500 reingeholt. Wenn ich nicht w\u00fcsste, welche ID so ein Fiat 500 hat k\u00f6nnte ich alternativ sagen:<\/p>\n<pre>myAuto=Auto.byTyp(\"500\")<\/pre>\n<p>weil &#8222;typ = StringCol(length=50, alternateID=True)&#8220; eine alternative ID darstellt: einen Golf gibt es nur von VW!<\/p>\n<p>Um die Datenbank nicht zu stark zu belasten, wenn ein Objekt mit vielen Abh\u00e4ngigkeiten herausgeholt wird, haben sich die schlauen Jungs die sog. &#8222;lazy updates&#8220; einfallen lassen. Ein Objekt des Typs Farbe wird passend zum Auto erst dann aus der Datenbank gelesen, wenn es auch wirklich ben\u00f6tigt wird. Will man andere Kriterien nutzen, setzt man select() ein:<\/p>\n<pre>fiats=Auto.select(Auto.q.marke=\"Fiat\")<\/pre>\n<pre>audisRot=Auto.select(AND(Auto.q.marke=\"Audi\", Auto.q.farbe=f2))<\/pre>\n<p>So bekommt man alle Fiats bzw. alle Audi-Typen, die grau (entspricht dem Objekt f2) lackiert werden. Ist das nicht klasse? Es geht aber noch besser. Mit:<\/p>\n<pre>Auto.select(Auto.q.marke=\"Fiat\",\n\u00a0  extraColumns = [func.COUNT(Auto.q.id)],\n  \u00a0groupBy = Auto.q.farbe,\n   orderBy = [DESC(func.COUNT(Auto.q.werk)),Auto.q.Sprit])<\/pre>\n<p>Damit bekommen wir alle Fiats, gruppiert nach dem selben Angebot an Farben (rot und grau), geordnet absteigend nach (1.) Anzahl der produzierenden Werke und (2.) Sprit-Typen (&#8222;Diesel&#8220; kommt hier nat\u00fcrlich vor &#8222;Benzin&#8220;!).<\/p>\n<h3>Fazit<\/h3>\n<p>Mit SQLObject kriegt man schnell und einfach Objekte aus der DB heraus (oder dort hinein), die man sofort woanders (in HTML-Templates?) verwenden kann. Es mag hier und da langsamer sein als pures SQL. Doch so Backend-unabh\u00e4ngig und genial einfach war das Leben in einer Datenbanken-Welt noch nie!<\/p>\n<p>Es ist egal, welchen Schaden man im Laufe des Lebens abbekommen hat (wie z. B. hier die Objekt-Orientierung) oder ob man die relationale Welt verstehen kann oder nicht. Weiterbildung und offene Augen daf\u00fcr, was andere bereits geleistet haben, erspart viel Arbeit und seltsame bzw. veraltete L\u00f6sungen.<\/p>\n<p><em>Wenn Sie noch Fragen haben, dann scheiben Sie sie einfach in das Kommentarfeld rein. &#8222;Wer anklopft, dem wird aufgetan!&#8220;<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Es gibt auf dieser Welt Entwickler, die von Oben nach Unten, von A nach Z oder von Pontius zum Pilatus programmieren. Man reiht die abzuarbeitenden Aufgaben als Code-Schnipsel hintereinander ein&#8230; Dann gibt es noch die anderen Entwickler, die mit Java oder \u00e4hnlichen Sprachen gro\u00df geworden sind. Ohne &#8222;class&#8220;, &#8222;package&#8220; und &#8222;import&#8220; l\u00e4uft da gar nichts&#8230; [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_sitemap_exclude":false,"_sitemap_priority":"","_sitemap_frequency":"","ocean_post_layout":"","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"","ocean_second_sidebar":"","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"","ocean_custom_header_template":"","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"","ocean_menu_typo_font_family":"","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"","ocean_post_oembed":"","ocean_post_self_hosted_media":"","ocean_post_video_embed":"","ocean_link_format":"","ocean_link_format_target":"self","ocean_quote_format":"","ocean_quote_format_link":"post","ocean_gallery_link_images":"on","ocean_gallery_id":[],"footnotes":""},"categories":[8],"tags":[52,72,74,89,101,126],"class_list":["post-257","post","type-post","status-publish","format-standard","hentry","category-software","tag-internet","tag-mysql","tag-opensource","tag-python","tag-software-co","tag-web","entry"],"_links":{"self":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/posts\/257","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/comments?post=257"}],"version-history":[{"count":0,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/posts\/257\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/media?parent=257"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/categories?post=257"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/tags?post=257"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}