Seite 1 von 1

Bottle: Navigation erstellen

Verfasst: Mittwoch 6. Februar 2013, 19:28
von friedduck
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?

Re: Bottle: Navigation erstellen

Verfasst: Mittwoch 6. Februar 2013, 22:05
von Sirius3
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.

Re: Bottle: Navigation erstellen

Verfasst: Mittwoch 6. Februar 2013, 22:39
von friedduck
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.

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 09:46
von friedduck
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.

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 10:14
von Sirius3
@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)

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 19:19
von friedduck
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.

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 19:32
von friedduck
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...

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 22:05
von Sirius3
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 [...]

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 22:18
von friedduck
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)

Re: Bottle: Navigation erstellen

Verfasst: Montag 11. Februar 2013, 22:49
von Sirius3
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.

Re: Bottle: Navigation erstellen

Verfasst: Dienstag 12. Februar 2013, 22:00
von friedduck
macht Sinn, werde ich machen.

Aber warum soll man [], anstelle von list() nehmen?

Re: Bottle: Navigation erstellen

Verfasst: Dienstag 12. Februar 2013, 22:45
von /me
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.

Re: Bottle: Navigation erstellen

Verfasst: Dienstag 12. Februar 2013, 23:08
von friedduck
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 ;-)

Re: Bottle: Navigation erstellen

Verfasst: Mittwoch 13. Februar 2013, 01:04
von 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.

Re: Bottle: Navigation erstellen

Verfasst: Mittwoch 13. Februar 2013, 08:51
von friedduck
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 [].