Bottle: Navigation erstellen

Django, Flask, Bottle, WSGI, CGI…
Antworten
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Hallo,

ich bin grade dabei mich in Bottle einzuarbeiten, klappt soweit ganz gut nur eine Sache begreife ich nicht, wie kriegt man eine
Liste der routes, die mann erstellt hat?

Um z.B ein Navigationsmenu zu erstellen.

Mein Weg sieht so aus:

Code: Alles auswählen

from bottle import Bottle, route, run, template

app = Bottle()

@app.route('/')

def index():
    return 'Bla'

@app.route('/test')

def test():
    return 'Bla'

for route in app.routes:
    print 'http://localhost%s' % route.__dict__['rule']
Ausgabe:

Code: Alles auswählen

http://localhost/
http://localhost/test
Gibt es evtl. einen eleganteren, anderen Weg?
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo friedduck,

zwischen Decorator und Funktion sollte keine Leerzeile sein, weil dann ja die enge Verknüpfung der beiden nicht mehr so deutlich ist.
Wie kommst Du auf "route.__dict__['rule']"? Auf Attribute einer Instanz greift man normalerweise direkt mit dem .-Operator zu: "route.rule".

Eleganter – also noch einfacher – kann denn der Zugriff doch eigentlich gar nicht mehr sein.
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Sirius3 hat geschrieben: Wie kommst Du auf "route.__dict__['rule']"?
Diese Schleife ist "schuld" :-):

Code: Alles auswählen

for route in app.routes:
    print dir(route)
Ausgabe:

Code: Alles auswählen

['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_context', '_make_callback', 'all_plugins', 'app', 'call', 'callback', 'config', 'method', 'name', 'plugins', 'prepare', 'reset', 'rule', 'skiplist']
Nein, im Ernst, ich bin noch recht neu in Python und manche Sachen interpretiere ich einfach falsch. Jetzt wo ich die Ausgabe sehe und dank deiner Erklärung, würde ich sagen, dass in dem Object? ein dict vorhanden ist, indem auch das attribut rule vorkommt.

Sirius3 hat geschrieben: Auf Attribute einer Instanz greift man normalerweise direkt mit dem .-Operator zu: "route.rule".
Eleganter – also noch einfacher – kann denn der Zugriff doch eigentlich gar nicht mehr sein.
Schon wieder was gelernt, meine Lösung sah mir gleich zu kryptisch aus, deswegen habe ich hier im Forum nachgefragt.
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Falls irgendjemand mal das gleiche Problem hat, das ist mein Lösungsweg:

Code: Alles auswählen

import bottle

app = bottle.Bottle()

@app.route('/')
def index():
    return 'Bla'

@app.route('/test')
def test():
    pass

@app.route('/<name>')
def test1(name):
    pass

def menu():
    unwanted = set('<>:')
    navurl = list()
    
    for url in app.routes:
        if not any( (char in unwanted) for char in url.rule):
            navurl.append(url.rule)
    return navurl

for url in menu():
    print (url)
Ausgabe:

Code: Alles auswählen

/
/test
Es werden nur statische Routes berücksichtigt.
Zuletzt geändert von Anonymous am Montag 11. Februar 2013, 10:12, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@friedduck: oder man schreibt

Code: Alles auswählen

def menu():
    unwanted = set('<>:')
    return (url.rule for url in app.routes if unwanted.isdisjoint(url.rule))

for url in menu():
    print (url)
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

sehr kompakte Schreibweise und tut was es soll :-)

Aus der Python Doku:

Code: Alles auswählen

isdisjoint(other)
Return True if the set has no elements in common with other. Sets are disjoint if and only if their intersection is the empty set.
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Ich habs noch etwas abgeändert:

Code: Alles auswählen

def navigation():
    host = '127.0.0.0'
    port = '8080'

    unwanted = set('<>:')
    return list(('http://%s:%s%s' % (host, port, url.rule) for url in app.routes if unwanted.isdisjoint(url.rule)))
Ausgabe:

Code: Alles auswählen

['http://localhost:8080/', 'http://localhost:8080/new']
Ich glaub mehr rausholen kann man nicht...
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie hast Du die Umwandlung '127.0.0.1' -> 'localhost' geschafft :D

Noch besser wäre es, den die Umgebungsvariablen des Web-Servers zu verwenden, um
Host und Port zu erfahren.

PS: Statt list((...)) besser [...]
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Sirius3 hat geschrieben:Wie hast Du die Umwandlung '127.0.0.1' -> 'localhost' geschafft :D
Das ist eine nicht näher kommentierte Python 5 funktion ;-)
Statt list((...)) besser [...]
Was du alles weißt..
Warum ist es besser?
Noch besser wäre es, den die Umgebungsvariablen des Web-Servers zu verwenden, um
Host und Port zu erfahren.
Geht das auch bei Bottle? Die IP und den Port gibst du ihm ja beim start mit.

Code: Alles auswählen

run(host='localhost', port=8080)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

friedduck hat geschrieben:Geht das auch bei Bottle? Die IP und den Port gibst du ihm ja beim start mit.
Solange Du den integrierten Webserver benutzt, hast Du noch einigermaßen Einfluß auf Hostname und Port.
Normalerweise läuft bottle aber mit WSGI auf einem richtigen Server, möglicherweise noch mit
virtuellen Hosts, load balancing, proxys usw.
Deshalb liefert jeder Request weitere Daten mit, so dass man weiß, welche URL der Benutzer tatsächlich
aufgerufen hat, sonst verweisen Deine Menülinks irgendwann mal auf falsche Seiten.

Schau mal nach request.url, bzw. request.urlparts.
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

macht Sinn, werde ich machen.

Aber warum soll man [], anstelle von list() nehmen?
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

friedduck hat geschrieben:Aber warum soll man [], anstelle von list() nehmen?
Geschmackssache.

Man kann aber mit dis mal einen Blick auf den von CPython erzeugten Bytecode werfen.

Code: Alles auswählen

def list_1(data):
    foo = list()

def list_2(data):
    foo = []
Aus diesem Codefragment wird im Disassembler folgendes:

Code: Alles auswählen

 12           0 LOAD_GLOBAL              0 (list)
              3 CALL_FUNCTION            0
              6 STORE_FAST               1 (foo)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE        

 15           0 BUILD_LIST               0
              3 STORE_FAST               1 (foo)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE
Man könnte jetzt vermuten, dass die zweite Variante schneller ist. Allerdings weiß man es nicht ohne gemessen zu haben und die reale Laufzeit ist wohl ohnehin eher unkritisch.
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Danke für die ausführliche Antwort, ist wohl wirklich geschmacksache.

Aber der 2 Beispiel suggeriert eine rasantere Ausführung :-)
Ich etscheide mich für die 2te Variante ;-)
BlackJack

Das Beispiel passt hier nicht, denn es ging ja nicht um ``[]`` vs. ``list()`` um eine leere Liste zu erstellen sondern um list comprehension vs. `list()`-Aufruf mit einem Generatorausdruck als Argument. Letzteres ist einfach umständlicher weil bei einer list comprehension direkt eine Liste erstellt wird, während bei einem Generatorausdruck halt erst einmal ein Generator erstellt wird, und daraus dann erst die Liste aufgebaut wird. Ein Zwischenergebnis mehr.
friedduck
User
Beiträge: 76
Registriert: Montag 23. Juli 2012, 20:41

Also ich kann nur aus der sicht eines Noobs sprechen und sagen, dass dieser Code für mich am verständlichsten und sehr gut nachvollziehbar ist:

Code: Alles auswählen

def menu():
    unwanted = set('<>:')
    navurl = list()
   
    for url in app.routes:
        if not any( (char in unwanted) for char in url.rule):
            navurl.append(url.rule)
    return navurl

for url in menu():
    print (url)
Dieser Code ist schwer für mich zu lesen und danach zu Verarbeiten, da ein generator objekt zurückgegeben wird, aber er ist sehr kompakt und ich denke das soll mein
Ziel werden. Man kanns ja kommentieren :-) :

Code: Alles auswählen

def menu():
    unwanted = set('<>:')
    return (url.rule for url in app.routes if unwanted.isdisjoint(url.rule))

for url in menu():
    print (url)
Das wichtigste vergessen, in dem Zusammenhang:
Daraus folgend ist list() besser für einen Anfänger zu lesen als nur [].
Antworten