Fehlersuche Flask, mit Vue.js

Django, Flask, Bottle, WSGI, CGI…
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Strings eignen sich nicht als IDs, und Du brauchst doch eh eine Tabelle mit allen Gasen. Ist der Name wirklich die einzige Eigenschaft?
Eine ID muß nur eindeutig sein, wenn sie der Primärschlüssel ist; für Tabellen die nur zwei andere Tabellen verknüpfen ist es nicht unüblich, keinen Primärschlüssel zu haben.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Naja, da würde man die beiden IDs als Primärschlüssel setzen, was dann auch gleich verhindert das man die gleichen Datensätze aus den anderen Tabellen mehr als einmal verknüpft.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für eure Antworten.

Zur Zeit sind alle Gase die zur Auswahl möglich sind in einer Datei, genau so wie die Kühler, die zur Auswahl stehen. Daran habe ich noch gar nicht gedacht, ich könnte die Daten natürlich in die Datenbank schreiben.

Der Benutzer wählt nur den Gasname aus und gibt an wie viel Prozent er von diesem Gas haben möchte, bis er bei 100% ist. Die Eigenschaften, die für die Berechnung benötigt werden, ergeben sich erst dann wenn alle Bestandteile und Mengenanteile bekannt sind und werden dann im Berechnungsdurchgang abgefragt oder berechnet. Für mich würde das bedeuten, das pro Auftrag nur der Gasname und der dazugehörige Prozentanteil gespeichert werden muss.

Das ganze Zusammenspiel, wie das nachher funktionieren soll, kann ich mir gerade noch nicht wirklich vorstellen und würde das daher herunter brechen.
Wenn ich das richtig verstanden habe, dann erstelle ich jetzt eine Tabelle mit den Kühlerdaten wobei jeder Kühler eine eigene ID (eine Zahl) bekommt.
Macht es dann auch Sinn eine Tabelle zu erstellen mit den Gasnamen? Also ID und Gasname? Hätte ich jetzt so gemacht.

Danach dann eine Tabelle mit den Eingabewerten für den Kühler, darin findet sich die ID des ausgewählten Kühlers wieder und die zusätzlichen Eingaben?
Als zusätzliche Herausforderung habe ich hier noch folgendes: Mein Programm lässt zu, das man sich einen individuellen Kühler erstellen kann. Die Eigenschaften, die sich hinter dem Kühlername verbiergen, wie zum Beispiel Rohrdurchmesser, Rohranzahl,... kann man selbst festlegen. Wie ich das mit der Datenbank anstellen soll, kann ich mir auch noch gar nicht vorstellen.

Das wären jetzt meine ersten Ziele. Es sei denn, es macht Sinn anders vorzugehen?


Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn man eine Datenbank hat, macht es wenig Sinn, Gase und andere Daten in anderen Dateien zu speichern.
Wenn man ein Datenbankdesign macht, ist es sinnvoll, vorzusorgen, dass die Tabellen einfach zu erweitern sind.
Etwas "individuelles" gibt es nicht, wenn es Standardkühler und Individualkühler gibt, dann landen die in der selben Tabelle, eventuell noch markiert, wessen Individualkühler das ist, damit man diese besser für die Eingabe filtern kann.
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend,

danke für die Antwort.
Das mit dem Individualkühler lasse ich vor erst weg und schaue mal, dass ich erst den einfachen Fall hinbekomme.

Ich würde jetzt so folgende Tabellen erstellen:

Code: Alles auswählen

CREATE TABLE coolers (
	id integer primary key auto_increment,
	name varchar(20),
	inside_diameter_outer_pipe integer,
        outside_diameter_cooling_pipe integer,
        inside_diameter_cooling_pipe integer
);

CREATE TABLE gases (
	id integer primary key auto_increment,
	name varchar(20)
);


CREATE TABLE cooler (
	id integer primary key auto_increment,
	cooler_id integer,
	order_number integer,
	cooling_countercurrent bool,
        gas_humidity_after_cooler float,
        gas_humidity_before_cooler float,
        gas_pressure float,
        gas_temperature_in float,
        gas_temperature_out float,
        inside_diameter_cooling_pipe float,
        inside_diameter_outer_pipe float,
        number_of_pipes float,
        outside_diameter_cooling_pipe float,
        temperature_cooling_fluid_in float,
        temperature_cooling_fluid_out float,
        number_of_cooler integer
);

CREATE TABLE general (
	id integer primary key auto_increment,
	order_number integer,
	glykohl_percent float,
        normvolume_flow float,
        suction_pressure float,
        temperature_suction_filter float
);
        
CREATE TABLE gas (
	id integer primary key auto_increment,
	order_number integer,
	gas_id integer,
        percentage float,
);

Würde das, auch mit der `order_number` so passen, damit ich nachher danach suchen kann?
Sorry ich muss jetzt vermutlich viele Zwischenschritte nachfragen, sonst verrenne ich mich wieder.

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Verwendung von Plural und Singular ist nur verwirrend. Ein Gas ist ein Gas. Ein Cooler ist ein Cooler; das andere ist ja die Verbindung zwischen Gas und Bestellung. order_number wäre besser order_id, zu der dann noch die Order-Tabelle fehlt.
Warum ist inside_diameter_outer_pipe mal ein integer und mal float? Und warum Doppelst Du diese Information?
Ich würde auch sagen, dass viele der Parameter generisch für einen Cooler sind und nicht für jede Bestellung individuell ist.
Was soll der General?
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.
Hm ich dachte mir die Plural-Tabellen enthalten die Gase und die Kühler, die zur Auswahl stehen.

In den anderen Tabellen wird dann der Kühler und das Gas gespeichert, welches für den Auftrag ausgewählt wurde. Habe ich das Prinzip falsch verstanden?

Sorry, die doppelte Information und auch der Integer-Typ war falsch und unachtsam von mir. Die Info gehört als float in die Tabelle mit den zur Verfügung stehenden Kühler.

„General“ sind die allgemeinen Eingaben, die gemacht werden und nicht den Kühler oder das Gas beschreiben.

Da du sagst, dass die Order-Tabelle fehlt, gehe ich davon aus, dass ich den Aufbau nicht verstanden habe. Gehört meine „Gas“ und „Cooler“ Tabellen zusammengefasst in die Order-Tabelle?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Gute Namen sind für das Verständnis wichtig. Eine Tabelle, die ein Gas beschreibt, kann halt nicht Gase heißen, und eine Tabelle die einer Bestellung ein Gas zuordner, heißt halt sowas wie OrderGasLink. General ist für einen Namen zu generell. Warum nicht einfach Order?
Wie willst Du Kühler und Gase in eine Tabelle packen?
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend,

danke für deine weitere Hilfe.
Ich glaube aber, das ich eine falsche Vorstellung davon habe, wie das aussehen soll.
Meine Gedanken zu den Tabellen waren: "Gase" soll alle möglichen Gasnamen enthalten, die der Benutzer auswählen kann, deswegen habe ich die Mehrzahl gewählt. Das gleiche habe ich mir bei "Coolers" gedacht.
Die Tabelle "Gas" beschreibt dann das ausgewählte Gas pro Auftrag, so verhält sich "Cooler" auch. Die zwei ändere ich jetzt um in "OrderGasLink" und "OrderCoolerLink". Soweit habe ich das hoffentlich richtig verstanden. (Ist CamleCase grundsätzlich die Schreibweise für Tabellennamen in SQL?)
Das ich "General" mit "Order" ersetze habe ich auch verstanden.

Dann hätte ich das hier (noch mit Coolers und Gases, weil ich nicht weis wo ich die Infos sonst hinpacken soll):

Code: Alles auswählen

CREATE TABLE Coolers (
	id integer primary key auto_increment,
	name varchar(20),
	inside_diameter_outer_pipe float,
        outside_diameter_cooling_pipe float,
        inside_diameter_cooling_pipe float,
        number_of_pipes float,
);

CREATE TABLE Gases (
	id integer primary key auto_increment,
	name varchar(20)
);


CREATE TABLE OrderCoolerLink (
	id integer primary key auto_increment,
	cooler_id integer,
	order_number integer,
	cooling_countercurrent bool,
        gas_humidity_after_cooler float,
        gas_humidity_before_cooler float,
        gas_pressure float,
        gas_temperature_in float,
        gas_temperature_out float,
        temperature_cooling_fluid_in float,
        temperature_cooling_fluid_out float,
        number_of_cooler integer
);

        
CREATE TABLE OrderGasLink (
	id integer primary key auto_increment,
	order_number integer,
	gas_id integer,
        percentage float,
);

CREATE TABLE Order (
	id integer primary key auto_increment,
	order_number integer,
	glykohl_percent float,
        normvolume_flow float,
        suction_pressure float,
        temperature_suction_filter float,
);
Haut das so hin?

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum immer noch Gases?
Der Link besteht aus einer order_id und einer gas_id, keine order_number.
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Weil ich nicht weis, wo ich die Gasnamen, die zur Auswahl stehen, sonst reinschreiben soll.
Bis jetzt habe ich eine *.json-Datei, die so aussieht:

Code: Alles auswählen

{"valid_gases": [
    "1-Butene",
    "Acetone",
    "Air",
    "Ammonia",
    "Argon",
    "Benzene",
    ...
    ]
}
Und die brauche ich nicht mehr, wenn ich eine Datenbank einbinde, deswegen die Gases.

Okay ja, "order_number" wäre ja doppelt und unnötig.

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Es geht um den Namen. Tabellen werden im Singular benannt, nicht im Plural.
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Achsooo, oh man sorry, das habe ich nicht begriffen.

Ihr habt mir das mit den Listen und Plural so angewöhnt, dass es sich für mich jetzt falsch anhört, eine Tabelle die mehrere Gase enthält "Gas" zu nennen 😃

Danke für deine Geduld. Ich erstelle jetzt die Tabellen, fülle "Gas" und "Cooler" und schau das ich die *.json-Abfrage durch die Datenbankabfrage ersetze. Wenn ich das hinbekomme, dann melde ich mich wieder, wenn es an das speichern geht.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Die Beschreibung der Tabelle beschreibt aber immer nur einen Datensatz. Das ist sozusagen die Klasse. Die tatsächlichen Daten in den Tabellen sind dann die Objekte dieser Klasse.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Erklärung, so ähnlich habe ich es mir letztendlich zusammen gereimt.
Letztendlich aber immer wieder interessant, wie man aneinander vorbei reden kann. Für mich ein großer Lerneffekt, für euch leider eine kleine Geduldsprobe 🫣

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich steige bei der Einbindung von Flask-SQLAlchemy in meinen Code nicht durch.
In der Doku wie auch in anderen Beispielen wird immer erst mal eine Klasse für die Datenbank erstellt, zum Beispiel sowas:

Code: Alles auswählen

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String, unique=True, nullable=False)
    email = db.Column(db.String)
Zum einen erbt die Klasse von `db` das auf Modulebene erstellt ist zum anderen benötige ich so eine Klasse doch gar nicht? Ich habe meine Datenbank manuell erstellt und will jetzt darauf zugreifen und auf Modulebene gibts bei mir nichts. Rein mit `SQLAlchemy` würde ich das so machen:

Code: Alles auswählen

#!/usr/bin/env python
from sqlalchemy import create_engine, text


def main():
    engine = create_engine("mysql+pymysql://dennis:Dennis89@127.0.0.1:3306/CoolerCalculation", echo=True)

    with engine.connect() as connection:
        queries = connection.execute(text("SELECT * FROM Cooler"))
        for query in queries:
            print(query.name)


if __name__ == '__main__':
    main()
Keine Sorge wegen Passwort und Username, ist nur ein Beispiel.
Ich finde ehrlich gesagt gar keinen wirklichen Startpunkt und kann meine Frage nicht wirklich konkret stellen. In der `main` habe ich zu meiner `app` die Datenbankverbindung angegeben und übergebe die Verbindung an meine FlaskView-Klasse. Dann kann ich zwar nach dem obigen Schema auf meine Daten zugreifen und mit `all()` habe ich eine Liste, mit den Daten. Und die muss ich dann manuell in JSON wandeln um sie an den Server zu senden? Das kann ich mir nicht so wirklich vorstellen.

Ist das mit `text` der richtige Ansatz? Bekomme ich die Daten irgendwie einfach in ein Wörterbuch/JSON um es dem Server zu senden?
Es wäre mir sehr geholfen, wenn ich einen sinnvollen Startpunkt hätte. Also wie integriere ich Flask-SQLAlchemy richtig und frage richtig ab, bezogen auf meinen Code. Der würde, reduziert, gerade so aussehen:

Code: Alles auswählen

#!/usr/bin/env python
from flask_weasyprint import render_pdf

from attrs import define, field

from cattrs import structure, unstructure
from cooler_calculation import calculate_cooler
from flask import Flask, jsonify
from flask_classful import FlaskView, request, route
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
from make_template import make_template


@define
class App(FlaskView):
    database = field()
    user_input = None
    results = None

    @route("/get_cooler_data", methods=["GET"])
    def get_cooler_data(self):
        # TODO query database
        cooler_data = self.database.session.execute(text('SELECT * FROM Cooler'))
        if request.method == "GET":
            # TODO replace with database connection
            return self.technical_data["cooler_data"]

    @route("/get_valid_gases", methods=["GET"])
    def get_valid_gases(self):
        # TODO query database
        # valid_gas_names = self.database.session.execute()
        if request.method == "GET":
            # TODO replace with database connection
            return self.technical_data["valid_gases"]

    @route("/process_calculation", methods=["POST", "GET"])
    def index(self):
        if request.method == "POST":
            self.process_input(request.get_json())
            return jsonify(request.get_json())
        if request.method == "GET":
            data = self.merge_result_and_input()
            return render_pdf(make_template(data))

    def merge_result_and_input(self):
        user_input = unstructure(self.user_input)
        results = {"results": unstructure(self.results)}
        return user_input | results

    # def process_input(self, input_data):
    #     try:
    #         self.user_input = structure(input_data, Data)
    #     except ValueError as error:
    #         ic(error)
    #     else:
    #         gas_to_percent = {gas.name: gas.percent for gas in self.user_input.gases}
    #         self.results = calculate_cooler(
    #             self.user_input.coolers,
    #             gas_to_percent,
    #             self.user_input.general.glykohl_percent,
    #             self.user_input.general.normvolume_flow,
    #             self.user_input.general.temperature_suction_filter,
    #             self.user_input.general.suction_pressure,
    #         )


def main():
    database = SQLAlchemy()
    app = Flask(__name__)
    app.config.from_object(__name__)
    app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://dennis:Dennis89@127.0.0.1:3306/CoolerCalculation"
    CORS(app, resources={r"/*": {"origins": "*"}})
    database.init_app(app)
    App.register(
        app,
        route_base="/",
        init_argument=database,
    )
    app.run()


if __name__ == "__main__":
    main()
Liegt der Fehler schon irgendwo hier im Code, dass keine sinnvolle Integration möglich ist?

Vielen Dank schon einmal!

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo nochmals,

ich habe mich noch mal mit der Umwandlung in ein Wörterbuch beschäftigt und `_asdict` gefunden.

Code: Alles auswählen

#!/usr/bin/env python
from sqlalchemy import create_engine, text


def main():
    engine = create_engine("mysql+pymysql://dennis:Dennis89@127.0.0.1:3306/CoolerCalculation", echo=True)

    with engine.connect() as connection:
        queries = connection.execute(text("SELECT * FROM Cooler"))
        for query in queries.all():
            print(query._asdict())


if __name__ == '__main__':
    main()
Ich bin jetzt verwundert, weil ich auf dem Stand bin, dass der führende Unterstrich mir signalisiert, dass die Methode nicht öffentlich ist, ich also nicht aufrufen soll. Doch die wird auch in der Doku beschrieben:
https://docs.sqlalchemy.org/en/14/core/ ... ow._asdict

"Darf" ich das jetzt nutzen?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Edit: Aber irgendwie ist das auch immer noch umständlich, ich bekomme jetzt lauter solche Wörterbücher:

Code: Alles auswählen

{'id': 1, 'name': 'DN 40', 'inside_diameter_outer_pipe': 41.1, 'outside_diameter_cooling_pipe': 6.0, 'inside_diameter_cooling_pipe': 4.0, 'number_of_pipes': 17.0}
Ursprünglich habe ich aber sowas:

Code: Alles auswählen

{'DN 40': {'number_of_pipes': 17, 'inside_diameter_outer_pipe': 41.1, 'outside_diameter_cooling_pipe': 6, 'inside_diameter_cooling_pipe': 4}, ...}
an den Browser gesendet.

Ich würde dass dann jetzt so lösen:
[code]#!/usr/bin/env python
from sqlalchemy import create_engine, text


def format_to_dict(coolers):
    names = [cooler['name'] for cooler in coolers]
    coolers = [
        {key: value for key, value in cooler.items() if key != 'name'}
        for cooler in coolers
    ]
    return dict(zip(names, coolers))
    

def main():
    engine = create_engine("mysql+pymysql://dennis:Dennis89@127.0.0.1:3306/CoolerCalculation", echo=True)

    with engine.connect() as connection:
        queries = connection.execute(text("SELECT * FROM Cooler"))
        cooler_data = format_to_dict([query._asdict() for query in queries.all()])


if __name__ == '__main__':
    main()
Oder die *.html - Datei ändern.
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Da erst zwei parallele Listen zu erzeugen ist unschön. Das ginge ja auch so:

Code: Alles auswählen

def format_to_dict(coolers):
    return {
        cooler["name"]: {
            key: value for key, value in cooler.items() if key != "name"
        }
        for cooler in coolers
    }
Warum `text()`? Beziehungsweise warum SQLAlchemy wenn das dann überhaupt gar nicht verwendet wird? Das benutzt man ja gerade um eben *nicht* SQL als Zeichenketten zusammenbasteln zu müssen. Mindestens. Wenn man nicht gar den ORM-Teil verwendet.

`queries` und `query` sind als Namen falsch. Das Ergebnis von dem `execute()` sind ja keine Abfragen und ein einzelner Cooler-Datensatz ist keine Abfrage. Das ist ja schon die Antwort auf eine Abfrage was da von `execute()` zurück kommt. Allgemein `rows` und `row`.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen und danke für die Antwort.

SQLAlchemy wird bis jetzt nicht wirklich verwendet, weil ich nicht rausfinden konnte, wie ich es in meinem Fall verwenden kann. In der Doku wie in Tutorials wird immer erst mal eine Klasse erzeugt, die ich hier so aber nicht habe und wenn ich es richtig verstehe, macht das bei mir auch keinen Sinn. Darauf bezieht sich meine Frage hier. `text` war dann die Möglichkeit, die ich für die Abfrage in meinem Fall gefunden habe. Würde ich aber natürlich gerne ändern.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten