Skip to content

Flask - Erste Schritte

Allgemeine Diskussionen
  • Ich habe ja meine letzten kleineren Projekte mit Pywebio gemacht. Aber, so langsam komme ich damit an seine Grenzen. Da fiel mir ein, das es für Python auch noch andere Frameworks gibt. Zwei die mir spontan einfielen waren

    Ich hatte beide schon mal getestet, aber da waren meine Python Kenntnisse noch sehr begrenzt. Mittlerweile bin ich da schon ein Stück weiter. Django machte damals den komplizierten Eindruck, so das meine Wahl auf Flask fiel.

    Da man heute einen prima Personal Trainer hat, ChatGPT, habe ich diesen mal gebeten mir ein kurzes Beispiel zu generieren. Grundlage ist meine private Aktienverwaltung, die auf der ersten Seite mein Depot anzeigt usw. Die Kurse scrape ich von einer Webseite usw.

    Kurze Zeit später hatte ich ein funktionierendes Beispiel, das ich bis jetzt gut ausgebaut habe. Da Flask den Logikteil vom HTML-Teil sehr gut trennt, kann man das relativ einfach überblicken. Nach kurzem Ausprobieren, hatte ich das Konzept in seinen Grundzügen verstanden.

    Der Anfang sah so aus.

    app.py

    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    # Sample data for our table
    DATA = [
        {"id": 1, "name": "John", "email": "john@example.com"},
        {"id": 2, "name": "Jane", "email": "jane@example.com"},
        {"id": 3, "name": "Doe", "email": "doe@example.com"}
    ]
    
    @app.route('/')
    def index():
        return render_template('index.html', data=DATA)
    
    if __name__ == "__main__":
        app.run(debug=True)
    

    templates/index.html

    <!DOCTYPE html>
    <html>
    <head>
        <title>Flask Table Example</title>
    </head>
    <body>
        <!-- Navigation Menu -->
        <ul>
            <li><a href="#">Menu 1</a></li>
            <li><a href="#">Menu 2</a></li>
            <li><a href="#">Menu 3</a></li>
            <li><a href="#">Menu 4</a></li>
            <li><a href="#">Menu 5</a></li>
        </ul>
    
        <!-- Table Displaying Data -->
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Email</th>
                </tr>
            </thead>
            <tbody>
                {% for row in data %}
                <tr>
                    <td>{{ row.id }}</td>
                    <td>{{ row.name }}</td>
                    <td>{{ row.email }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </body>
    </html>
    
    

    Mittlerweile ist das mein Stand

    2b147097-c888-447e-8fc4-acd7f459d666-grafik.png

    Daten kommen aus einer Redis Datenbank usw. Vielleicht stelle ich das mal hier vor, wenn es "fertig" ist. Liest hier einer mit, der auch Flask nutzt?

  • Ok, ich benutze eine Funktion die sich die aktuellen Kurs abholt. Das habe ich in Pywebio mit

    put_loading()
    

    gemacht. Dafür brauche ich jetzt eine Alternative. Mir kam da in den Sinn, das mit asyncio zu machen. Den Test aus der Flask Doku ausprobiert.

    @app.route("/get-data")
    async def get_data():
        data = await async_db_query(...)
        return jsonify(data)
    

    Leicht abgewandelt in

    @app.route('/refresh', methods=['POST'])
    async def refresh():
        import time
        time.sleep(5)
        return jsonify("TEST")
    

    Kommt beim Testen die Fehlermeldung das async erst ab Version 2.0 unterstützt wird und auch nur, wenn Flask mit async installiert wurde 🤔

    Ok, kurz recherchiert. Man muss Flask mit der Option async installieren.

    (venv) frank@debian:~/PycharmProjects/flask$ pip install Flask[async]
    Requirement already satisfied: Flask[async] in ./venv/lib/python3.11/site-packages (3.0.0)
    Requirement already satisfied: Werkzeug>=3.0.0 in ./venv/lib/python3.11/site-packages (from Flask[async]) (3.0.0)
    Requirement already satisfied: Jinja2>=3.1.2 in ./venv/lib/python3.11/site-packages (from Flask[async]) (3.1.2)
    Requirement already satisfied: itsdangerous>=2.1.2 in ./venv/lib/python3.11/site-packages (from Flask[async]) (2.1.2)
    Requirement already satisfied: click>=8.1.3 in ./venv/lib/python3.11/site-packages (from Flask[async]) (8.1.7)
    Requirement already satisfied: blinker>=1.6.2 in ./venv/lib/python3.11/site-packages (from Flask[async]) (1.6.3)
    Collecting asgiref>=3.2 (from Flask[async])
    

    Danach ging mein Beispiel und es kam 5 Sekunden später folgende Anzeige im Webbrowser.

    9753d07e-a2ce-47a7-9be0-c2010684b725-grafik.png

    Dann kann ich jetzt weiter spielen..

  • Mein vorhandenes Projekt war doch etwas größer als ich gedacht hatte. Also musste ich mehr Zeit aufwenden um es nach Flask zu transferieren. Nach einiger Zeit hatte sich eine ganz ansehnliche Zahl von Dateien angesammelt und es kam wie es kommen musste, ich wusste manchmal nicht mehr, welches File ich anfassen musste. Chaos kam auf 🙂

    So fing ich an ein wenig zu recherchieren und kam auf die Funktion Blueprint von Flask. Mich ein wenig eingelesen, ChatGPT mal eben um ein Beispiel gebeten und dann angefangen die Applikation entsprechend umzubauen.

    Auch das hat Zeit gekostet. Aber, jetzt habe ich ein aufgeräumtes Projekt, was es mir wesentlich einfacher macht, daran zu arbeiten.

    5226e90d-aa20-4b20-93af-e50f8f841880-grafik.png

    Wenn ihr also mal vorhabt, so was zu coden, halte ich es für sinnvoll das Projekt von Anfang an zu strukturieren. Und zum Schluss noch ein Screenshot zum Stand des Projektes.

    a7981ffc-0f93-41f4-93e2-c914fdba5e43-grafik.png

    Und weiter geht es, ist nämlich noch nicht fertig 😀

  • 0 Stimmen
    2 Beiträge
    273 Aufrufe
    FrankMF

    Ich habe den Sonntag mal genutzt um ein wenig was einzubauen. Einmal habe ich mit datatables.net die Tabelle etwas funktionaler gestaltet.

    00a95363-1f41-4bbf-884a-34f21aea81e4-grafik.png

    Man kann nun suchen, sortieren und wenn man mehr als 10 Datenpunkte hat, hat die Tabelle auch Pagination. Ein nettes Projekt, macht einiges einfacher 👍

    Das nächste, ich hatte es im Video ja kurz erwähnt, mir fehlte ja noch die Restore Funktion von Restic. Ok, ist jetzt auch drin.

    Da die Benutzerverwaltung mittlerweile komplett eingebaut ist, werde ich demnächst meine Test Datenbanken und alles was damit zu tuen hat, aus dem Code entfernen. Brauch ich jetzt nicht mehr. Habe jetzt zwei Benutzer, einen Produktiven und einen zum Testen.

    Langfristig steht auch noch eine zweite Sprache auf dem Zettel. Aber, dafür muss ich Lust haben, das Thema juckt nicht so richtig 😉

  • FreeOTP+ Datenumzug nach Bitwarden Authenticator

    Python3
    1
    0 Stimmen
    1 Beiträge
    150 Aufrufe
    Niemand hat geantwortet
  • Pycharm - Interpreter Settings

    Python3
    1
    0 Stimmen
    1 Beiträge
    126 Aufrufe
    Niemand hat geantwortet
  • MongoDB - Erste Erfahrungen

    Linux
    2
    0 Stimmen
    2 Beiträge
    156 Aufrufe
    FrankMF

    So frisch von der MongoDB Front und wieder viel gelernt, weil beim Üben macht man Fehler 🙂

    Oben war ja mongodump & mongorestore von der KI empfohlen. Hier das wie ich es gemacht habe.

    mongodump frank@redis-stack:~$ mongodump -u frank -p '<password>' --host 192.168.3.9 --authenticationDatabase admin -d portfolio -o mongodump/ 2024-04-06T09:29:25.174+0200 writing portfolio.stockList to mongodump/portfolio/stockList.bson 2024-04-06T09:29:25.175+0200 writing portfolio.users to mongodump/portfolio/users.bson 2024-04-06T09:29:25.175+0200 done dumping portfolio.stockList (8 documents) 2024-04-06T09:29:25.176+0200 writing portfolio.total_sum to mongodump/portfolio/total_sum.bson 2024-04-06T09:29:25.177+0200 done dumping portfolio.total_sum (1 document) 2024-04-06T09:29:25.177+0200 writing portfolio.old_total_sum to mongodump/portfolio/old_total_sum.bson 2024-04-06T09:29:25.177+0200 writing portfolio.stocks to mongodump/portfolio/stocks.bson 2024-04-06T09:29:25.177+0200 done dumping portfolio.users (4 documents) 2024-04-06T09:29:25.178+0200 writing portfolio.settings to mongodump/portfolio/settings.bson 2024-04-06T09:29:25.178+0200 done dumping portfolio.settings (1 document) 2024-04-06T09:29:25.179+0200 done dumping portfolio.old_total_sum (1 document) 2024-04-06T09:29:25.179+0200 done dumping portfolio.stocks (34 documents) mongorestore mongorestore -u frank -p '<password>' --host 192.168.3.9 --authenticationDatabase admin -d portfolio mongodump/meineDatenbank/

    Hier wird die Datensicherung mongodump/meineDatenbank/ in die neue Datenbank portfolio transferiert.

    Grund für das Ganze? Mich hatte der Datenbank Name meineDatenbank gestört.

    Benutzerrechte

    Jetzt der Teil wo man schnell was falsch machen kann 🙂 Ich hatte also die neue Datenbank, konnte sie aber nicht lesen. Fehlten halt die Rechte. Ich hatte dann so was hier gemacht.

    db.updateUser("frank", { roles: [ { role: "readWrite", db: "meineDatenbank" }, { role: "readWrite", db: "portfolio" }]})

    Ging auch prima, kam ein ok zurück. Nun das Problem, ich hatte beim Einrichten, den User frank als admin benutzt. Durch den oben abgesetzten Befehl (frank ist ja admin), wurden die neuen Rechte gesetzt und die Rechte als Admin entzogen!! Das war jetzt nicht wirklich das was ich gebrauchen konnte. LOL

    Ich hatte jetzt keine Kontrolle mehr über die DB. Das war aber nicht so wirklich kompliziert, das wieder zu ändern. Die Authentication temporär abstellen. Also /etc/mongod.conf editieren und

    #security: security.authorization: enabled

    eben mal auskommentieren. Den Daemon neustarten und anmelden an der DB.

    mongosh --host 192.168.3.9

    Danach neuen User anlegen

    db.createUser({ user: "<name>", pwd: "<password>", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] })

    mongod.conf wieder ändern und neustarten. Danach hat man wieder eine DB mit Authentifizierung und einen neuen Admin. Ich bin diesmal, man lernt ja, anders vorgegangen. Es gibt nun einen Admin für die DB und einen User zum Benutzen der Datenbanken! So wie man es auch auf einem produktiven System auch machen würde. Wenn ich jetzt mal was an den Benutzerrechten des Users ändere, kann mir das mit dem Admin nicht mehr passieren. Hoffe ich 🙂

  • Redis - Datenbank Zugriff mit Python

    Redis
    3
    0 Stimmen
    3 Beiträge
    166 Aufrufe
    FrankMF

    Ich bin mit der Lernkurve noch nicht so richtig zufrieden. Eine Frage die sich mir stellte, geht das einfacher? Der Ursprung meiner Datenbank Struktur liegt in einem anderen Projekt, wo ich versucht habe Daten permanent in einem File zu speichern. Dazu hatte ich damals JSON genommen. Deswegen auch diese Zeilen

    self.project = str(db_client.json().get('settings', '$..project')[0]) or self.project

    Gut, ich hatte dann mal ChatGPT gefragt, wie macht man das so 'normalerweise'? es kam eine Klasse heraus, die ich dann intensiv ausprobiert habe, ein wenig umgebaut usw. So lange, bis ich der Meinung war, ok ich habe es verstanden. Jetzt nutzte der Code auch mehr Redis Funktionen, wie

    self.client.hset('settings', name, json.dumps(data))

    Es waren jetzt folgende Funktionen drin

    hset hexists hdel hget

    Dokumentation -> https://redis.io/commands/hset/

    Beim Durchlesen des Codes hatte ich jetzt mehr das Gefühl, so muss das sein 🙂

    In RedisInsight sieht das dann jetzt so aus.

    393195f7-1017-4285-8fca-734ee6b4bff7-grafik.png

    Klasse class PortfolioSettings: def __init__(self, host='172.17.0.2', port=6379, db=0): if args.test_mode == 1: self.client = redis.StrictRedis(host=SERVER_IP, port=port, db=TEST[0]) else: self.client = redis.StrictRedis(host=SERVER_IP, port=port, db=LIVE[0]) def set_settings(self, name, data): """Init settings if db don't exist""" if not self.client.hexists('settings', name): self.client.hset('settings', name, json.dumps(data)) return True return False def edit_setting(self, name, data): """Edit an entry in settings""" if self.client.hexists('settings', name): self.client.hset('settings', name, json.dumps(data)) return True return False def delete_setting(self, name): """Delete an entry in settings""" return self.client.hdel('settings', name) def get_setting(self, name): """Get an entry in settings""" setting = self.client.hget('settings', name) return json.loads(setting) if setting else None def get_all_settings(self): """Get all entries in settings""" settings = self.client.hgetall('settings') return {k.decode(): json.loads(v) for k, v in settings.items()}

    Und hier die Initialisierung

    settings_data = PortfolioSettings() ##################### # Will only be executed if DB is not available! ##################### if not settings_data.get_all_settings(): # Settings initialisieren print("INIT") settings_data.set_settings("project", "Portfolio") settings_data.set_settings("version", "0.0.3") settings_data.set_settings("theme", "dark") settings_data.set_settings("url_list", ["https://www.onvista.de/aktien/Deutsche-Telekom-Aktie-DE0005557508"]) settings_data.set_settings("exchange_list", ['DKB','Smartbroker','BUX'])

    Teile der Klasse sind [KI-generiert]

    Ich war zufrieden und habe die Klasse dann in mein Projekt übernommen und den Code überall entsprechend angepasst.

  • Pycharm - Umzug auf neuen Rechner

    Linux
    1
    0 Stimmen
    1 Beiträge
    93 Aufrufe
    Niemand hat geantwortet
  • Python & Redis-Datenbank

    Verschoben Linux
    3
    0 Stimmen
    3 Beiträge
    128 Aufrufe
    FrankMF

    Heute dann die nächste Herausforderung. Mein JSON soll so aussehen, damit ich das entsprechend erweitern kann.

    Stocks {0: {'stockname': 'Deutsche Telekom Aktie', 'wkn1': '4534543534', 'wkn2': 'sfsdfsdfsfdfd', 'quantity': 100}, 1: {'stockname': 'Henkel', 'wkn1': '4534543534', 'wkn2': 'sfsdfsdfsfdfd', 'quantity': 50}}

    Die Daten sollen wie oben schon ausprobiert, in einer Redis Datenbank liegen. So weit auch kein großes Problem. ABER, der Zugriff auf diese Daten war dann meine nächste Hürde 🙂

    Ok, ich habe also mehrere Einträge im JSON File bzw. in der Datenbank. Wie komme ich da nun wieder dran. Ein paar ☕ später dann die Lösung.

    Wie komme ich an den einzelnen Eintrag, also über den Index??

    r1.json().get('stocks', 1)

    Gibt als Ergebnis

    {'stockname': 'Henkel', 'wkn1': '4534543534', 'wkn2': 'sfsdfsdfsfdfd', 'quantity': 50}

    Ok, das passt schon mal. Somit kann man dann gewohnt auf die einzelnen Elemente zugreifen.

    print("TESTING", testing['stockname'])

    Ausgabe

    TESTING Henkel

    Ok, Teil 1 erledigt. Jetzt habe ich ja irgendwann mehrere Elemente in der Liste und brauch dann den letzten Index , um damit was machen zu können. Also, z.B. durch die Daten zu loopen.

    objkeys = r1.json().objkeys('stocks') print("Objkeys", objkeys)

    Ausgabe

    Objkeys ['0', '1']

    Ok, kommt eine Liste des Index zurück. Damit kann man arbeiten 😉

    Ich hatte dann zum Testen mittels einer while Schleife die Daten geladen, aber jetzt beim Tippen klingelt es und wir machen das schön mit enumerate 😉

    @staticmethod def load(): data = {} for count, value in enumerate(objkeys): testing = r1.json().get('stocks', count) data[count] = { "stockname": testing['stockname'], "wkn1": testing['wkn1'], "wkn2": testing['wkn2'], "quantity": testing['quantity']} return data

    Somit habe ich die Daten aus der Redis Datenbank in einem Objekt und kann damit arbeiten.

  • PyWebIO

    Angeheftet PyWebIO
    1
    0 Stimmen
    1 Beiträge
    197 Aufrufe
    Niemand hat geantwortet