Listen in SQLAlchemy

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
sonovice
User
Beiträge: 10
Registriert: Sonntag 17. Februar 2013, 20:06

Hallo z'ammen,

ich arbeite mich gerade ein wenig in ORM mit SQLAlchemy ein und stehe vor folgendem Problem:
Es existiert eine Klasse "Mikrofon", die folgende SQL-Spalten enthalten soll:
  • Hersteller
  • Typbezeichnung
  • Richtcharakteristik
Leider gibt es Mikrofone, die umschaltbare Richtcharakteristiken bereitstellen. Wie könnte man soetwas geschickt und "ORM-mäßig" verpacken? "List" wird als Datentyp offensichtlich nicht von SQLAlchemy unterstützt.

Ich befürchte, dass das eine völlige Grundlagenfrage ist, aber es ist schwierig, nach vernünftigen Stichworten diesbezüglich zu googlen...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Das hat imho erst einmal nichts mit dem ORM zu tun, sondern eher mit der grundlegenden *relationalen* Modellierung.

Wenn ich das richtig verstehe, bilden "Hersteller" und "Typbezeichnung" einen PK, um ein Mikrofon zu definieren. Nun haben aber nicht alle Mikrofone nur *einen* Wert "Richtercharacteristik", sondern theoretisch beliebig viele? Richtig?

Diese Charakteristiken sind für jedes Mikrofon genommen dann eindeutig? Wenn ja, könntest Du einfach die Charakteristik zusätzlich in Deinen PK aufnehmen, um eine eindeutig identifizierbaren Datensatz zu schaffen. Wenn Du dann eine Query nur über "Hersteller" und "Typ" als Bedingung durchführst, erhältst Du einfach alle Charakteristiken eines Modells.

Allerdings ist dann die Frage, ob noch anderen, einzigartige Datensätze pro Mikrofon hinzukommen... wenn ja, sollten diese schon in eine separate Tabelle ausgelagert werden. Denn sonst hast Du Redundanzen in Deinem Modell. Sprich, eine Tabelle "Produkt" und eine "Charakterisken". In ersterer stehen nur die einzigartigen Eigenschaften eines Mikrofons, in der zweiten nur diese "Richtercharakteristiken" (ergo das, was ich im Absatz zuvor beschrieben habe ;-) )
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sonovice
User
Beiträge: 10
Registriert: Sonntag 17. Februar 2013, 20:06

Vielen Dank für deine Antwort. Ich versuche mal, das etwas präziser zu formulieren:

In der Welt der Musikaufnahme gibt es nur eine handvoll sinnvoller Richtcharakteristika:
  • Niere
  • Breite Niere
  • Superniere
  • Hyperniere
  • Kugel
  • Acht
  • Keule
Einem Mikrofon kann entweder nur eine dieser 7 Optionen zugeordnet sein oder eben mehrere.

Funktional soll am Ende eine Datenbank herauskommen, die sich sowohl nach Hersteller als auch Charakteristik filtern lassen soll. Ich hätte jetzt als PK einfach eine ID verteilt, die Hersteller als eigene Tabelle mit entsprechender Relation zu den Mikros gemacht und den Typen als einfache String-Column. Nur bei den Charakteristiken bin ich mir unschlüssig...
BlackJack

@sonovice: Das könnte man auf diese Tabellen runterbrechen:

Code: Alles auswählen

hersteller (id serial, name text)
mikrofon (id serial, hersteller_id integer, typ text)
richtcharakteristika (id serial, name text)
mikrofon_charakteristika (mikrofon_id integer, richtcharakteristika_id integer)
sonovice
User
Beiträge: 10
Registriert: Sonntag 17. Februar 2013, 20:06

BlackJack hat geschrieben:@sonovice: Das könnte man auf diese Tabellen runterbrechen [...]
Oh man, ich sollte wegkommen von meinem reinen "Klassendenken" und noch ein paar Nachhilfestunden in relationalen Datenbanken nehmen... Vielen Dank, so ergibt das natürlich erstaunlich einfach Sinn.
sonovice
User
Beiträge: 10
Registriert: Sonntag 17. Februar 2013, 20:06

So, nur um kurz sicherzustellen, dass ich keinen Unfug baue und später Stunden zum Debuggen vergehen:
Ist folgendes korrekt?

Code: Alles auswählen

class Manufacturer(Base):
    __tablename__ = 'manufacturer'

    id = Column(Integer, primary_key=True)
    name = Column(String)


class Directivity(Base):
    __tablename__ = 'directivity'

    id = Column(Integer, primary_key=True)
    name = Column(String)


class Microphone(Base):
    __tablename__ = 'microphone'

    id = Column(Integer, primary_key=True)
    manufacturer_id = Column(Integer, ForeignKey('manufacturer.id'))
    comment = Column(String)


class MicrophoneDirectivity(Base):
    __tablename__ = 'microphone_directivity'

    id = Column(Integer, primary_key=True)
    microphone_id = Column(Integer, ForeignKey('microphone.id'))
    directivity_id = Column(Integer, ForeignKey('directivity.id'))
Besonders der PK ganz am Schluss bereitet mir noch etwas Kopfzerbrechen. Und fehlen nicht noch Relationen, um z.B. nach Hersteller bzw. Charakteristik zu filtern?


EDIT: Thema backreferences - Die hab' ich komplett weggelassen, merke ich gerade...
BlackJack

Das letzte würde man nicht durch eine Klasse modellieren, denn es ist ja eigentlich nur die Verbindung zwischen zwei Tabellen. Schau Dir mal das hier an: http://docs.sqlalchemy.org/en/rel_0_8/o ... ny-to-many
sonovice
User
Beiträge: 10
Registriert: Sonntag 17. Februar 2013, 20:06

Okay, also hier meine Überarbeitung:

Code: Alles auswählen

microphone_directivity_association = Table('association', Base.metadata,
                                           Column('microphone_id', Integer, ForeignKey('microphone.id')),
                                           Column('directivity_id', Integer, ForeignKey('directivity.id')))


class Manufacturer(Base):
    __tablename__ = 'manufacturer'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    microphones = relationship('Microphone', backref='manufacturer')

    def __init__(self, name):
        self.name = name


class Microphone(Base):
    __tablename__ = 'microphone'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    comment = Column(String)

    manufacturer_id = Column(Integer, ForeignKey('manufacturer.id'))
    directivities = relationship('Directivity',
                                 secondary=microphone_directivity_association,
                                 backref='microphones')

    def __init__(self, name, comment=''):
        self.name = name
        self.comment = comment


class Directivity(Base):
    __tablename__ = 'directivity'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    def __init__(self, name):
        self.name = name
Falls noch Schnitzer drin sein sollten, würde ich mich über Kritik freuen.
Antworten