Relationsdaten erhalten - Flask-Sqlalchemy

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Hallo zusammen,

ich habe ein Projekt mit flask-sqlalchemy gestartet, bei der ich eine Datenbank mit Relationen (MySQL) erlernen möchte.
Ich habe dabei zwei Tabellen (Gruppen mit Spalte ID, Name) und (Adresse mit Spalten x, y, Gruppe).
Beim Auslesen der Adressentabelle erhalte ich aber nur die ID auf die ich referenziert habe und kann leider keinen Zugriff auf die anderen Values erhalten ("AttributeError: 'int' object has no attribute 'group_name'")

Im Code sieht das so aus:

Code: Alles auswählen

class Gruppe(db.Model):
    __bind_key__ = 'Adressen'
    __tablename__ = 'Gruppe'
    id = db.Column('AK', db.Integer, primary_key = True)
    group_name = db.Column('Name Gruppe', db.String(20), nullable = True)
    active = db.Column('Aktiv', TINYINT(1))
    address = db.relationship('Adressen', backref='Gruppe')

    def __init__(self, group_name, active):
        self.group_name = group_name
        self.active = active
    
class Adressen(db.Model):
    __bind_key__ = 'Adressen'
    __tablename__ = 'Adressen'
    id = db.Column('ID', db.Integer, primary_key = True, unique = True)
    birthday = db.Column('Geburtsdatum', db.DateTime(), nullable = True)
    last_name = db.Column('Name', db.String(20), nullable = True)
    first_name = db.Column('Vorname', db.String(15), nullable = True)
    street = db.Column('Straße', db.String(25), nullable = True)
    family = db.Column('FamStand', db.String(1), nullable = True)
    comment = db.Column('Bemerkung', LONGTEXT(), nullable = True)
    group = db.Column('AK', db.Integer, db.ForeignKey('Gruppe.AK'))
    
## route zum testen
@app.route('/all')
def addressen_all():
    neu = Adressen.query.filter_by(id=1).first()
    new = Gruppe.query.filter_by(id=1).first()
    name = neu.group
    print(neu.group.group_name) ####hier erwarte ich dann einen Name statt der Zahl
    print(new.group_name)
    return render_template("HTML/Addressen/addressen_all.html", Addresse = Adressen.query.all(), title = 'Adressen')
    
    
###im HTML sieht die Tabelle aktuell so aus###
 <tbody>
                {% for addresse in Addresse %}
                    <tr>
                        <td>{{ addresse.id }}</td>
                        <td>{{ addresse.birthday }}</td>
                        <td>{{ addresse.last_name }}</td>
                        <td>{{ addresse.first_name }}</td>
                        <td>{{ addresse.street }}</td>
                        <td>{{ addresse.city_plz }}</td>
                        <td>{{ addresse.family }}</td>
                        <td>{{ addresse.comment }}</td>
                        <td>{{ addresse.group}}</td>
                    </tr>
                {% endfor %}
Wo liegt dabei mein Denkfehler? Denn normalerweise sind doch Relationen für genau solche Fälle da.
*sollte das mit einem left join gemacht werden, wie bekomme ich diesen denn dann in die HTML Tabelle? (Ohne dabei ggf. die Datenbank zu erweitern etc?

Grüße
Karli
Zuletzt geändert von Karlirex am Mittwoch 4. Mai 2022, 07:11, insgesamt 1-mal geändert.
Benutzeravatar
sparrow
User
Beiträge: 4144
Registriert: Freitag 17. April 2009, 10:28

Ein paar Anmerkungen vorweg:

Relationen (also Tabellen) sollten ihren Namen in der Einzahl tragen. "Gruppe" ist korrekt, "Adressen" sollte "Adresse" heißen. Dann fällt es dir hinterher leichter anhand der Namen die Beziehung zu erkennen. Eine many-to-many-Beziehung zwischen irgendwas und "Adressen" wären sonst "adressenadressen" oder "adressenenen" oder "adressess"?

Jetzt bin ich deutlich tiefer in Djangos ORM als in SQLAlchemy, aber soviel ich weiß, muss man den Fermdschlüssel definieren und zusätzlich eine "relationship". Die sehe ich bei dir aber in "Adressen" nicht. group ist da nur ein Fremdschlüssel, keine Relationship. Normalerweise legt man den Fremschlüssel mit "foo_id" an und die Relationship mit "foo", dann funktioniert es wie gewünscht.

Wird hier auch brav in der Dokumentation erklärt.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

sparrow hat geschrieben: Mittwoch 4. Mai 2022, 07:11 Ein paar Anmerkungen vorweg:

Relationen (also Tabellen) sollten ihren Namen in der Einzahl tragen. "Gruppe" ist korrekt, "Adressen" sollte "Adresse" heißen. Dann fällt es dir hinterher leichter anhand der Namen die Beziehung zu erkennen. Eine many-to-many-Beziehung zwischen irgendwas und "Adressen" wären sonst "adressenadressen" oder "adressenenen" oder "adressess"?

Jetzt bin ich deutlich tiefer in Djangos ORM als in SQLAlchemy, aber soviel ich weiß, muss man den Fermdschlüssel definieren und zusätzlich eine "relationship". Die sehe ich bei dir aber in "Adressen" nicht. group ist da nur ein Fremdschlüssel, keine Relationship. Normalerweise legt man den Fremschlüssel mit "foo_id" an und die Relationship mit "foo", dann funktioniert es wie gewünscht.

Wird hier auch brav in der Dokumentation erklärt.
Da lag mit der "foo_id" und dem "foo" wirklich der Fehler. Danke dir.
(Ist mein erstes Mal mit dem Arbeiten solcher Relationen) Ich danke dir.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Und wie löse ich eine Relation, wenn in der Ursprungsdatenbank die Referenzierung kein Integer ist?
Da hat sich wohl bei MS Access eine Referenz als String eingebaut. Darauf kann SQL-Alchemy nicht einen Foreign Key setzen. Zumindest kommt mit das so zurück:

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1005, 'Can\'t create table `xx`.`Daten` (errno: 150 "Foreign key constraint is incorrectly formed")')
[SQL:
CREATE TABLE `Daten` (
`AKTIV` VARCHAR(1),
`Monat` VARCHAR(12),
`KAUF` VARCHAR(20),
`Daten` DOUBLE,
`Jahr` INTEGER,
`DATEN_ID` INTEGER,
`Kennummer` INTEGER NOT NULL AUTO_INCREMENT,
`DatenName_ID` INTEGER,
`BuchDat` DATETIME,
`Hüll-Nr` VARCHAR(15),
`Sachdaten` VARCHAR(50),
PRIMARY KEY (`Kennummer`),
FOREIGN KEY(`Monat`) REFERENCES `Monat` (`Monat`),
UNIQUE (`Kennummer`),
FOREIGN KEY(`DatenName_ID`) REFERENCES `Datenname` (`Kennummer`)
)

Und die Monatstabelle sieht zumindest in Python umgesetzt 1:1 so aus:

Code: Alles auswählen

class Monat(db.Model):
    __bind_key__ = 'xx'
    __tablename__ = 'Monat'
    month = db.Column('Monat', db.String(12), primary_key = True, unique = True)
    month_rel = db.relationship('Daten', backref = 'Daten', viewonly = True)

Andere Felder/Spalten gibt es in der Tabelle nicht. Der Monat ist eigentlich auch nichtmal ein primary_key.

Wo setz ich da an?
Die Tabelle um eine ID-Spalte erweitern?
Daten neu einfügen?

Grüße
Karlirex
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Naja du musst den foreign key dann schon auch vom gleichen Datentyp haben wie die Spalte, auf die der referenziert. Das hat auch nix mit SA zu tun, das verlangt (zu recht) die DB so.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Der Typ ist passend, String(12). An sich liegt aber in der Monatstabelle kein primary_key dran, das ist nur die eine Spalte ohne ID/Index oder iwas. (Habe die DB so bekommen bzw so aus MS Access gemacht)
UNd laut dem Fehler kann ich da dann nicht drauf referenzieren, trotz gelichen Typs
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ist er nicht. Da steht doch

FOREIGN KEY(`DatenName_ID`) REFERENCES `Datenname` (`Kennummer`)

und

`DatenName_ID` INTEGER,

Daher kommt der Fehler. DatenName_ID muss auch schon ein String sein.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

@__deets__ es geht aber doch um Monat oder nicht?
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Karlirex: ups. Da habe ich mich erfolgreich verwirrt. Wobei du da natuerlich auch die Korrektheit pruefen musst, die Tabelle Datenname ist ja nicht angegeben. Und die Fehlermeldung sagt ja gar nicht, welcher Key das ist.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

@__deets__ das der DAtenname nicht angegeben ist, leigt ein bisschen an meinen Projekt-Schwärzungs-Skills ^^ aber an sich ist das korrekt verwiesen. Aber es hört sich schonmal schön an, dass ich wirklich nicht die Tabelle brauche mit einem richtigen "integer key" sondern es auch mit Strings geht etc. Vllt fällt mir noch was ein. Oder wenn jemand noch einen Idee hat und wenn es nur ist, was man noch googlen kann, freue ich mich :D
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Natürlich gehen auch String keys. Sie sind nur schwierig, wenn sie - anders als deine Monate - keine natürliche und stabile Textform haben. Nimmst du zb den Namen eines Users, heiratet der, und ändert den. Und du stehst wahlweise vor einer DB Migration, oder einem disassoziierten Schlüssel und irgendwelchen Namensfeldern.

Und die Fehlermeldung ist klar. Das ist in irgendeiner Form dein Problem. Beim Monat sehe ich das nicht, musst du also woanders suchen.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

@__deets__ : der Fehler lag an einem Tippfehler in der Tabellenbezeichnung -.-

Eine neue andere Frage, ich habe eine Suche bzw. möchte ich diese Integrieren. Die Suche bei Adressen für Spalten wie "last_name" oder "street" funktioniert wunderbar über ein like.
Ich möchte allerdings auch über die Gruppe suchen können. Gebe ich den Namen für die Gruppe ein, erhalte ich kein Suchergebnis. Gebe ich die ID-Nummer ein, bekomme ich das Ergebnis mit der entsprechenden Gruppe heraus.
Wie funktioniert das denn auch mit der Textsuche?

Code: Alles auswählen

Adressen.query.join(Adressen.group.and_(Arbeitskreis.group_name != 'AK Karlirex'))
Das war meine erste Idee, dabei bekomme ich im print() aber lediglich das SQL-Statement ausgeworfen und kein passendes Suchergebnis.
Ich verstehe das "Problem", dass die Gruppe in Adressen nur als Integer gespeichter ist. Dennoch kann ich mir diese ja über ein

Code: Alles auswählen

adresse.group.group_name
ausgeben lassen.
Bei der like-Suche klappt die Verknüpfung so nicht.

Code: Alles auswählen

Adressen.group.group_name.like("AK Karlirex")
Vielleicht gibts da eine Lösung die ich etwas übersehe.

Danke nochmal,
Grüße
Karlirex
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na du musst eben einen join machen, damit du die Gruppentabelle in der Suche mit einbeziehen, damit du deren ID basierend auf einem like-Query bestimmen kannst. Wie sowas geht, ist in SA gut beschrieben.

https://docs.sqlalchemy.org/en/14/tutor ... ps-to-join
Antworten