Warum braucht es SQLAlchemy?

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Warum stellen die DB-Macher die objektorientierte Funktionalität eigentlich nicht einfach selbst zur Verfügung? :K
BlackJack

@meego: Welcher DB-Macher stellt denn dann genau was zur Verfügung? Die schaffen es ja nicht einmal SQL einheitlich anzubieten.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Gibt es keine objektorientiert ansprechbare Datenbank in der Welt? Oder wenigsten ein Tool dass das Schema in Pythoncode übersetzt?

SQLAlchemy scheint ziemlich kompliziert, aber ich werde mich weiter einlesen.. Immerhin konnte ich jetzt schon ein paar Datensätze in eine Tabelle schreiben. (Wenn auch erst bloss mit normalem SQL und ohne SQLAlchemy).
BlackJack

@meego: Es gibt natürlich auch OOP-Datenbanken. Das sind dann aber eben keine SQL-Datenbanken. Und es gibt auch SQL-Datenbanken die selbst schon zumindest Ansätze einer OOP-Abstraktionsschicht bieten. Da hat dann aber jeder Hersteller der das macht sein eigenes Konzept.

Das Schema automatisch übersetzen kann man mit SQLAlchemy (SA) denn die Bibliothek bietet für die meisten DBMS auch ”reflection”. Da braucht kein Python-Code erzeugt zu werden, denn das passiert dynamisch zur Laufzeit. Allerdings möchte man beim ORM normalerweise selbst die Kontrolle haben wie, was abgebildet wird. Da kann man ja eine ganze Menge Feinheiten manuell einstellen und man möchte oft auch zusätzliche Funktionalität in die Modelle programmieren.

Selbst ohne das ORM ist SQLAlchemy nützlich um Unterschiede zwischen verschiedenen DBMS und DB-API V2 konformen Modulen auszubügeln.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
meego hat geschrieben:SQLAlchemy scheint ziemlich kompliziert, aber ich werde mich weiter einlesen.. Immerhin konnte ich jetzt schon ein paar Datensätze in eine Tabelle schreiben. (Wenn auch erst bloss mit normalem SQL und ohne SQLAlchemy).
SA scheint kompliziert, weil es mächtig ist - das Grundprinzip ist aber nicht wirklich kompliziert. Zumal die ja bei SA nicht zwingend den ORM-Teil nutzen musst. Du kannst ja z.B. auch "nur" das Connection Pooling benutzen, um die Verbindung zur SQL-DB herzustellen.

Der ORM von Django ist übrigens, zumindest für den Einsteig, IMHO viel simpler und für den Einstieg einfacher. Wobei SA AFAIK deutlich mächtiger ist - ob du das dann brauchst oder nicht musst du selber entscheiden :-)

Gruß, noisefloor
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Es führt also wohl kein Weg daran vorbei, die Tabellenklassen von Hand aufgrund vom Diagramm-Design abzutippen und dann hoffentlich nix zu vergessen.

@noisefloor: Kann man bei Django auch SA verwenden?
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Es führt also wohl kein Weg daran vorbei, die Tabellenklassen von Hand aufgrund vom Diagramm-Design abzutippen und dann hoffentlich nix zu vergessen.
Genau - du musst dein DB-Modell in Python abbilden.
@noisefloor: Kann man bei Django auch SA verwenden?
Du kannst innerhalb von Django natürlich auch andere Module verwenden als die von Django, wie z.B. ein anderes ORM oder eine andere Template-Engine. Ob das für dich Vorteile hat kannst nur du entscheiden.

Ein älterer, aber ganz guter Blogpost zu SA und Django ist z.B. im Blog von Armin Ronacher zu finden: http://lucumr.pocoo.org/2011/7/19/sqlachemy-and-you/.

Gruß, noisefloor
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Hier ein Beispielscript zur Erstellung einer DB, welches ich im Web gefunden habe:

Code: Alles auswählen

import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import [b]declarative_base[/b]
from sqlalchemy.orm import [b]relationship[/b]
from sqlalchemy import create_engine
 
[b]Base = declarative_base()[/b]
 
class Person(Base):
    __tablename__ = 'person'
    # Here we define columns for the table person
    # Notice that each column is also a normal Python instance attribute.
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
 
class Address(Base):
    __tablename__ = 'address'
    # Here we define columns for the table address.
    # Notice that each column is also a normal Python instance attribute.
    id = Column(Integer, primary_key=True)
    street_name = Column(String(250))
    street_number = Column(String(250))
    post_code = Column(String(250), nullable=False)
    person_id = Column(Integer, ForeignKey('person.id'))
    [b]person = relationship(Person)[/b]
 
# Create an engine that stores data in the local directory's
# sqlalchemy_example.db file.
engine = create_engine('sqlite:///sqlalchemy_example.db')
 
# Create all tables in the engine. This is equivalent to "Create Table"
# statements in raw SQL.
Base.metadata.create_all(engine)
Was ist declarative_base()? Aus der Doku werde ich noch nicht schlau. Die DB selber muss die Engine sein. Ist Base also einfach eine Art übergeordnetes Klammerobjekt für alle Alchemyklassen?
Warum die Beziehung irgendwie definiert werden muss, verstehe ich auch noch nicht (warum reicht der ForeignKey nicht?).
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@meego: irgendwo mußt Du doch angeben, dass person die Relationship zur Klasse Person ist. Und das Datenbankdesign mußt Du natürlich irgendwo eingeben, ob das nun als "CREATE TABLE" oder als Python-Klasse ist, ist doch egal, wobei Python-Klassen bequemer zu tippen sind.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@meego: hast du das SQLAlchemy Tutorial gelesen? Da ist im 3. Absatz erklärt, wozu `declarative_base()` gut ist.

Gruß, noisefloor
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

@sirius: Meine Überlegung war folgende: Da Column() ein Objekt von Alchemy ist, könnte es für Alchemy vielleicht möglich sein, den ForeignKey Python irgendwie im Hintergrund mitzuteilen. Aber die technischen Hintergründe sind mir unbekannt.

@Noisefloor: Noch nicht. Ich habe zwei andere Kurztutorials gelesen. Aber Danke für den Hinweis, das ist überraschend verständlich geschrieben.

Verwendet Ihr denn auch noch "__init__" für die Klassen?
BlackJack

@meego: Das mit dem `ForeignKey` automatisch mitteilen habe ich nicht verstanden. Was sollte denn da Deiner Meinung nach automatisch passieren? Wie sollte der Name entstehen? Woher soll SA nur aus der `Column()` mit dem `ForeignKey` wissen um welche Art von Beziehung es sich handelt? Man will ja nicht immer so etwas simples wie ``person = relationship(Person)`` da stehen haben.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Aus dem ForeignKey selbst ist nicht zu ermitteln ob dies eine One-To-One oder Many-To-One Beziehung ist. Bei einer Many-To-One Beziehung ist es nicht möglich zu ermitteln welche Datenstruktur verwendet werden soll: Liste, set, irgendwas anderes?

relationship() erlaubt dir diese Informationen die das Schema nicht beschreiben kann, SQLAlchemy mitzuteilen.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

So wie ich es verstanden habe, kann eine Person mehrere Adressen haben (da der ForeignKey in der Adresstabelle liegt). Über den ForeignKey könnte man dann immer den Namen der Person ermitteln. Falsch?

Das mit Liste + Dictionary habe ich nicht verstanden.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@meego: richtig. Aber viele Abhängigkeitsvarianten lassen sich in einer Hochsprache viel präziser ausdrücken. Den Unterschied zwischen einer 1:1- oder einer 1:n-Beziehung lassen sich mit Foreign-Keys alleine nicht ausdrücken. Du willst eine 1:n-Beziehung haben, in anderen Tabellen ist das vielleicht anders.
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Ich habe mal etwas zusammen gecoded. Die User können sich Nachrichten schicken. Wie wäre die Beziehung hier zu definieren? Sonstige Kritik?:

Code: Alles auswählen

import os
import sys
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlaclhemy import create_engine

Base = declarative_base
#datenbankblaupausen
class User(Base):
	__tablename__ = 'user'
	#Columns for the table user
	id = Column(Integer, primary_key=True)
	vorname = Column(String(80), nullable=False)
	name = Column(String(80), nullable=False)
	email = Column(String(80), index=True, nullable=False)
	strasse = Column(String(80), nullable=False)
	plz = Column(String(80), nullable=False)
	ort = Column(String(80), nullable=False)
	password_hash = Column(String(500), nullable=False)
	salt = Column(String(250), nullable=False)
	id_messages = Column(Integer, ForeignKey('message.id'))

class Messages(Base):
	id = Column(Integer, primary_key=True)
	zeitstempel = Column(TIMESTAMP, nullable=False)
	gelesen = Column(Boolean, nullable=False)
	betreff = Column(String(250), nullable=False)
	text = Column(String, nullable=False)
BlackJack

@meego: Ich würde mal sagen das funktioniert so nicht. Schreib doch mal ein paar konkrete Datensätze auf ein Blatt Papier (oder in eine Textdatei) mit sagen wir mal zwei Benutzern die sich gegenseitig ein paar Nachrichten geschickt haben.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

jeder User kann N Messages schicken und jede Message hat genau einen (1) User (=Absender) und einen (?) Empfänger. Das spiegelt dein Entwurf nicht wieder.

Gruß, noisefloor
meego
User
Beiträge: 380
Registriert: Montag 4. März 2013, 14:36

Hallo. Ist es so besser?:

Bild

Code: Alles auswählen

class User(Base):
	__tablename__ = 'user'
	#Columns for the table user
	id = Column(Integer, primary_key=True)
	vorname = Column(String(80), nullable=False)
	name = Column(String(80), nullable=False)
	email = Column(String(80), index=True, nullable=False)
	strasse = Column(String(80), nullable=False)
	plz = Column(String(80), nullable=False)
	ort = Column(String(80), nullable=False)
	password_hash = Column(String(500), nullable=False)
	salt = Column(String(250), nullable=False)
	id_messages = Column(Integer, ForeignKey('message.id'))

class Messages(Base):
	id = Column(Integer, primary_key=True)
	zeitstempel = Column(TIMESTAMP, nullable=False)
	empfaenger = Column(Integer, ForeignKey('user.id'))
	sender = Column(Integer, ForeignKey('user.id'))
	gelesen = Column(Boolean, nullable=False)
	betreff = Column(String(250), nullable=False)
	text = Column(String, nullable=False)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@meego:
fast richtig - das `id_messages = Column(Integer, ForeignKey('message.id'))` in User macht keinen Sinn, statt dessen ist es sinnvoll, in User 2 (Rück-)Relationen anzulegen - eine für empfangene und eine für gesendete Nachrichten.
Antworten