Web.py

Django, Flask, Bottle, WSGI, CGI…
Antworten
moehre
User
Beiträge: 39
Registriert: Donnerstag 21. April 2016, 13:50

Hallo,

ich habe einen webserver der auf meine Datenbank PostgreSQL draufzugreift.
Die Abfrage soll als JSON zurückgeschickt werden. Dieses JSON-Object soll später mittels AJAX-GET-Request in WebGL eingebunden werden.
Ich habe noch zusätzlich mal die Access-Control-Allow-Origin eingefügt damit er keine Probleme später mit der Same-Origin-Policy bekommt.

Code: Alles auswählen

import web
import psycopg2
import json

urls = (
		"/", "index",
		"/bbox", "bbox",
		)

app = web.application(urls, globals())
web.config.debug = True

##################### Connect to an existing database (postgresql) ##################################
try:
	conn = psycopg2.connect(database='...', user='...', password='...')

except:
	print "I am unable to connect to the database"		
######################################Database end ####################################################	

	
class index:
    def GET(self):
		web.header('Access-Control-Allow-Origin',      '*')
		web.header('Access-Control-Allow-Credentials', 'true')
		return "hallo"

class bbox:
	def GET(self):	
		web.header('Access-Control-Allow-Origin',      '*')
		web.header('Access-Control-Allow-Credentials', 'true')
		
		data = web.input(XMIN="[]", YMIN="[]", XMAX="[]", YMAX="[]")
		
		cur = conn.cursor()
		
		query = "SELECT a.id AS building_nr, ST_AsGeoJSON(c.Geometry) AS geometry, d.Classname AS polygon_typ FROM building a, thematic_surface b, surface_geometry c, Objectclass d WHERE c.Geometry && ST_MakeEnvelope(" + data.XMIN + ", "+ data.YMIN + ", " + data.XMAX + ", " + data.YMAX + ") AND a.id = b.building_id AND b.lod2_multi_surface_id = c.root_id AND c.geometry IS NOT NULL AND b.Objectclass_ID = d.ID AND a.ID=5;"
		cur.execute(query) 
		
		r = [dict((cur.description[i][0], value) \
			for i, value in enumerate(row)) for row in cur.fetchall()]

		return r
	
# Aufruf der App		
if __name__ == "__main__": 
		app.run()
Mein Ergebnis ist sowas in der Art:
[{'building_nr': 5, 'geometry': '{"type":"Polygon","coordinates":[[[3500267.16,5392933.95,456.904],[3500259.19,5392933.01,456.904],[3500258.586,5392938.152,456.904],[3500258.02,5392942.97,456.904],[3500265.98,5392943.94,456.904],[3500266.552,5392939.097,456.904],[3500267.16,5392933.95,456.904]]]}', 'polygon_typ': 'BuildingGroundSurface'}

1) ist das schon ein korrektes JSON-Objekt oder was gibt er mir da zurück???
Weil ich mir nur die Variable r zurückgeben lasse.

Es gibt ja noch die Methode:
json_output = json.dumps(r)
return json_output
sieht aber vom Ergebniss sogut wie identisch aus. Was ist da der Unterschied?

2) was bieten in die anderen frameworks im vergleich zu web.py außer das es nicht mehr gepflegt wird? Würdet ihr andere empfehlen?

Danke.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@moehre: webpy wandelt einfach alles, was Du von der GET-Methode zurückgibst in einen String um. Das was Du da hast ist also kein JSON sondern die Stringrepräsentation Deiner Python-Objekte.

Dass Du ein json-Antwort bekommst, siehst Du als Browser daran, dass der Content-Type "application/json" ist. Korrekt wäre also soetwas:

Code: Alles auswählen

    def GET(self):
        [...]
        web.header('Content-Type', 'application/json')
        return json.dumps(r)
Andere Frameworks bieten json schon von Haus aus an. Wenn man beispielsweise bei Bottle ein Wörterbuch zurückgibt, wird dies automatisch mit korrektem Content-Type als json geschickt.

Zum Code:
try-except-Blöcke, die nach Ausführung des Fehlerfalls ein kaputtes Programm hinterlassen, weil z.B. bestimmte Variablen gesetzt sind, sind quatsch. Lass das Exception-Handling einfach weg, wenn Du nichts sinnvolles machst. Nackte excepts sind sowieso quatsch, weil das ein sinnvolles Testen verhindert.

In SQL-Statements dürfen nie Parameter per String-Zusammenstückelung oder -formatierung eingefügt werden. Dafür gibt es Platzhalter und das zweite Argument von execute. Du übernimmst ungeprüft Daten von einem Web-Formular. Jeder kann also bei Dir beliebig mit der Datenbank spielen, je nach Datenbankkonfiguration ist sogar Dein gesamter Server kompromitiert.

Vermeide das \-Zeilenfortsetzungzeichen, weil z.B. ein nicht sichtbares Leerzeichen dahinter Dir einen Syntaxfehler produziert. Bei Klammern erkennt Python automatisch, dass die Zeile fortgesetzt werden muß.

Statt enumerate würde ich an Deiner Stelle zip benutzen:

Code: Alles auswählen

 result = [dict((d[0], v) for d, v in zip(cur.description, r)) for r in cur]
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Also mir wird es nicht langweilig, das hier zu posten: https://xkcd.com/327
In specifications, Murphy's Law supersedes Ohm's.
moehre
User
Beiträge: 39
Registriert: Donnerstag 21. April 2016, 13:50

Danke jetzt habe ich es mal ausprobiert und klappt auch mit der Ausgabe als JSON :wink:

Ja das mit der Überprüfung der Daten ist sehr wichtig. Ich habe jetzt im query Platzhalter verwendet und im execute Statement einfach Parameter angegeben.

Code: Alles auswählen

import web
import psycopg2
import json

urls = (
		"/", "index",
		"/bbox", "bbox",
		)
app = web.application(urls, globals())
web.config.debug = True
conn = psycopg2.connect(database='...', user='...', password='...')	

	
class index:
    def GET(self):

		return "hallo"

class bbox:
	def GET(self):	
		web.header('Access-Control-Allow-Origin',      '*')
		web.header('Access-Control-Allow-Credentials', 'true')		
		web.header('Content-Type', 'application/json')
		
		data = web.input(XMIN="[]", YMIN="[]", XMAX="[]", YMAX="[]")

		cur = conn.cursor()
		
		# execute a command
		#query = "SELECT a.id AS building_nr, ST_AsGeoJSON(c.Geometry) AS geometry, d.Classname AS polygon_typ FROM building a, thematic_surface b, surface_geometry c, Objectclass d WHERE c.Geometry && ST_MakeEnvelope(" + data.XMIN + ", "+ data.YMIN + ", " + data.XMAX + ", " + data.YMAX + ") AND a.id = b.building_id AND b.lod2_multi_surface_id = c.root_id AND c.geometry IS NOT NULL AND b.Objectclass_ID = d.ID AND a.ID=5;"
		query = "SELECT a.id AS building_nr, ST_AsGeoJSON(c.Geometry) AS geometry, d.Classname AS polygon_typ FROM building a, thematic_surface b, surface_geometry c, Objectclass d WHERE c.Geometry && ST_MakeEnvelope(%s, %s, %s, %s) AND a.id = b.building_id AND b.lod2_multi_surface_id = c.root_id AND c.geometry IS NOT NULL AND b.Objectclass_ID = d.ID AND a.ID=5;"
		
		cur.execute(query, [data.XMIN,data.XMAX,data.XMAX,data.YMAX]) 
		
		r = [dict((d[0], v) for d, v in zip(cur.description, r)) for r in cur]
		
		return json.dumps(r)

	
# Aufruf der App		
if __name__ == "__main__": 
		app.run()
Ist das der richtige Weg? Wieso erfolgt bei Benutzung von %i eine Fehlermeldung und bei %s nicht?
Ich will ja in meinem Beispiel nur ganze Zahlen als Input haben
BlackJack

@moehre: *Der* Platzhalter ist %s. Das hat nichts mit Zeichenkettenformatierung mittels ``%`` zu tun. Ist etwas ungünstig das (einige) den gleichen Platzhalter für SQL verwendet haben, statt das sie einfach die SQL-Standard-Platzhalter verwenden. Was ein paar Datenbankmodule auch tun. Die API ist an der Stelle deswegen sowieso besch****, weil man dann doch wieder spezifischen Code schreiben muss, oder man schreibt sich eine Abstraktion, die aber letztendlich jeder schreiben muss. Einer der Gründe warum ich fast immer SQLAlchemy verwende.
moehre
User
Beiträge: 39
Registriert: Donnerstag 21. April 2016, 13:50

Ok danke!
Eins noch bezüglich der Zeichenformatierung!
Gibt es eine Möglichkeit direkt bei web.input([]) zu definieren das nur Integers reinkommen?
Wenn alles eingelesen ist (auch Zahlen) sind es ja stings...
BlackJack

@moehre: Nein, die Möglichkeit gibt es nicht. Die Umwandlung musst Du selbst vornehmen.
moehre
User
Beiträge: 39
Registriert: Donnerstag 21. April 2016, 13:50

Alles klar! Vielen Dank Euch
Antworten