Fehlersuche Flask, mit Vue.js

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

Dafür sind ja Exceptions da. Man schreibt den Code einmal, der umwandelt und gleichzeitig prüft. Im Fehlerfall kann man dann die Exception behandeln.
In Deinem Fall sind das ja eh zwei Schritte. Du passt den JSON-String, hast danach schon die Struktur mit den richtigen Typen, musst dann aber noch das Wörterbuch und den Wertebereich der Zahlen prüfen.
Die Funktion, die genau eine Sache macht, könnte heißen: Wandler den String in eine valide Datenstruktur um
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die schnelle Antwort.

Ich bin mir nicht sicher ob ich dich richtig verstanden habe. Ich gehe durch den JSON-String und nehme dann jeden einzelnen Wert und und prüfe ihn und wandle ihn gleichzeitig um und das macht die Funktion `convert_string_to_valid_structure`.
Was gibt die mir zurück? Nur den umgewandelten Wert oder ein Wörterbuch mit den umgewandelten Werten?
Egal was von beidem, ich habe in beiden Fällen noch ein Knoten im Kopf, wenn es um die Ausnahmebehandlung geht. Was gibt die Funktion dann zurück? Ich kann nicht bei Erfolg den Wert zurück geben und beim auftreten einer Ausnahme ein `False`, weil eine Funktion immer ein Datentyp zurück gibt und das würde auch nicht zum Funktionsnamen passen.

Also so wie ich das verstanden habe, inklusive meinen Knoten im Kopf, würde es beispielhaft so aussehen (Das Wörterbuch entsteht aus dem ursprünglichen JSON, nur etwas umfangreicher):

Code: Alles auswählen

from icecream import ic

DATA = {
    "General": {
        "pressure": "1",
        "temperature": "25",
    },
}


def convert_string_to_valid_structure(string):
    try:
        value = float(string.replace(",", "."))
    except ValueError:
        ic("Was soll ich hier zurück geben?")
    else:
        if value > 0:
            return value
        else:
            ic("Was soll ich hier zurück geben?")


def process_input_data(data):
    for field in data:
        for description, value in data[field].items():
            value = convert_string_to_valid_structure(value)
            ic(value)


def main():
    process_input_data(DATA)


if __name__ == "__main__":
    main()
Mit dem umgewandelten Wert und mit `description` mache ich in dem Beispiel noch nichts, weil ich denke, das ich da was falsch verstanden habe.
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17844
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum steht in Deinem Wörterbuch Strings statt Zahlen?
Exceptions sind dazu da, Fehler zu melden. Warum willst Du die in irgendetwas anderes umwandeln? Du schreibst ja selbst, dass das Probleme macht.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.

Wenn ich den JSON-String, den ich erhalte, in ein Wörterbuch wandle, dann sieht der so aus, mit Strings anstatt mit Zahlen. So wie ich das verstanden habe, ist jetzt meine Aufgabe, die Strings zu wandeln und wenn der Wertebereich passt, ein neues Wörterbuch dass dann Zahlen enthält zu erstellen. Mit dem ich dann meine Berechnungen starten kann.
Liege ich hier schon irgendwo falsch?

Um dem Benutzer eine Fehlermeldung anzuzeigen, muss ich in der Exception irgendwas zurück geben, dass das Programm dazu bewegt, alle weiteren Schritte nicht durchzuführen und stattdessen eine Fehlermeldung anzuzeigen. Oder macht man das irgendwie mit `raise HTTPException` , dass dann im Browser eine Fehlermeldung kommt? Das wäre dann gleich wieder von der Flask-App abhängig und keine allgemeine Funktion.

Irgendwie bekomme ich das gerade nicht so auf die Reihe. Sorry, ich hoffe ich übersehe das Offensichtliche gerade nicht. Vermutlich hat sich bei mir mal wieder eine feste Vorstellung vom Programmablauf eingebrannt, in die ich das Konzept nicht passt. Kannst du mir bitte noch einmal auf die Sprünge helfen?

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

Warum schickst Du denn einen JSON-String von Deiner Webseite mit Strings statt mit Zahlen? Da liegt ja der erste Fehler.
Wenn irgendein Fehler auftritt, liefert Flask "500 Internal Error" zurück. Willst Du etwas anderes zurückmelden, dann mußt Du halt die Exception, die Du behandeln möchtest, abfangen. Da ist die Fehlerbehandlung bei Flask nicht anders als in irgendeinem normalen Programm.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die weitere Hilfe.
Ich bin davon ausgegangen, dass das mit den Strings üblich sei. Auf jeden Fall kommen die Werte jetzt als Zahlen an. Das mache ich in JavaScript mit `parseFloat()`. Hoffe dass das noch Stand der Dinge ist?

Da ich JavaScript-seitig jetzt Zahlen schicke, die auch im gewünschten Wertebereich liegen, habe ich theoretisch alles was ich für die Berechnung brauche. Nur um jetzt keinen Verständnis-Fehler zu haben, ich prüfe in Python jetzt noch ein mal den Wertebereich und den Datentyp?

Beim wandeln in `float` zum Beispiel will ich ja den `ValueError` abfangen, dann werde ich an dieser Stelle mit dem `errorhandler` von Flask eine Fehlermeldung an die Webseite zurück geben. So zumindest mal mein Plan, ich hoffe dass ich das dann richtig verstanden habe.

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

@Dennis89: Man geht auf Serverseite immer davon aus, dass die Daten nicht über die Webseite oder den eigenen JavaScript-Code kommen müssen. Die Daten können nicht nur ungeprüft sein, sondern auch absichtlich irgendwie manipuliert worden sein um mal zu schauen was so passieren kann wenn man Sachen schickt mit denen der Programmierer nicht gerechnet hat. Das sind ungeprüfte Benutzereingaben.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen (edit: mittlerweile ist der Morgen ja vorbei :mrgreen: ) und danke für die Antwort.

Okay, das ist ehrlich gesagt doof für mich, aber sehe ich ein und will ich auf jeden Fall einbauen. Hatte das auf die leichte Schulter genommen, aber gerade scheitere ich daran voll. Ich denke das ich auch die JSON-Struktur schlecht gewählt habe, deswegen schreibe ich hier mal meine Gedanken auf.
Es gibt auf der Webseite 3 Bereiche, in denen der Benutzer Eingaben machen muss. Zum einen muss er ein Medium oder ein Gemisch aus mehreren Medien definieren, hier ein Gas. Dann muss er mindestens einen Kühler definieren. Die Anzahl der Kühler ist abhängig von seinem Anwendungsfall und dann gibt es noch 4 allgemeine Eingabefelder, die muss man einmal ausfüllen, egal wie viele Kühler man gewählt hat.
Die gekürzte JSON-Struktur sieht bei mir dann so aus:

Code: Alles auswählen

DATA = {
    "Gas": [{"gas_name": "CO2", "percent": 100}],
    "General": {
        "suction_pressure": 5,
    },
    "Kühler": [
        {
            "cooler_name": "DN 150",
            "cooling_countercurrent": True,
            "gas_humidity_after_cooler": 0,
        }
    ],
}
Jetzt habe ich schon das Problem, das ich zwei mal eine Liste habe und einmal nicht. Und was ich gar nicht bedacht habe. Ich muss auch prüfen ob der Gasname und der Kühlername zulässig ist. Genau so ob die Strömungsrichtung einen Wahrheitswert besitzt.

Ist die JSON-Struktur sinnvoll gewählt?

Meine Idee war, das ich für die Bereiche Konstanten definiere, die die zulässigen Werte enthalten, bzw. den Bereich in dem die Werte liegen dürfen. Also sowas:

Code: Alles auswählen

GAS_ENTRY_TO_VALID_RANGE = {
    "percent": (0, 100),
    "gas_name": ["CO2", "CO"]
}
Das ist auch wieder doof, weil ich einmal einen Wertebereich und einmal eine Auswahl an Strings drin habe. Und ich muss zum Schluss, wenn es mehrere Gase sind, noch prüfen ob alle Prozentangaben zusammen 100 ergeben.

Das ist so doch Mist?

Da ich keine bessere Idee hatte, habe ich beschlossen das ich die drei Bereiche getrennt voneinander prüfe. Für den Gas-teil würde das so aussehen:

Code: Alles auswählen

from icecream import ic

DATA = {
    "Gas": [{"gas_name": "CO2", "percent": 50}, {"gas_name": "CO2", "percent": 50}],
    "General": {
        "suction_pressure": 5,
    },
    "Kühler": [
        {
           "cooler_name": "DN 150",
           "cooling_countercurrent": True,
           "temperature_cooling_fluid_in": 5,
        }
    ],
}

GAS_PROPERTY_TO_VALID_VALUE = {"percent": (0, 100), "gas_name": ["CO2", "CO"]}


def validate_gas_input(description_to_input, description_to_valid_value):
    if description_to_input["gas_name"] not in description_to_valid_value["gas_name"]:
        ic("Falscher Gasname")
        # TODO Add logging
        # TODO abort(404)
    percent = description_to_input["percent"]
    if isinstance(percent, str):
        percent = percent.replace(",", ".")
    try:
        percent = float(percent)
    except ValueError:
        ic("Keine Zahl eingegeben")
        # TODO Add logging
        # TODO abort(404)
    else:
        min_value, max_value = description_to_valid_value["percent"]
        if not min_value <= percent <= max_value:
            ic("Eingabe liegt auserhalb des zulässigen Bereichs")
            # TODO Add logging
            # TODO abort(404)
    description_to_input["percent"] = percent
    return description_to_input


def process_input_data(data):
    gas_inputs = data["Gas"]
    gas_data = [
        validate_gas_input(gas_input, GAS_PROPERTY_TO_VALID_VALUE)
        for gas_input in gas_inputs
    ]
    if sum(gas["percent"] for gas in gas_data) != 100:
        ic("Die Gaszusammensetzung muss in Summe 100% betragen")
        # TODO Add logging
        # TODO abort(404)
    ic(gas_data)


def main():
    process_input_data(DATA)


if __name__ == "__main__":
    main()
Wenn ich das für die anderen zwei Bereiche mit einbaue, würde ich den Teil, in dem ich den Wertebereich prüfe, in eine Funktion auslagern, damit ich den Code nicht doppelt/dreifach schreiben muss. Aber erst mal die Bitte an euch: Macht das so Sinn? Irgendwie sieht das für mich komisch aus.

Auf das `abort` will ich dann mit einer Fehlerseite reagieren, wie in der Doku hier beschrieben. Würde aber erst gerne die Logik ordentlich hinbekommen, bevor ich mich darum dann kümmere.

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

@Dennis89: Das ist immer so eine Frage wie viel Aufwand man treiben möchte. An dieser Stelle würde ich aufpassen das ich mir da am Ende keine Bibliothek zur Validierung nachbaue, wo es bereits mehrere zur Auswahl gibt.

Der `isinstance()`-Test sieht so aus als wenn da entweder eine Zahl oder eine Zeichenkette rein kommen kann. Das sollte IMHO an der Stelle bereits entschieden und eine Zahl sein.

Das ``# TODO abort(404)`` gehört nicht in die Funktion zur Validierung. Die sollte nicht wissen müssen wie/wo die validierten Daten hinterher verwendet werden. 404 wäre auch falsch, das heisst ja das die Seite/Ressource nicht gefunden werden konnte.

Bei der Summe der Prozentangaben wäre ich bei `float()` vorsichtig mit *genau* 100:

Code: Alles auswählen

In [76]: 99.8 + 0.1 + 0.1 == 100
Out[76]: False
Wahrscheinlich nicht die beste Wahl für die Validierung, aber hier mal ein Beispiel mit `glom`, was ich gerne Nehme wenn ich was mit verschachtelten ”JSON”-Strukturen anstellen muss, und das eher deklarativ angehen möchte:

Code: Alles auswählen

#!/usr/bin/env python3
from numbers import Number

from glom import And, Check, M, Match, MatchError, glom

DATA = {
    "Gas": [
        {"gas_name": "CO2", "percent": 50},
        {"gas_name": "CO2", "percent": 50},
    ],
    "General": {
        "suction_pressure": 5,
    },
    "Kühler": [
        {
            "cooler_name": "DN 150",
            "cooling_countercurrent": True,
            "temperature_cooling_fluid_in": 5,
        }
    ],
}

DATA_SPEC = Match(
    {
        "Gas": And(
            lambda gases: sum(gas["percent"] for gas in gases) == 100,
            [
                {
                    "gas_name": Check(one_of=["CO2", "CO"]),
                    "percent": 0 < M <= 100,
                }
            ],
        ),
        "General": {"suction_pressure": M >= 0},
        "Kühler": And(
            lambda coolers: len(coolers) > 0,
            [
                {
                    "cooler_name": str,
                    "cooling_countercurrent": bool,
                    "temperature_cooling_fluid_in": Number,
                }
            ],
        ),
    }
)


def main():
    try:
        data = glom(DATA, DATA_SPEC)
    except MatchError:
        print("Booohooo.")
    else:
        print("\\o/", data)


if __name__ == "__main__":
    main()
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.

Da ich keine Bibliothek zur Validierung kenne und auch gar nicht auf die Idee kam, das es da schon etwas geben könnte, hätte ich soviel Aufwand betrieben, bis es so ordentlich wie möglich ist.
Das mit `isinstance` habe ich eingebaut, weil du meintest, dass man die kommenden Daten manipulieren kann um zu schauen, mit was der Programmierer nicht gerechnet hat. Ich hab damit gerechnet, dass man da ein String wie zum Beispiel "3,4" schicken könnte.

Soll ich dann für die Summe der Prozentangabe einen Bereich von 99.999 bis 100.001 oder so ähnlich vorgeben?

Das mit `glom` sieht gut aus und ich habe gar nicht sooooo lange gebraucht um es zu verstehen (meine ich zumindest). Aber es hört sich von dir ja nicht unbedingt so an, als wenn ich das jetzt verwenden soll. 🤯

Auf der Suche habe ich Pydantic gefunden. Sieht zumindest etwas populär aus, gibts da Meinungen dazu?

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

@Dennis89: Ich bin halt nicht gewohnt so etwas wie `isinstance()` in solchen Fällen selbst zu schreiben. In dem `glom`-Beispiel ist das ja auch drin, aber halt ”versteckt” weil das die Aktion ist, die durch `Match` gemacht wird, wenn ein Datentyp in der Struktur verwendet wird. Wobei speziell in Deinem Code ist das ja nicht nur ein Test, sondern Teil einer automagischen Umwandlung von Zeichenkette in Gleitkommazahl.

Wegen Gleitkommawerte vergleichen könntest Du Dir mal `math.isclose()` anschauen.

Ich weiss nicht ob ich `glom` für Validierung empfehlen würde. Ich habe das schon dafür verwendet weil ich es sowieso schon für das deklarative erstellen oder umbauen von JSON-ähnlichen ”Dokumenten” verwendet habe. Validierung war bei der Bibliothek auch noch nicht dabei als ich angefangen hatte die zu verwenden.

Was ich sonst noch so kannte war `cerberus`. Das ist wirklich nur zum Validieren von solchen Daten. Beispiel:

Code: Alles auswählen

#!/usr/bin/env python3
from cerberus import Validator

DATA = {
    "Gas": [
        {"gas_name": "CO2", "percent": 50},
        {"gas_name": "CO", "percent": 50},
    ],
    "General": {
        "suction_pressure": 5,
    },
    "Kühler": [
        {
            "cooler_name": "DN 150",
            "cooling_countercurrent": True,
            "temperature_cooling_fluid_in": 5,
        }
    ],
}


def check_percentage_sum(field, value, error):
    total = sum(gas["percent"] for gas in value)
    if total != 100:
        error(
            field,
            f"the 'percent' values must add up to 100%, not {total}%",
        )


SCHEMA = {
    "Gas": {
        "type": "list",
        "schema": {
            "type": "dict",
            "schema": {
                "gas_name": {"type": "string", "allowed": ["CO2", "CO"]},
                "percent": {"type": "number", "min": 0, "max": 100},
            },
        },
        "check_with": check_percentage_sum,
    },
    "General": {
        "type": "dict",
        "schema": {"suction_pressure": {"type": "number", "min": 0}},
    },
    "Kühler": {
        "type": "list",
        "minlength": 1,
        "schema": {
            "type": "dict",
            "schema": {
                "cooler_name": {"type": "string"},
                "cooling_countercurrent": {"type": "boolean"},
                "temperature_cooling_fluid_in": {"type": "number"},
            },
        },
    },
}


def main():
    validator = Validator(require_all=True)
    if validator.validate(DATA, SCHEMA):
        print(validator.document)
    else:
        print(validator.errors)


if __name__ == "__main__":
    main()
Das hat den Vorteil, dass das Schema selbst wieder JSON sein kann/könnte. Im Beispiel jetzt nicht ganz weil da eine Funktion als Wert drin steckt, das kann man aber umgehen in dem man das als eigene Regel angibt die man auf eine von `Validator` abgeleiteten Klasse als Methode implementiert.

Dann gibt es noch `marshmallow` das auch (de)serialisieren kann, wo das Schema als Klassen angegeben wird:

Code: Alles auswählen

#!/usr/bin/env python3
from pprint import pprint

from marshmallow import Schema, ValidationError, fields, validate, validates

DATA = {
    "Gas": [
        {"gas_name": "CO2", "percent": 50},
        {"gas_name": "CO", "percent": 50},
    ],
    "General": {
        "suction_pressure": 5,
    },
    "Kühler": [
        {
            "cooler_name": "DN 150",
            "cooling_countercurrent": True,
            "temperature_cooling_fluid_in": 5,
        }
    ],
}


class GeneralSchema(Schema):
    suction_pressure = fields.Number(
        validate=validate.Range(min=0), required=True
    )


class GasSchema(Schema):
    name = fields.String(
        data_key="gas_name",
        validate=validate.OneOf(["CO2", "CO"]),
        required=True,
    )
    percent = fields.Number(
        validate=validate.Range(0, 100, min_inclusive=False), required=True
    )


class CoolerSchema(Schema):
    name = fields.String(data_key="cooler_name", required=True)
    cooling_countercurrent = fields.Boolean(required=True)
    temperature_cooling_fluid_in = fields.Number(required=True)


class DataSchema(Schema):
    gases = fields.List(
        fields.Nested(GasSchema), data_key="Gas", required=True
    )
    general = fields.Nested(GeneralSchema, data_key="General", required=True)
    coolers = fields.List(
        fields.Nested(CoolerSchema), data_key="Kühler", required=True
    )

    @validates("gases")
    def validate_gases(self, data, **_kwargs):
        total = sum(gas["percent"] for gas in data)
        if total != 100:
            raise ValidationError(
                f"total percent must be 100, not {total}", "gases"
            )


def main():
    pprint(DataSchema().load(DATA))


if __name__ == "__main__":
    main()
Die Ausgabe, wo man sehen kann, dass da auch die Namen der Schlüssel ”korrigiert” werden können, also jetzt alles in englisch, in kleiner Schreibweise und die "name"-Schlüssel ohne die etwas redundanten Prefixe:

Code: Alles auswählen

{'coolers': [{'cooling_countercurrent': True,
              'name': 'DN 150',
              'temperature_cooling_fluid_in': 5.0}],
 'gases': [{'name': 'CO2', 'percent': 50.0}, {'name': 'CO', 'percent': 50.0}],
 'general': {'suction_pressure': 5.0}}
Das dann mal als Basis für das nächste Beispiel `attrs` + `cattrs` (wobei die Kombination auch konfiguriert werden kann das Namen zwischen Objekten und Schlüsseln ”übersetzt” werden können:

Code: Alles auswählen

#!/usr/bin/env python3
from attrs import define, field
from attrs.validators import (
    and_,
    ge as is_greater_or_equal,
    gt as is_greater,
    in_,
    le as is_less_or_equal,
    min_len,
)
from cattrs import structure
from prettyprinter import install_extras, pprint

install_extras(["attrs"])

DATA = {
    "gases": [
        {"name": "CO2", "percent": 50},
        {"name": "CO", "percent": 50},
    ],
    "general": {
        "suction_pressure": 5,
    },
    "coolers": [
        {
            "name": "DN 150",
            "cooling_countercurrent": True,
            "temperature_cooling_fluid_in": 5,
        }
    ],
}


@define
class General:
    suction_pressure: float = field(validator=is_greater_or_equal(0))


@define
class Gas:
    name: str = field(validator=in_(["CO2", "CO"]))
    percent: float = field(
        validator=and_(is_greater(0), is_less_or_equal(100))
    )


@define
class Cooler:
    name: str
    cooling_countercurrent: bool
    temperature_cooling_fluid_in: float


@define
class Data:
    gases: list[Gas] = field()
    general: General
    coolers: list[Cooler] = field(validator=min_len(1))

    @gases.validator
    def _validate_gases(self, _attribute, value):
        total = sum(gas.percent for gas in value)
        if total != 100:
            raise ValueError(f"total percent must be 100, not {total}")


def main():
    pprint(structure(DATA, Data))


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

Data(
    gases=[Gas(name='CO2', percent=50.0), Gas(name='CO', percent=50.0)],
    general=General(suction_pressure=5.0),
    coolers=[
        Cooler(
            name='DN 150',
            cooling_countercurrent=True,
            temperature_cooling_fluid_in=5.0
        )
    ]
)
Pydantic mag ich nicht so weil ich ja schon auf `attrs` für Klassen und Boilerplate schreiben vermeiden setze.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Hi,

ja stimmt, ich hätte nur prüfen sollen und nicht wandeln.
Schön das es `isclose` gibt, das hätte ich jetzt sonst nachgebaut.

Vielen Dank für die vielen Beispiele!
`marshmallow` gefällt mir schon ganz gut. Weil ich das relativ schnell gut lesen konnte und ich es übersichtlich finde.
`cerberus` ist zwar auch nicht so schwer zu verstehen, aber das finde ich, wie bei `glom` auch, nicht so übersichtlich.
`attrs` + `cattrs` hat mich erst kurz "erschlagen", aber genau genommen ist dass dann doch fast am einfachsten zu lesen und zu verstehen. Dazu trägt natürlich auch das umbenennen der Importe bei. Ich denke dass ich das Beispiel mal auf meine Daten anwenden werde. Ich habe mir heute noch ein mal Gedanken gemacht und ich habe Durchmesserangaben, die von einander abhängig sind. Ich denke das könnte ich hier ganz gut einbauen.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
grubenfox
User
Beiträge: 454
Registriert: Freitag 2. Dezember 2022, 15:49

wenn ich mich da mal einklinken darf: beim Thema Validieren von ... denke ich, analog zu XML und XML Schema, immer gleich an '...schema'. Hier also an JSON Schema.

Warum nicht dieses bzw. was sind da die Unterschiede zu den oben genannten anderen Möglichkeiten? (Sollten wir dafür ein neues Thema aufmachen?)

Und passend zum Punkt 'fehlende serverseitige Validierung' etwas vom Wochenende: https://www.golem.de/news/ungeschuetzte ... 85242.html :o
Wenn man z.b. mit dem Requests-Package direkt mit einem Webserver in Kontakt tritt, dann kann man da alles mögliche rüber schicken....
Benutzeravatar
__blackjack__
User
Beiträge: 13270
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Hm, wenn ich an XML denke wird mir schon leicht übel, wenn ich dann an XML Schema denke, möchte ich schreiend weglaufen, womit JSON Schema schon mal einen ziemlich schlechten Start hat. Gerade mal auf die Seite geschaut: Und ja es ist eine Monsterspezifikation die sich anscheinend auf mehrere Unterspezifikationen aufteilt.

Konkret hatte sich Dennis89 ja schon gegen Cerberus ausgesprochen. JSON-Schema sieht sehr ähnlich so aus, nur dass das deutlich umfangreicher ist. Ob es umfangreich genug ist um die „alle percent-Werte in diesen Objekten müssen zusammen 100% ergeben“-Einschränkung auszudrücken, keine Ahnung, aber man wird da nicht einfach eine Python-Funktion für schreiben können.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Benutzeravatar
grubenfox
User
Beiträge: 454
Registriert: Freitag 2. Dezember 2022, 15:49

__blackjack__ hat geschrieben: Donnerstag 23. Mai 2024, 13:36 Hm, wenn ich an XML denke wird mir schon leicht übel, wenn ich dann an XML Schema denke, möchte ich schreiend weglaufen, womit JSON Schema schon mal einen ziemlich schlechten Start hat. Gerade mal auf die Seite geschaut: Und ja es ist eine Monsterspezifikation die sich anscheinend auf mehrere Unterspezifikationen aufteilt.
;) ich hätte da so ein paar XSD's zur Hand.... schaue ich mir auch lieber nicht an. Aber wenn man die Betonung auf JSON legt, dann erscheint mir JSON im Vergleich zu XML als die etwas schönere Wahl. Wo bei ich bisher auch nur flüchtig auf die Seite geschaut habe. Bisher war der Gedanke sich mal JSON Schema ernsthafter zu beschäftigen von theoretischer Natur. Aber wenn 'schreiend' und 'laufend' dann beim Monster-Komplex EDIFACT!
Benutzeravatar
sparrow
User
Beiträge: 4246
Registriert: Freitag 17. April 2009, 10:28

XML ist JSON für Boomer.

Habe ich mal gehört.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich wollte noch einen Hinweis geben, falls jemand mal ähnliches vor hat und auch `attrs` verwenden will und möglicherweise die Doku nicht unbedingt angeschaut hat.
Da es in meinem Fall für den Benutzer angenehmer ist, die Werte in Einheiten anzugeben, in denen ich aber nicht rechne, habe ich mir überlegt, wann und wo ich die am besten umrechne. Auf der Suche habe ich in der `attrs` - Doku von `field` das Argument `converter` entdeckt. Man muss nur beachten, dass `converter` *vor* `validator` ausgeführt wird. 😎

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

Wo Du gerade „Einheiten“ erwähnst — kennst Du https://pint.readthedocs.io ? 🍺
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Benutzeravatar
Dennis89
User
Beiträge: 1226
Registriert: Freitag 11. Dezember 2020, 15:13

Moin,

ne kenne ich natürlich nicht. Sieht auf jeden Fall gut aus und die Liste mit den möglichen Einheiten ist auch sehr umfangreich.
Danke dafür 🍻

Ich muss mal mehr dran denken, das ich bei Problemen mal schauen könnte, ob da schon mal jemand was gemacht hat.

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

Guten Morgen,

da ich nun schönen Code habe um meiner Berechnung passende Eingabewerte zu schicken, läuft die Berechnung auch sauber durch. Allerdings habe ich jetzt wieder eine "Struktur" - Frage.

Ziel ist es auf der Webseite die berechneten Werte anzuzeigen, das sind 7 Stück. Die würde ich mit passenden Namen als Schlüssel in ein Wörterbuch stecken. Ich kenne aber auch schon das Ziel danach, dass ist nämlich eine PDF-Erstellung und da kommen nicht nur die Ergebnisse rein, sondern auch alle Eingaben. Würde man jetzt die Eingaben und die Ergebnisse getrennt lassen? Packe ich alles zusammen in ein Objekt (wenn ja, welches?) Die Eingaben sind ja schon in `catters.structure`. Die Frage tauchte wieder mal bei der Namenswahl auf. Weil wenn ich alles in ein Objekt packe, dann kann ich das nicht mit "result" bezeichnen, wenn ich das getrennt lasse, wird das gefühlt wieder unübersichtlicher Coede.

Bis jetzt werden die Ergebnisse nur an einen Namen gebunden. Habe mir auch schon überlegt eine Klasse zu erstellen, aber das wäre wieder eine `__init__` voll mit `None` - Werten, das sieht irgendwie immer komisch aus.

Könnt ihr mir bitte sagen, wie ihr das strukturieren würdet? Würde das sehr gerne so ordentlich wie möglich machen.



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