XMLHttpRequest mit Bottle

Django, Flask, Bottle, WSGI, CGI…
Antworten
dd0815
User
Beiträge: 33
Registriert: Donnerstag 25. Juli 2019, 13:57

Hallo Python-Gemeinde,

mittlerweile habe ich mit etwas in Bottle einarbeiten können (nach meinen ersten Versuchen: viewtopic.php?f=1&t=46152).

Jetzt scheitere ich etwas an der dynamischen Inhaltsaktualisierung (via XMLHttpRequest - aka Ajax).

Habt ihr vielleicht ein Minimalbeispiel für mich (ich habe das bei keinem Bottle-Tutorial gefunden)? Bzw. dürft Ihr Euch gern mein nicht funktionierendes Beispiel anschauen:

Meine Testwebseite (ignoriert mal die MySQL-Datenbankabfrage sowie Kommentare):

Code: Alles auswählen

@app.route('/hello')

def hello():

    # if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        # return 'This is an AJAX request'
    # else:
        # return 'This is a normal request'

    try:
        dbcon = mysql.connector.connect(
            host = "127.0.0.1",
            user = "root",
            passwd = "",
            database = "test"
        )

    except mysql.connector.Error as e:
        strErr = "Error:", e.errno        # error number
        strErr += ": ", e.msg       # error message
        #return strErr
        return template('error', result=strErr, template_lookup=['c:/Daten/02_Projekte/Prj_008_Firma/50_Projekte/testBottle/static'] ) #absoluten Pfad angeben, ganz hinten darf kein Slash stehen

    cursor = dbcon.cursor()

    # Datensätze auslesen
    cursor.execute("SELECT * from anlagen")
    result = cursor.fetchall()
    cursor.close() # immer cursor schließen nach jedem Befehl
    # for data in result:
        # #strTest = "name: " + str(data[3]) + "; descr1: " + str(data[4])
        # response += "name: " + str(data[3]) + "; descr1: " + str(data[4]) + "<br />"
        # #response += "%(strTest)s<br />"

    dbcon.close()

    return template('index', result=result, template_lookup=['c:/Daten/02_Projekte/Prj_008_Firma/50_Projekte/testBottle/static'] ) #absoluten Pfad angeben, ganz hinten darf kein Slash stehen
Im Prinzip wird hier das Template "index" geladen:

Code: Alles auswählen

<!DOCTYPE html>
<html lang="de">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link type="text/css" href="/static/mystyle.css" rel="stylesheet">
    <script src="/static/context.js"></script>
    <script src="/static/httpreq.js"></script>
</head>

<body oncontextmenu="return contextmenu()">

	<table id="contexttable" class="system"></table>
	<div id="show" class="system" style="padding: 1px;"></div>

    <div id="testcm" hidden>
       <h2>eigenes Kontextmenü</h2>
       <ul>
            <li><button id="hide">verstecken</button></li>
            <li><button id="show">Anzeigen</button>
                <ul>
                    <li><button id="showUrl">URL anzeigen</button></li>
                    <li><button id="showSrc">SRC anzeigen</button></li>
                </ul></li>
            <li><button id="move">1x normales Kontextmenü</button></li>
        </ul>
    </div>

<header>
header
</header>

<nav>
nav
    <form action=""> 
      <select name="customers" onchange="showCustomer(this.value)">
        <option value="">Select a customer:</option>
        <option value="ALFKI">Alfreds Futterkiste</option>
        <option value="NORTS ">North/South</option>
        <option value="WOLZA">Wolski Zajazd</option>
      </select>
    </form>
</nav>

<aside id="news">
Überraschung
</aside>

<article>
    <ul>
      % for item in result:
        <li>{{item}}</li>
      % end
    </ul>
    <div id="txtHint">Anzeige</div>
</article>

<aside>
bereich
</aside>

<footer>
footer
</footer>

</body>
</html>
Hier noch das im Template "index" erwähnte Skript "/static/httpreq.js", was letztlich nicht wie erhofft die Datei gettest.py ausführt, sondern mir schön den Inhalt dieser Datei darstellt:

Code: Alles auswählen

function showCustomer(str) {
  var xhttp;  
  if (str == "") {
    document.getElementById("txtHint").innerHTML = "";
    return;
  }
  xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("txtHint").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/static/gettest.py?q="+str, true);
  xhttp.send();
  
Ja soweit meine Versuche... Wer bis hierher durchgehalten hat, darf mich auch gern auf allen Ebenen zurechtweisen, was wie besser machen könnte (ich will ja schließlich ein anständiger Entwickler werden. :D

Viele Grüße,
dd0815
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Bottle behandelt deine Python-Datei wie jede andere Textdatei auch. Der Fehler ist also logisch. Warum bindest du das Modul nicht in dein Bottle-Script ein und führst den Aufruf der Funktionen dort aus?
When we say computer, we mean the electronic computer.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@dd0815: Das ist im Grunde keine Bottle-Frage denn ob der Browser direkt nun einen HTTP-Request absetzt oder JavaScript einen XMLHttpRequest, das ist aus Sicht des Webservers, also hier Bottle erst einmal total egal. Du musst dafür wie für alle anderen Anfragen einfach ein Route in Deinem Bottle-Programm schreiben und die wie alle anderen Anfragen auch vom JavaScript aus ansprechen. Wenn die Route "/hello" sein soll, dann muss das auch im JavaScript so stehen und nicht irgendwas mit "/static…". *Dort* sollte man auch keinen serverseitigen Code ablegen, sondern wirklich nur Sachen die an den Client ausgeliefert werden sollen und auf Serverseite *statisch* sind.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

JavaScript kennt seit einiger Zeit die fetch-API. Das ist eigentlich der aktuelle Weg, um Daten dynamisch via JavaScript nachzuladen. XMLHttpRequest API ist ein bisschen oldschool.

Die fetch-API ist ausführlich dokumentiert, z.B. bei Google, aber auch z.B. bei MDN.

Serverseitig ändert sich dadurch aber nichts.

Gruß, noisefloor
dd0815
User
Beiträge: 33
Registriert: Donnerstag 25. Juli 2019, 13:57

Hallo zusammen,

vielen Dank für Eure schnellen Antworten. Es hat tatsächlich geholfen, das Modul als eine Route im Bottle-Skript einzutragen. :D Das Problem bei zusammenkopierten Beispielen aus dem Netz: anfangs hast Du keine Ahnung, warum was funktioniert bzw. bei Fehlern eben nicht, warum etwas nicht funktioniert und wie zur Hölle Du das ändern kannst...

@sls:
Leider ist der Fehler nicht logisch für mich - ich dachte die Python-Datei wird auch als solche ausgeführt, hier ist sie übrigens, ich hatte sie beim ersten Post vergessen, sie liest auch nur eine Tabelle einer Datenbank aus:

Code: Alles auswählen

#!c:\Installation\Python37\python

import sys
import mysql
import mysql.connector
from mysql.connector import errorcode

response = "Content-type: text/html\n\n"

try:
    dbcon = mysql.connector.connect(
        host = "127.0.0.1",
        user = "root",
        passwd = "",
        database = "test"
    )

except mysql.connector.Error as e:
    strErr = "Error:", e.errno        # error number
    #print "SQLSTATE value:", e.sqlstate # SQLSTATE value
    strErr += ": ", e.msg       # error message
    #print "Error:", e                   # errno, sqlstate, msg values
    #s = str(e)
    #print "Error:", s                   # errno, sqlstate, msg values

    response = "<h2>Mööp - Fehler beim Öffnen der Datenbank</h2>"
    response += "%(strErr)s<br />"

    print (response % vars())
    sys.exit(0) # Abarbeitung beenden


cursor = dbcon.cursor()

# Datensätze auslesen
cursor.execute("SELECT * from anlagen")
result = cursor.fetchall()
cursor.close() # immer cursor schließen nach jedem Befehl
for data in result:
    #strTest = "name: " + str(data[3]) + "; descr1: " + str(data[4])
    response += "name: " + str(data[3]) + "; descr1: " + str(data[4]) + "<br />"
    #response += "%(strTest)s<br />"

#print (response)
return (response)

# Verbindung zur Datenbank schließen
dbcon.close() 
@__blackjack__
Im Prinzip versuche ich nur gleich von Anfang an etwas Struktur ins Projekt bringen, weswegen ich mit Templates arbeite sowie die Verzeichnissen static und views angelegt, wo diese Templates, die js-Dateien sowie die css liegt. Was bei mir auch nicht funktioniert hat (wenn ich mich richtig erinnere), ist in einem Template ein anderes Template aufzurufen (wollte immer wieder benötigte Templates wie Header/Footer in eigene Dateien auslagern... Als Testumgebung habe ich Win7-32, Python 3.7 und Bottle in irgendeinem Verzeichnis. Ständig wurden irgendwelche Pfade nicht gefunden, wewegen ich erstmal die absoluten genommen habe, damit ich erstmal weitermachen konnte.

Wie strukturiert Ihr größere Projekte?

@noisefloor
Danke für den Tip, die fetch-API schaue ich mir gern mal an...

Viele Grüße,
dd0815
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

CGI macht man heutzutage nicht mehr, erst recht nicht, wenn man Bottle benutzt.

Deine ganze Stringverhunzung funktioniert nicht. `strErr` ist kein String, sondern ein Tuple. `vars` hat in einem sauberen Programm auch nichts verloren, zumal Du hier ja nur strErr formatieren willst. Für Stringformatierung benutzt man entweder f-Strings oder `format`.
Wenn man eine for-Schleife hat, packt man zuerst alle Strings in eine Liste um sie danach mit `str.join` zusammenzufügen. *-SELECTS sollte man nicht benutzen, weil man so nie wieder die Reihenfolge von Feldern verändern kann. Frage konkret die Spalten 3 und 4 ab, die Du sowieso nur brauchst.
Das `return` hast Du gerade so eingebaut, weil ein `return` auf oberster Ebene nicht geht.
dd0815
User
Beiträge: 33
Registriert: Donnerstag 25. Juli 2019, 13:57

Hallo Sirius3,

ja, diese Beispieldatei ist noch vor meiner Umstellung auf Bottle entstanden, ist nur zusammenkopiert und die DB-Abfrage ist auch reichlich sinnlos - alles nur Test und hab mit einer realen Applikation nichts zu tun. Mir gings hier erstmal nur um den XMLHttpRequest, der in Bottle nicht funktioniert hat. Aber das Thema hat sich durch die ersten Antworten schon erledigt, aber vielen Dank für Deine Hinweise...

Viele Grüße,
dd0815
Antworten