QDialog

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

ich habe mir mal ein Beispiel zu Dialog-Widgets angesehen, hier zu finden: https://doc.qt.io/qtforpython-6/example ... _book.html

In der AddDialogWidget Klasse am Schluss der __init__ methode steht:

Code: Alles auswählen

button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
Das Prinzip der Signals & Slots ist mir inzwischen ja klar, aber was ich (immer noch) nicht (ganz) verstehe ist, wo diese Signale verarbeitet werden, bzw. warum sie hier an das DialogWidget selbst connected sind.

In der aufrufenden add_entry methode der AddressWidget Klasse wird doch mit

Code: Alles auswählen

if add_dialog.exec():
    name = add_dialog.name
    address = add_dialog.address
auch nur der Rückgabewert von .exec() verarbeitet ?!
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Ich glaube, das Prinzip ist dir noch nicht klar.

Was genau ist denn der Rückgabewert von .exec(). Und wie kommt der zustande?
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

sparrow hat geschrieben: Samstag 9. September 2023, 12:27 Was genau ist denn der Rückgabewert von .exec(). Und wie kommt der zustande?
Erst mal danke für den Denkanstoß.

QDialog.accept() setzt den Rückgabewert auf QDialog.Accepted (wird das zwischengespeichert?), dadurch wird das Signal accepted() ausgesendet, was gleichzeitig der Rückgabewert von .exec() ist ??
(für Rejected vice versa)
Und die aufrufende methode bildet den slot für diesen Wert? Oder nur deshalb, weil die Methode mit @slot definiert ist (ohne geht es aber auch) ?

Mir ist nicht klar, wann hier was passiert und was letztlich der Unterschied zwischen signal und rückgabewert ist.
Benutzeravatar
__blackjack__
User
Beiträge: 14050
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Das Beispiel benutzt einen modalen Dialog in dem es `exec()` auf dem Dialog aufruft, also eine eigene Ereignisschleife startet. Die läuft solange bis `done()` auf dem Dialog aufgerufen wurde. Das beendet die Schleife und `exec()` kehrt mit dem Wert zurück der `done()` übergeben wurde. `done()` dürfte auch die Signale `accepted()`, `rejected()`, und `finished()` senden.

`accept()` und `reject()` rufen `done()` mit dem entsprechenden Wert auf.

Zwischengespeichert wird der Wert auch, denn es gibt das Qt-`result`-Property.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

@__blackjack__: Ich vermute, dass der Verständisfehler an einer anderen Stelle liegt und durch das Unverständnis des Beispiel schlecht in Worte gepackt wurde.

@mechanicalstore:

Ich glaube, du machst dir das viel zu kompliziert.

Schauen wir doch mal, was die Dokumentation von QDialog so sagt.

Und da steht für exec():
Shows the dialog as a modal dialog, blocking until the user closes it. The function returns a DialogCode result.
Exec() zeigt also einen modalen Dialog.
Der Code blockiert an dieser Stelle so lange, bis der Funktionsaufruf zurück kommt und dann kann man mit dem Rückgabewert arbeiten.

Und für accept() steht da:
Hides the modal dialog and sets the result code to Accepted.
Der Dialog verschwindet also, der ReturnCode wird gesetzt und der aufrufende Code wird fortgesetzt.

Der Code kommt bis hierher ohne Signale aus. Sie werden zwar in dem von dir verlinkten Beispiel abgefeuert aber nicht verwendet - und das wäre auch unnötig da ein einfacher Aufruf von accept() oder reject() ja den Returncode setzt und den modalen Dialog schließt bzw. versteckt.
Unter der Haube läuft da ab, was __blackjack__ beschreibt. Aber genutzt wird das alles _im Code_ nicht.

Soweit klar?

Ich nahm nach deinem ersten Post und noch immer an, dass dich das verbinden der Signale in der init verwundert.
Die Signale, die da verbunden werden: Wer löst die eigentlich aus und was ist das Ziel der Signale? Also was bewirken sie?
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

Danke euch beiden für die Unterstützung!
sparrow hat geschrieben: Samstag 9. September 2023, 14:59 Ich nahm nach deinem ersten Post und noch immer an, dass dich das verbinden der Signale in der init verwundert.
Die Signale, die da verbunden werden: Wer löst die eigentlich aus und was ist das Ziel der Signale? Also was bewirken sie?
Ja, das hatte mich auch Anfangs verwundert, wobei das aber zwischenzeitlich (hoffentlich) klar geworden ist.

Das Signal button_box.accepted geht (nach click-event des entsprechenden Buttons) nach accept() des Dialogfensters, was dann wiederum den result Wert setzt, das Fenster schließt und selbst dann noch das accepted() Signal (in dem Beispiel ungenutzt) aussendet. reject() vice versa.

Passt das?
Benutzeravatar
__blackjack__
User
Beiträge: 14050
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: 👍
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Samstag 9. September 2023, 16:14 @mechanicalStore: 👍
Danke! :P

Leider habe ich schon wieder ein Problem, stehe auf dem Schlauch. Dazu muss ich allerdings 4 Dateien her kopieren, ich weiß nicht, wie ich das sinnvoll kürzen kann, damit es noch läuft. Es ist einfach so, dass in p_datafill.py Zeilen 80 und 88 ignoriert werden und ich verstehe nicht warum. Hauptprogramm ist p_projectdata.py. (Zusatzfrage; lässt sich p_alchemy.py schöber gestalten?). Nach klick auf den Button "new" in der toolbar kommt der Eingabedialog. Testdaten für Projekt (wegen regex) z.B. "PA-12-123". Am Ende steht der Eintrag in der Datenbank, aber die View wird (wieder mal) nicht upgedatet. Ich rufe in den Zeilen 80 und 88 beginResetModel / endResetModel auf, was von da aus offenbar nicht geht, obwohl ich ja die Instanz auf model mitgebe.


p_alchemy.py

Code: Alles auswählen

#!/usr/bin/env python3

from sqlalchemy import INTEGER, TEXT, Column, create_engine, select
from sqlalchemy.orm import sessionmaker, declarative_base

Base = declarative_base()
Session = sessionmaker()

class Project(Base):
    __tablename__ = "project"

    project_id = Column(INTEGER, primary_key=True)
    project_name = Column(TEXT, nullable=False, unique=True)
    project_netplan = Column(TEXT, nullable=True)
    project_description = Column(TEXT, nullable=True)

    @classmethod
    def run(self):
        engine = create_engine("sqlite:///:memory:")
        Base.metadata.create_all(engine)
        session = Session(bind=engine)
        return session

    @classmethod
    def get_all_projects(self, session):
        return session.execute(select(Project)).scalars().all()

    @classmethod
    def add_project(self, session, name, netplan, description):
        if Project.is_existing_project(session, name):
            return False
        else:
            project = Project(project_name = name,
                          project_netplan = netplan,
                          project_description = description)
            session.add(project)
            session.commit()
            return True
    
    @classmethod
    def is_existing_project(self, session, name):
        if session.execute(select(Project).filter_by(project_name = name)).scalars().first():
            return True
        else:
            return False

def main():

    session = Project.run()

   # Testdaten erzeugen
    for i in range(1, 12, 2):
        if not Project.add_project(session,
                            f'P-{i}-{i*10}',
                            f'Netplan-{i / 5}',
                            f'Description-{i * 22}'):
            print(f'Projekt P-{i}-{i*10} ist schon in der Datenbank')

    # Test-Duplikate erzwingen
    for i in range(1, 12, 1):
        if not Project.add_project(session,
                            f'P-{i}-{i*10}',
                            f'Netplan-{i / 5}',
                            f'Description-{i * 22}'):
            print(f'Projekt P-{i}-{i*10} ist schon in der Datenbank')

    # Testdaten ausgeben
    for pq in Project.get_all_projects(session):
        print(f'{pq.project_name}   {pq.project_netplan}   {pq.project_description}   {pq.project_id}')

if __name__ == '__main__':
    main()
p_datafill.py

Code: Alles auswählen

#!/usr/bin/env python3

import re

from PySide6.QtCore import Qt, QSize, Slot
from PySide6.QtWidgets import (QDialog, QLabel, QTextEdit, QLineEdit, QMessageBox,
                               QDialogButtonBox, QGridLayout, QVBoxLayout)

from p_alchemy import Project
# from p_modeldata import TableModel

class AddDialogWidget(QDialog):
    def __init__(self, session, model, parent=None):
        super().__init__(parent)

        self.resize(QSize(400, 200))

        self.session = session
        self.model = model

        self.project_regex = re.compile('^[a-zA-Z]{1,2}\-\d{2}\-\d{3}$')

        self.msgBox = QMessageBox()

        self.project_label = QLabel("Projekt:")
        self.project_input = QLineEdit()

        self.netplan_label = QLabel("Netzplan-Nr:")
        self.netplan_input = QLineEdit()

        self.description_label = QLabel("Beschreibung")
        self.description_input = QLineEdit()

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok |
                                      QDialogButtonBox.Cancel)

        self.grid = QGridLayout()
        self.grid.setColumnStretch(1, 2)

        self.grid.addWidget(self.project_label, 1, 0)
        self.grid.addWidget(self.project_input, 1, 1)

        self.grid.addWidget(self.netplan_label, 2, 0)
        self.grid.addWidget(self.netplan_input, 2, 1)

        self.grid.addWidget(self.description_label, 3, 0)
        self.grid.addWidget(self.description_input, 3, 1)

        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(self.grid)
        self.main_layout.addWidget(self.button_box)

        self.setLayout(self.main_layout)

        self.setWindowTitle("Neues Projekt")

        self.button_box.accepted.connect(self.check_validat)
        self.button_box.rejected.connect(self.reject)

    def check_validat(self):
        project_text = self.project_input.text().strip().upper()
        self.project_input.setText(project_text)

        netplan_text = self.netplan_input.text().strip().upper()
        self.netplan_input.setText(netplan_text)

        description_text = self.description_input.text().strip().upper()
        self.description_input.setText(description_text)

        if not self.project_regex.match(project_text):
            self.msgBox.setText("Eingabe ungültig")
            self.msgBox.exec()

        elif Project.is_existing_project(self.session, project_text):
            self.msgBox.setText("Projekt schon in der Datenbank vorhanden")
            self.msgBox.exec()

        else:
            self.model.beginResetModel()     #  <---- Aufruf hat keine Wirkung

            if not Project.add_project(self.session,
                                    project_text,
                                    netplan_text,
                                    description_text):
                self.msgBox.setText("Projekt konnte nicht angelegt werden")
                self.msgBox.exec()

            self.model.endResetModel()       #  <---- Aufruf hat keine Wirkung

            # Testdaten ausgeben
            for pq in Project.get_all_projects(self.session):
                print(f'{pq.project_name}    {pq.project_netplan}    {pq.project_description}    ID = {pq.project_id}')       
            print()

            self.accept()

    @property
    def project_text(self):
        return self.project_input.text()

    @property
    def netplan_text(self):
        return self.netplan_input.text()

    @property
    def description_text(self):
        return self.description_input.text()


def main():

    import sys
    from PySide6.QtWidgets import QApplication

    session = Project.run()

    app = QApplication(sys.argv)

    dialog = AddDialogWidget(session)
    if (dialog.exec()):
        print(dialog.project_text)
        print(dialog.netplan_text)
        print(dialog.description_text)


if __name__ == '__main__':
    main()

p_modeldata.py

Code: Alles auswählen

#!/usr/bin/env python3

from PyQt6.QtCore import (QAbstractTableModel, Qt, QModelIndex)

from p_alchemy import Project

class TableModel(QAbstractTableModel):
    def __init__(self, session):
        super().__init__()

        self.session = session

        self.column_labels = ['Projekt', 'Netzplan', 'Beschreibung']
        self.column_attributes = ['project_name', 'project_netplan', 'project_description']
        self.projects = Project.get_all_projects(session)

    def headerData(self, section, orientation, role):
        if role == Qt.ItemDataRole.DisplayRole:
            if orientation == Qt.Orientation.Horizontal:
                return self.column_labels[section]
            elif orientation == Qt.Orientation.Vertical:
                return f'Zeile-{section}  '
            else:
                return False

    def data(self, index, role):
        if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
            return getattr(self.projects[index.row()], self.column_attributes[index.column()])

    # def setData(self, index, value, role):
    #     if role == Qt.ItemDataRole.EditRole:
    #         self.beginResetModel()
    #         setattr(self.projects[index.row()], self.column_attributes[index.column()], value)
    #         self.session.commit()
    #         self.endResetModel()
    #         return True

    #     return False

    # def flags(self, index):
    #     return Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable

    def rowCount(self, _index):
        return len(self.projects)

    def columnCount(self, _index):
        return len(self.column_labels)
p_projectdata.py

Code: Alles auswählen

#!/usr/bin/env python3

import sys

from PyQt6.QtCore import QSize, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView, QVBoxLayout, QWidget, QToolBar, QStatusBar
from PyQt6.QtGui import QAction, QIcon

from p_alchemy import Project
from p_modeldata import TableModel
from p_datafill import AddDialogWidget

class MainWindow(QMainWindow):
    def __init__(self, session):
        super().__init__(windowTitle = "Main Window")
        self.resize(QSize(1000, 500))
        self.session = session

        self.toolbar = QToolBar("Toolbar Test")
        self.toolbar.setIconSize(QSize(16,16))
        self.addToolBar(self.toolbar)

        self.button_new = QAction(QIcon("../icons/new.png"), "Neu", self)
        self.button_new.setStatusTip("Neues Projekt hinzufügen")
        self.button_new.triggered.connect(self.buttonNewClick)
        self.toolbar.addAction(self.button_new)

        self.button_delete = QAction(QIcon("../icons/cross.png"), "Löschen", self)
        self.button_delete.setStatusTip("Markiertes Projekt Löschen")
        self.button_delete.triggered.connect(self.buttonDeleteClick)
        self.toolbar.addAction(self.button_delete)

        self.button_exit = QAction(QIcon("../icons/arrow-skip.png"), "Exit", self)
        self.button_exit.setStatusTip("Projekt-Daten verlassen")
        self.button_exit.triggered.connect(self.buttonExitClick)
        self.toolbar.addAction(self.button_exit)

        self.setStatusBar(QStatusBar(self))

        self.table = QTableView()
        self.model = TableModel(self.session)
        self.table.setModel(self.model)
        self.table.resizeColumnsToContents()

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.table)

        self.widget = QWidget()
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)

    def buttonNewClick(self, s):
        dialog = AddDialogWidget(self.session, self.model)
        dialog.exec()

    def buttonDeleteClick(self, s):   
        print("Hello")

    def buttonExitClick(self, s):   
        print("Hello")

def main():
 
    session = Project.run()

   # Testdaten erzeugen
    for i in range(1, 12, 2):
        if not Project.add_project(session,
                            f'P-{i}-{i*10}',
                            f'Netplan-{i / 5}',
                            f'Description-{i * 22}'):
            print(f'Projekt P-{i}-{i*10} ist schon in der Datenbank')

    # Testdaten ausgeben
    for pq in Project.get_all_projects(session):
        print(f'{pq.project_name}   {pq.project_netplan}   {pq.project_description}   {pq.project_id}')
  
    app = QApplication(sys.argv)
    window = MainWindow(session)
    window.show()
    app.exec()

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14050
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Welche Wirkung hattest Du Dir denn erwartet? Der erste Aufruf teilt mit „Achtung, da ändert sich gleich alles am Model“ und der zweite Aufruf teilt mit „jetzt hat sich alles am Model geändert“. Jetzt ist halt die Frage: Was hast Du denn am Model geändert? Korrekte Antwort ist: Gar nichts. Also zeigt der View alles neu an, aber da sich nichts geändert hat, macht das nicht wirklich einen Unterschied.

Wobei das IMHO auch komisch riecht, das diese beiden Methoden von aussen aufgerufen werden. Das Model sollte mitteilen das sich was ändert/geändert hat.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Samstag 9. September 2023, 20:01 @mechanicalStore: Welche Wirkung hattest Du Dir denn erwartet? Der erste Aufruf teilt mit „Achtung, da ändert sich gleich alles am Model“ und der zweite Aufruf teilt mit „jetzt hat sich alles am Model geändert“. Jetzt ist halt die Frage: Was hast Du denn am Model geändert? Korrekte Antwort ist: Gar nichts. Also zeigt der View alles neu an, aber da sich nichts geändert hat, macht das nicht wirklich einen Unterschied.
Oh mann, danke, ist klar. Ich war zu lange am Monitor heute... Geht jetzt mit den Änderungen:

p_modeldata.py

Code: Alles auswählen

   ...
   def columnCount(self, _index):
        return len(self.column_labels)

    def update_model(self):
        self.beginResetModel()
        self.projects = Project.get_all_projects(self.session)
        self.endResetModel()
p_datafill.py

Code: Alles auswählen

      ...
       else:
            if not Project.add_project(self.session,
                                    project_text,
                                    netplan_text,
                                    description_text):
                self.msgBox.setText("Projekt konnte nicht angelegt werden")
                self.msgBox.exec()
            else:
                self.model.update_model()
Wobei das IMHO auch komisch riecht, das diese beiden Methoden von aussen aufgerufen werden. Das Model sollte mitteilen das sich was ändert/geändert hat.
Ist jetzt Innen. :D Wobei das aber über die Referenz auch gehen müsste, oder? Wäre dann halt nur unsauber.

Besten Dank und Gruß
Benutzeravatar
__blackjack__
User
Beiträge: 14050
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Anmerkungen zur `Project`-Klasse:

Bei `classmethod()` heisst das erste Argument per Konvention `cls` (weil ``class`` ein Schlüsselwort ist und die Konvention ``class_`` nocht nicht geboren war, als das eingeführt wurde).

Und genau wie bei `self`: Wenn `cls` nicht verwendet wird, sollte man einen guten Grund haben warum das überhaupt auf der Klasse definiert ist. Und falls man den hat, dann ist das keine `classmethod()` sondern eine `staticmethod()`.

`run()` ist beim besten Willen keine Methode. Das sollte einfach eine Funktion sein. Und auch mit einem besseren Namen, denn da ”läuft” ja nichts, da wird eine Session erzeugt.

Die Attribute sollten nicht alle den Klassennamen als Präfix haben und auch in den Methodennamen sollte der Klassenname nicht (fast) überall auftauchen. Diese ständigen Wiederholungen bringen dem Leser keinen Mehrwert.

``if``/``else`` wo aufgrund der Bedingung `True` oder `False` zurückgegeben wird braucht kein ``if``/``else`` denn die Bedingung ist ja bereits ein Wahrheitswert oder kann zumindest mit `bool()` oder ``not`` zu einem gemacht werden.

Aber die Abfrage hat sowieso ein Problem: Die fragt Datensätze ab, statt einfach nur abzufragen ob mindestens ein Datensatz existiert. SQL kennt dafür ein EXISTS und SQLAlchemy entsprechend eine `exists()`-Methode.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
from sqlalchemy import INTEGER, TEXT, Column, create_engine, select
from sqlalchemy.orm import declarative_base, sessionmaker

Base = declarative_base()
Session = sessionmaker()


def create_session():
    engine = create_engine("sqlite:///:memory:")
    Base.metadata.create_all(engine)
    return Session(bind=engine)


class Project(Base):
    __tablename__ = "project"

    id = Column(INTEGER, primary_key=True)
    name = Column(TEXT, nullable=False, unique=True)
    netplan = Column(TEXT, nullable=True)
    description = Column(TEXT, nullable=True)

    @classmethod
    def get_all(cls, session):
        return session.execute(select(cls)).scalars().all()

    @classmethod
    def exists(cls, session, name):
        return session.execute(
            select(cls).filter_by(name=name).exists()
        ).scalar()

    @classmethod
    def add(cls, session, name, netplan, description):
        if cls.exists(session, name):
            return False

        session.add(cls(name=name, netplan=netplan, description=description))
        session.commit()
        return True


def main():
    session = create_session()

    for step_size in [2, 1]:
        for i in range(1, 12, step_size):
            name = f"P-{i}-{i * 10}"
            if not Project.add(
                session, name, f"Netplan-{i / 5}", f"Description-{i * 22}"
            ):
                print(f"Projekt {name} ist schon in der Datenbank")

    # Testdaten ausgeben
    for project in Project.get_all(session):
        print(
            f"{project.name}"
            f"   {project.netplan}"
            f"   {project.description}"
            f"   {project.id}"
        )


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo __blackjack__

Vielen herzlichen Dank für die Korrekturen und Hinweise.

Grüße
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo __blackjack__

Nachdem ich das endlich testen konnte, wirft python eine Menge Fehlermeldungen:

Code: Alles auswählen

tut-env-ammun) [******@Zuse-Z4 develop]$ python develop2/p_alchemy.py 
Traceback (most recent call last):
  File "/home/******/develop/develop2/p_alchemy.py", line 65, in <module>
    main()
  File "/home/******/develop/develop2/p_alchemy.py", line 49, in main
    if not Project.add(
           ^^^^^^^^^^^^
  File "/home/******/develop/develop2/p_alchemy.py", line 36, in add
    if cls.exists(session, name):
       ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/******/develop/develop2/p_alchemy.py", line 30, in exists
    return session.execute(
           ^^^^^^^^^^^^^^^^
  File "/home/******/develop/tut-env-ammun/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2262, in execute
    return self._execute_internal(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/******/develop/tut-env-ammun/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2042, in _execute_internal
    statement = coercions.expect(roles.StatementRole, statement)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/******/develop/tut-env-ammun/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py", line 442, in expect
    return impl._implicit_coercions(
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/******/develop/tut-env-ammun/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py", line 1201, in _implicit_coercions
    return super()._implicit_coercions(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/******/develop/tut-env-ammun/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py", line 506, in _implicit_coercions
    self._raise_for_expected(element, argname, resolved)
  File "/home/******/develop/tut-env-ammun/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py", line 535, in _raise_for_expected
    raise exc.ArgumentError(msg, code=code) from err
sqlalchemy.exc.ArgumentError: Executable SQL or text() construct expected, got <sqlalchemy.sql.selectable.Exists object at 0x7fabcb896d90>.
Scheint ein Problem innerhalb der exists Methode von sqlalchemy zu sein?! Zumindest geht es ab da in die library.
Benutzeravatar
__blackjack__
User
Beiträge: 14050
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Eine Menge? Das ist *eine* Fehlermeldung. Ich schreibe ja auch fast immer „ungetestet“ dran wenn es nicht getestet ist. Da fehlt ein `select()`-Aufruf:

Code: Alles auswählen

    @classmethod
    def exists(cls, session, name):
        return session.execute(
            select(cls).filter_by(name=name).exists().select()
        ).scalar()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
mechanicalStore
User
Beiträge: 177
Registriert: Dienstag 29. Dezember 2009, 00:09

Ja, eine "Menge" war falsch ausgedrückt, ist mir klar, dass es eine ist, ist halt in der lib geendet, da dachte ich, dass da der Fehler liegt. Das "ungetestet" habe ich gelesen und ist mir bewusst. Und ist natürlich kein Vorwurf, bin froh und dankbar für die Hilfe!!

Komme offenbar mit der Suche in sqlalchemy noch nicht richtig zurecht. Wollte das selbst lösen, aber jegliche Suche nach "exists()" hat mich nie dahin geführt, wo eindeutig die Syntax so beschrieben wurde, wie Du sie oben jetzt dargestellt hast. Klingt komisch, ist aber so. Es gibt etliche Links dahin.
Antworten