SqlAlchemy-Tutorial: Relation funktioniert nicht

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Benutzeravatar
Käptn Haddock
User
Beiträge: 169
Registriert: Freitag 24. März 2006, 14:27

Hallo!

Ich arbeite gerade das SqlAlchemy-Tutorial (0.5.5) durch und hab ein Problem mit einem Beispiel zu 'relation', es geht halt nicht so wie es da steht. Dabei funktionert die Relation vom Parent zum Child, aber nicht andersrum.
So sieht das Beispiel aus, wenn die markierte Zeile auskommentiert ist gehts, wenn nicht kommt die unten stehende Fehlermeldung.Vielleicht kann mir wer sagen, was da falsch ist.

Code: Alles auswählen

from sqlalchemy import Column, Integer, String, MetaData, ForeignKey, create_engine
from sqlalchemy.orm import relation, backref,  sessionmaker,  clear_mappers
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))

>>>    user = relation('User', backref=backref('addresses', order_by=id)) <<<

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

    def __repr__(self):
        return "<Address('%s')>" % self.email_address
         
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    password = Column(String)
    
    addresses = relation(Address, order_by=Address.id, backref="user")

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

    def __repr__(self):
       return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)

def main():
    con = 'sqlite://'
    engine = create_engine(con, echo=True)
    Session = sessionmaker(bind=engine)
    metadata = Base.metadata
    metadata.create_all(engine)
    session = Session()
if __name__ == '__main__':
    main()
Fehlermeldung:
unhandled ArgumentError: Error creating backref 'user' on relation 'User.addresses': property of that name exists on mapper 'Mapper|Address|addresses'

Danke im Voraus!

Uwe
---------------------------------
have a lot of fun!
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Das "User" darf in der entsprechende Zeile nicht in Anführungszeichen...
Benutzeravatar
Käptn Haddock
User
Beiträge: 169
Registriert: Freitag 24. März 2006, 14:27

ice2k3 hat geschrieben:Das "User" darf in der entsprechende Zeile nicht in Anführungszeichen...
Doch, muß es. Da ich declarative verwende, ist die Klasse User noch nicht angelegt wenn Address darauf verweist. Für diesen Fall muß ich die noch nicht angelegten Klassen als Strings übergeben. Sagt die Anleitung und lässt sich auch experimentell bestätigen ;)

Grüsse Uwe
---------------------------------
have a lot of fun!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann musst Du aber auch die deklarative "Ausprägung" von relation() verwenden:

Code: Alles auswählen

user = relation("User", backref="adresses")
# und hier tust Du das ja bereits!
addresses = relation("Address", backref="user")
Benutzeravatar
Käptn Haddock
User
Beiträge: 169
Registriert: Freitag 24. März 2006, 14:27

Code: Alles auswählen

class Address(Base):
    ...
    user = relation('User', backref = 'addresses')

class User(Base):
   ...
   addresses = relation('Address', backref = 'user')
Kein Unterschied...

ratlose Grüsse

Uwe
---------------------------------
have a lot of fun!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann lass doch einfach eine relation weg! Wozu diese zwei mal hinschreiben... dafür gibts doch das "backref" oder nicht?
Benutzeravatar
Käptn Haddock
User
Beiträge: 169
Registriert: Freitag 24. März 2006, 14:27

Hyperion hat geschrieben:Dann lass doch einfach eine relation weg! Wozu diese zwei mal hinschreiben... dafür gibts doch das "backref" oder nicht?
Naja, die ist halt dazu da, von dem einen auf das andere zugreifen zu können. Wie hier vom Child auf das Parent geht auch letzlich über ein Query mit dem ForeignKey, wäre so halt eleganter gewesen. Haupsache, die andere Richtung geht, so das die Child-Objekte im Parent als Liste verfügbar sind, da die oft verwendet werden sollen im Projekt. Naja, wenns halt im Tutorial falsch steht ist das zwar ärgerlich aber kein Weltuntergang.

Vielen Dank für die HIlfe!

Gruß Uwe
---------------------------------
have a lot of fun!
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Zum einen musst du nur auf eine Klasse eine relation festlegen zum anderen solltest du entweder die Klasse oder den Tabellennamen verwenden(Z. 28).
Benutzeravatar
Käptn Haddock
User
Beiträge: 169
Registriert: Freitag 24. März 2006, 14:27

DasIch hat geschrieben:Zum einen musst du nur auf eine Klasse eine relation festlegen zum anderen solltest du entweder die Klasse oder den Tabellennamen verwenden(Z. 28).
Naja, das ist orginal das Beispiel aus dem SqlAlchemy-Tutorial.
Ich gehe davon aus, dass das schon irgendwie stimmt ;)
adresses heißt zwar die Tabelle aber es wird auch eine Liste 'adresses' als Attribut angelegt, die den Zugriff auf die Child-Elemente erlaubt. Der Teil funktioniert auch. Der Zugriff vom Child aufs Parent soll nach der Anleitung so auch gehen, wäre praktisch und elegant, ist aber nicht kriegsentscheidend ;)

Insofern schönes Wochenende zusammen

Grüssle Uwe
---------------------------------
have a lot of fun!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Käptn Haddock hat geschrieben:
Hyperion hat geschrieben:Dann lass doch einfach eine relation weg! Wozu diese zwei mal hinschreiben... dafür gibts doch das "backref" oder nicht?
Naja, die ist halt dazu da, von dem einen auf das andere zugreifen zu können. Wie hier vom Child auf das Parent geht auch letzlich über ein Query mit dem ForeignKey, wäre so halt eleganter gewesen. Haupsache, die andere Richtung geht, so das die Child-Objekte im Parent als Liste verfügbar sind, da die oft verwendet werden sollen im Projekt. Naja, wenns halt im Tutorial falsch steht ist das zwar ärgerlich aber kein Weltuntergang.

Vielen Dank für die HIlfe!

Gruß Uwe
Ja, aber durch das backref brauchst Du eben ja nur einen relation()-Aufruf! Du kannst dann schon in beiden Klassen auf das / die Attribute zurgreifen. Zumindest bei mir klappt das ;-)

Beispiel:

Code: Alles auswählen

foo = relation("Klasse", backref="bar")
foo = Attributsname in der Klasse, wo dieser Aufruf von relation() steht.
bar = Attributsname in der Klasse "Klasse".
Benutzeravatar
Käptn Haddock
User
Beiträge: 169
Registriert: Freitag 24. März 2006, 14:27

Ja, bei mir klappt das jetzt auch.
Anscheinend habe ich die Anleitung irgendwie nicht richtig geparst, aber dort stehen eben _zwei_ Relations, eine in jedem Objekt. Aber das scheint nicht notwendig zu sein. Kein Schimmer, wie das zu verstehen ist. Nochmal Danke an alle!

Gruß Uwe
---------------------------------
have a lot of fun!
doca82
User
Beiträge: 48
Registriert: Mittwoch 16. September 2009, 19:39
Wohnort: Berlin

hi ich hatte das problem auch...vielleicht doch ein Fehler im Tutorial?!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

doca82 hat geschrieben:hi ich hatte das problem auch...vielleicht doch ein Fehler im Tutorial?!
Vielleicht ist es als "entweder... oder" gemeint? Also entweder in der Klasse, oder in der anderen. Wobei das "nicht in beiden!" fehlt :-D
Antworten