Sqlalchemy many to one, oder doch nicht?

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

Hallo, ich hätte da mal ne Frage und zwar würde ich gern wie schon geschrieben wohl ne many to one relationship erstellen (MySQL), hab das allerdings noch nie gemacht und Datenbanken sind nicht ganz so meine Expertise.

Folgendes, ich habe ein table für Artikel und einen für Bestellungen ich würde gerne eine relation haben die mir nach der Bestellung die Details zu den Artikeln rausgibt.

Die tables sehen wie folgt aus:

Code: Alles auswählen

class Artikel(Base):
    __tablename__ = 'artikel'
    data_id = db.Column(db.Integer, primary_key = True)                         
    art_nr = db.Column(db.String(20))
    art_size = db.Column(db.String(20))                           
    art_name = db.Column(db.String(20))                        
    art_group= db.Column(db.String(20))                   
    art_description = db.Column(db.String(20)) 

class Orders(Base):
    __tablename__ = 'orders'
    data_id = db.Column(db.Integer, primary_key = True)
    order_nr = db.Column(db.String(20))
    art_nr = db.Column(db.String(20))
    art_size = db.Column(db.String(20))
Was mich jetzt etwas verunsichert, ich habe in der table orders mehrere Einträge für jede order_nr:
also bsp:
order art_nr art_size
123 456 20
123 456 21
123 789 19
458 100 10

So z.B. im Artikel table hab ich für jede Artikelvariante einen Eintrag, also ist die richtige Variante hier many to one, weil es mehrere Artikel pro Order geben kann aber jeder Artikel nur 1x in der Artikel table existiert?

Muss ich mir hierfür jetzt so etwas wie ein extra table erstellen und muss ich diesen nur beschreiben oder muss der wirklich in der Datenbank existieren?
Oder gibts da ne bessere Herangehensweise?

Wenn ich es richtig verstanden haben müsste es ungefähr so aussehen oder?

Code: Alles auswählen

class Orders(Base):
    __tablename__ = 'orders'
    data_id = db.Column(db.Integer, primary_key = True)
    order_nr = db.Column(db.String(20))
    art_nr = db.Column(db.String(20))
    art_size = db.Column(db.String(20))
    artikel_id = Column(Integer, ForeignKey('artikel.data_id'))
    orders = relationship("Orders", backref="Artikel")
Wie schon gefragt müssten, sofern das überhaupt richtig ist artikel_id und orders im Orders Table exisiterien, oder nur definiert sein?
Wirklich verstehen tu ich das ganze allerdings nicht, daher hoffe ich hier etwas "Erleuchtung" zu finden?
BlackJack

@taake: Also normalerweise hat man da noch eine extra Tabelle denn bei einer Bestellung gibt es normalerweise ja noch *zusätzliche* Daten für *eine* Bestellung. Das Datum und den Empfänger beispielsweise. Und dann hat meine keine „many to one“-Beziehung sondern eine „many to many“-Beziehung zwischen Bestellung und Artikel die durch eine zusätzliche Tabelle abgebildet wird. Denn jeder Artikel kann ja bei mehreren Bestellungen vorkommen und eine Bestellung kann sich auf mehrere Artikel beziehen.

Warum gibt es `art_nr` und `art_size` identisch in beiden Tabellen? Das sieht falsch aus.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Und was haben Nummer und Größe mit Kunst zu tun?
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@pillmuncher: Na die Exponate sind durchnummeriert und die Grösse ist je nach Typ des Exponats in Quadrat- oder Kubikmetern angegeben, damit die Spedition weiss womit sie für die Auslieferung zu rechnen hat. :mrgreen:
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

Ja sehr lustig;

Sofern noch Interesse an Konstruktivem besteht:

@BlackJack:
Das ganze läuft anhand der Bestellungs ID; Kunde, Lieferant, Datum usw. sind hierbei nicht relevant, da hier Werbematerial für den Kunden bereit gestellt wird; dafür sind lediglich die in der Bestellung enthaltene Artikel, anhand ihrer Nr und ihrer Grösse zu identifizieren.

Warum sieht es demnach falsch aus wenn die Identifikatoren in beiden Tabellen vorkommen?

Zur Zeit läuft es so das Nachts nen Job läuft der die data_id des Artikels aus dem Artikel table sucht
und in ein extra Feld des tables Orders schreibt. Da die Berechnung dafür recht viel Zeit frisst und nicht wirklich nice ist würde ich das ganze gerne wie bereits beschrieben handhaben.

Die descritions der tables sind eher sinnbildlich zu sehen und enthalten mehr Felder, der Einfachheit halber wurden sie hier auf die relevanteren heruntergebrochen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@taake: relationale Datenbanken sollten so aufgebaut sein, dass es keine redundanten Informationen gibt. Über die artikel_id der Orders-Tabelle kann jederzeit art_nr und art_size aus der Artikel-Tabelle gefunden werden (orders.artikel.art_nr).
data_id heißt normalerweise einfach id oder eben artikel_id, das data-Präfix hat null Informationsgehalt.
BlackJack

@taake: Okay, die witzeleien über die Kunst mal etwas konstruktiver formuliert: Dieser `art_*`-Präfix hat da nichts zu suchen. Es macht keinen Sinn allen Spaltennamen der Artikel-Tabelle noch einmal `art_*` voranzusetzen, denn das bringt keine zusätzliche Information für den Leser. Das es sich um eine Artikelnummer und eine Artikelgrösse und einen Artikelnamen und … handelt, geht ja schon aus dem Umstand hervor das die Spalte sich in der `Artikel`-Tabelle befindet und wenn daraus Python-Objekte werden, dann das es sich um Attribute auf einem Objekt vom Typ `Artikel` handelt. Weder `Artikel.art_name` in SQL noch `article.art_name` in Python haben einen Mehrwert gegenüber `Artikel.name` in SQL und `article.name` in Python. Im Gegenteil: Man hat eine komische, überflüssige Abkürzung als Präfix weniger. Vermeidet auch Witze über Kunst. ;-)

Zum `data_`-Präfix hat Sirius3 ja schon etwas gesagt.

Englisch und Deutsch bei den Namen zu mischen ist nicht einfach nur unschön sondern deutsch macht oft auch ganz praktische Probleme beim Plural weil es viel mehr deutsche Bezeichnungen gibt bei denen Sigular und Plural gleich sind. Wie in diesem Beispiel `Artikel`. Für die Rückreferenz nimmt man üblicherweise den Namen dessen was da referenziert wird und je nach dem ob es eine 1:1 oder 1:n Beziehung ist, wählt man die Bezeichnung in Plural oder Singular. Ist ein `order.artikel` dann *ein* Objekt oder *mehrere*? Bei `order.article` vs. `order.articles` wäre das sofort beim lesen klar.

Dieses Problem besteht auch ausserhalb vom ORM. Mal angenommen man hat eine Sequenz von Artikeln und nennt die naheliegenderweise `artikel`. Und will nun über die einzelnen Artikel iterieren mit ``for artikel in artikel:`` — ups, nein besser nicht. Was nun? Denkt man sich einen anderen Namen für den einzelnen Artikel aus oder für die Sequenz? Welchen? Bekommt man das einheitlich hin? Wie machen andere das? Alles Überlegungen und Entscheidungen die man im englischen gar nicht erst treffen muss: ``for article in articles:``.

Und immer noch Namen: `Orders` ist falsch. Der Klassenname bezeichnet die Bedeutung *eines* Exemplars und *ein* `Orders`-Objekt modelliert nicht wie der Name suggeriert mehrere Bestellungen sondern *einen* Posten auf *einer* Bestellung. Aus dem gleichen Grund ist der Tabellenname im Grunde falsch, denn so eine Tabellendefinition kann man sich wie die deklaration der Datenfelder einer Klasse vorstellen. Gerade wenn man ein ORM verwendet wird dieser Zusammenhang ja recht deutlich sichtbar.

Falls `Artikel.art_group` dergestalt ist das alle Artikel einer Artikelgruppe dort den gleichen Zeichenkettenwert stehen haben, dann ist das übrigens eine weitere Verletzung der Normalform und sollte beseitigt werden.

Was die Tabellen und -beziehungen angeht, denke ich das wird so ähnlich aussehen:

Code: Alles auswählen

article_order = Table(
    'article_order', Base.metadata,
    db.Column('article_id', db.ForeignKey('article.id'), primary_key=True),
    db.Column('order_id', db.ForeignKey('order.id'), primary_key=True),
)


class Article(Base):

    __tablename__ = 'article'

    id = db.Column(db.Integer, primary_key=True)
    number = db.Column(db.String(20), nullable=False, unique=True)
    size = db.Column(db.String(20), nullable=False)
    name = db.Column(db.String(20), nullable=False)
    group = db.Column(db.String(20), nullable=False)
    description = db.Column(db.String(20), nullable=False)

    # `orders` from backref on `Order`.


class Order(Base):

    __tablename__ = 'order'

    id = db.Column(db.Integer, primary_key=True)
    number = db.Column(db.String(20), nullable=False, unique=True)

    articles = relationship(Article, secondary=article_order, backref='orders')
Ich habe bei `nullable` und `unique` einfach mal geraten, muss also nicht stimmen. Das musst Du wissen.
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

@Sirius3:
das mit dem data_id oder manchmal auch einfach nur _id kommt daher das wenn ich einfach nur id eingeben, das syntax highlighting anspringt. Daher verwende ich id nicht - den Spass ein Eigennamen von Python zu verwenden wollte ich mir kein zweites Mal geben.
Daher ja okay hat keinem Mehrwert oder irgendwas allerdings muss ich mich nicht fragen ob es ggf. daran liegt sollte irgendwas nicht wie gewollt funktioniert.

@BlackJack:
Damit hast du vollkommen recht, die Benennung ist kurz gesagt suboptimal.
Einer der Gründe ist dass das ganze eher so über die Zeit gewachsen ist, was mich zu dem Punkt der Redundanz führt.
Die ist leider nicht wirklich vermeidbar, zumindest ist mir bis dato noch keine passable Möglichkeit dafür eingefallen.
Die Datensätze werden auf dem System nicht selbst erzeugt, sondern Nachts von unserem Navision System importiert, zum einen möchte ich gar nicht erst den Server mit dem ERP verbinden zum anderen hat der am Tage schon genug Anfragen zu bearbeiten, da brauch er die nicht auch noch.
Daher werden die des Nachts übertragen.
Die einzelnen Tabellen sind quasi durch neue Funktionen des Servers hinzugekommen, würde ich noch mal ganz von vorne Anfangen würde ich das ganze wahrscheinlich anders aufziehen, aber dafür fehlt sowohl Zeit als auch Lust.
So genug der Rechtfertigung der Suboptimalität ;)

Es gibt allerdings bei den Orders, zu jeder OrderNr multiple Artikel, stell das ein Problem da?
Daher die Verknüfung von der ArtikelNumber im Order Table und der ArtikelNumber im ArticleTable.

Wenn ich dein Beispiel richtig verstehe sind order.number und article.number identisch?
Also würde ich über article_order.Order.number = xyz
ebenfalls article_order.Article.name, für den artikel herausbekommen?
Tut mir leid für die dumme Frage aber ich benutzt, bzw versuch es, gerade das erste Mal sowas zu benutzen.
BlackJack

@taake: `id` ist der Name einer eingebauten Funktion, das kollidiert doch aber nicht wenn man den Namen als *Attribut* verwendet. Da ist das ”syntax highlighting” in Deinem Editor oder Deiner IDE etwas zu übervorsichtig.

Nein, `Article.number` und `Order.number` sind bei meinem Beispiel nicht das gleiche. Das eine sind Artikelnummern, das andere die Nummer einer Bestellung. Verbunden werden die beiden Tabellen durch die `article_order`-Tabelle und da auch über die IDs. Für so etwas führt man doch die künstlichen IDs ein, damit nicht über Zeichenketten zugeordnet werden muss, sondern effizienter über ganze Zahlen. Ausserdem kann man so die Artikelnummern nachträglich beispielsweise noch auf ein anderes System umstellen ohne das die Verbindungen zwischen den Tabellen verloren gehen.
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

@BlackJack:
okay zukünftig werd ich dann wohl id als namen nehmen ;)

Mhmmm das verwirrt mich jetzt eher, heisst dass das ich das ganzen überhaupt nicht nutzen kann?
Weil so ich ich es gerade verstehe müssten die ids in beiden tables gleich sein also id 1 in einem table auf id 1 in dem anderen verweisen, das ergibt für mich gerade wenig bis überhaupt keinen Sinn.

Ich war eigentlich davon ausgegangen das ich mit dieser article_order table die article und die order verknüpfen könnte da ich in beiden die Information für die jeweilige Artikelnummer und die jeweilige Partikelgrösse habe und das ganze damit mappen könnte, hätte mir bzw. dem Server nen Nachtjob erspart.

Allerdings bin ich was das angeht jetzt irgendwie weniger schlauer als vorher.

Sei es drum
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@taake: dich verwirrt vermutlich die 'id' als primary key. Dies ist kein Name sondern ein Integer der zusätzlich zu den Daten eines jeden Deiner Datensätze von der Datenbank mit angelegt wird. Und zwar so, dass es jede 'id' (nomen est omen) pro Tabelle nur einmal gibt. Über die 'article_order'-Tabelle kann dann eine m2m-Verknüpfung der Datensätze in den beiden anderen Tabellen vorgenommen werden.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@taake: ich bin gerade verwirrt, was Du mit "da ich in beiden die Information für die jeweilige Artikelnummer [..] habe" meinst. Die Artikel-Nummer ist nur in der Artikel-Tabelle. Welche Artikel im jeweiligen Order sind, kann man über die Tabelle article_order herausfinden. Also als Beispiel, wenn in article_order steht:

Code: Alles auswählen

Article-ID  Order-ID
        1  1
        2  1
        1  2
        3  2
heißt das, dass die Artikel mit der ID 1 und 2 in Order 1 sind und Artikel 1 und 3 in Order 2. Das ist eine many-to-many-Abbildung, weil der selbe Artikel in verschiedenen Order sein kann und ein Order mehrere Artikel haben kann.
taake
User
Beiträge: 125
Registriert: Donnerstag 14. Oktober 2010, 08:49

@kbr: ne die id verwirrt mich nicht, mich verwirrt wie er rückschlüsse ziehen kann. Also die entsprechend verknüpfung über den article_order table das attribut size um genau zu sein, wie kann er darauf rückschließe ziehen?

@Sirius3: die ArtikelNr ist halt nicht nur in der article_table sonder auch in der order_table (siehe description erster post)
Meinst du mit der Frage ob es irgendwelchen Bezug zwischen der ID in dem Order mit der ID im Artikeltable gibt?
Nein gibt es nicht und wenn wäre er rein zufällig.

Daher dachte ich das man das ganze via relationship verbinden könnte über die Angaben von Artikel nummer und artikel größe, da diese informationen in jedem table vorhanden sind.
Und man darüber (so dachte ich zumindest) ein einfaches mapping hinbekommen müsste.

Wäre ziemlich gut gewesen, da zur Zeit aber nicht so viele Anfragen zeitgleich reinkommen das man das ganze
nicht quasi jedesmal von hand machen könnte, ohne relationship werde ich erstmal das machen.

Scheinbar schreiben wir hier zu konsequent aneinander vorbei, zumindest kommt es mir so vor.
Ggf. wenn mehr Anfragen beantworten werden müssen oder ich mal mehr Zeit habe werde ich nochmal drauf zurück kommen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@taake: dafür ist doch genau die ID da, um Beziehungen zwischen den einzelnen Tabellen herzustellen.
Antworten