2013-06-16 20 views
7

Chcę zdefiniować regułę url z trzech zmiennych składników, takich jak:URL konflikty routingu dla plików statycznych w Kolby serwerze dev

@app.route('/<var_1>/<var_2>/<var3>/') 

Ale uważam, że serwer rozwój ocenia takie zasady przed starając się dopasować do statycznego akta. Więc coś takiego:

/static/images/img.jpg 

zostanie złapany przez mojego reguły zawartości, zamiast być przekazany do wbudowanej obsługi plików statycznych. Czy istnieje sposób na zmusić serwer programistyczny do dopasowania najpierw do plików statycznych?

P.S. Jest to tylko problem, jeśli reguła zawiera więcej niż dwa zmienne komponenty.

+0

kilka interesujących brzmi: [System routingu Kolba] (http://flask.pocoo.org/docs/design/#the-routing-system) i [Werkzeug routing] (http: // Werkzeug .pocoo.org/docs/routing/# module-werkzeug.routing). Może też lubię odkrywać za pomocą [Blueprints] (http://flask.pocoo.org/docs/blueprints/) –

Odpowiedz

16

To jest funkcja optymalizacji tras werkzeug. Zobacz Map.add, Map.update i Rule.match_compare_key:

def match_compare_key(self): 
    """The match compare key for sorting. 

    Current implementation: 

    1. rules without any arguments come first for performance 
    reasons only as we expect them to match faster and some 
    common ones usually don't have any arguments (index pages etc.) 
    2. The more complex rules come first so the second argument is the 
    negative length of the number of weights. 
    3. lastly we order by the actual weights. 

    :internal: 
    """ 
    return bool(self.arguments), -len(self._weights), self._weights 

Istnieje self.arguments - Bieżąca argumentów self._weights - głębokość ścieżka.

Dla '/<var_1>/<var_2>/<var3>/' mamy (True, -3, [(1, 100), (1, 100), (1, 100)]). Istnieje (1, 100) - domyślny argument łańcuchowy o maksymalnej długości 100.

Dla '/static/<path:filename>' mamy (True, -2, [(0, -6), (1, 200)]). Istnieje (0, 1) - ścieżka non argumentem długość łańcucha static, (1, 200) - ścieżka argumentem ciąg długości max 200.

Więc nie znaleziono żadnej piękny sposób, aby ustawić własne Map implementację Flask.url_map lub ustawić priorytet dla rządów mapy. Rozwiązania:

  1. Konfiguracja Flask Aplikacja jako app = Flask(static_path='static', static_url_path='/more/then/your/max/variables/path/depth/static').
  2. Zmień @app.route('/<var_1>/<var_2>/<var3>/') na @app.route('/prefix/<var_1>/<var_2>/<var3>/').
  3. Dodaj własny konwerter i użyj jako @app.route('/<no_static:var_1>/<var_2>/<var3>/').
  4. Importuj werkzeug.routing, utwórz własną implementację mapy, zmień werkzeug.routing.Map na własną implementację, importuj flask.
  5. Użyj serwera tak jak przy produkcji.
+0

Okay, źle zrozumiałem twoją odpowiedź na początku. Tak więc w przypadku opcji 1 zmieniasz adres URL, w którym są wyświetlane pliki statyczne, i dodajesz do tego adresu url wystarczająco "fałszywego" podkatalogu, aby nie mógł on kolidować z żadnymi istniejącymi regułami adresu URL tworzonymi przez zmienne składniki. Tak więc, dla mojego przykładu, 'static_url_path' musiałby zostać zmieniony na coś takiego:'/static/static/static/static', a następnie nie zostałby przechwycony przez '@ app.route ('/ ///') ' –

+0

Użyłem opcji 1. Nie wiem dlaczego, ale nie działałoby, gdybym miał argument static_path, ale działało, gdy go usunąłem. Skończyło się na użyciu: 'app = Flask (__ nazwa__, static_url_path = '/ r/s/static')' – trevor

6

Tak więc, jak zauważyło tbicr, zachowanie to jest głęboko osadzone w Werkzeug i nie ma naprawdę eleganckiego sposobu radzenia sobie z Flask. Najlepszym obejście mogłem wymyślić jest:

Zdefiniowanie komplementarnej obsługi plików statycznych jak:

@app.route('/static/<subdir>/<path:filename>/') 
def static_subdir(subdir=None, filename=None): 

    directory = app.config['STATIC_FOLDER'] + subdir 
    return send_from_directory(directory, filename) 

Tutaj app.config['STATIC_FOLDER'] jest pełna ścieżka do folderu statyczne na komputerze z uruchomioną aplikacją.

Teraz ten przewodnik łapie rzeczy takie jak /static/images/img.jpg, pozostawiając mój widok z trzech zmiennych składników sam.

+0

To jest dobry pomysł, ale nie musisz dodawać własnego programu obsługi, ponieważ możesz to zrobić za pomocą ustawień aplikacji 'Flask'. Zobacz moje rozwiązanie odpowiedzi 1: skonfiguruj aplikację Flask jako 'app = Flask (static_path = 'static', static_url_path = '/ more/then/your/max/variables/path/depth/static')' ale zmieni to statyczną ścieżkę URL (nie ma problemu z 'url_for ('static', filename = 'images/img.jpg'').) – tbicr

+0

IMO, twoje rozwiązanie jest najlepiej wspomniane tutaj, Petrus. Wprowadzenie manekinów podkatalogów jest z pewnością brzydsze niż jawne złapanie go w regułę URL. –

3

Jednym ze sposobów obejścia tego problemu jest oszukiwanie algorytmu sortowania reguł poprzez podszywanie się do zarejestrowanej reguły w metodzie match_compare_key().Zauważ, że ten hack działa tylko w przypadku tras, które zostały zarejestrowane bezpośrednio pod app.route() (obiekt Flask), a nie w Blueprints. Trasy projektów są dodawane do globalnej mapy URL tylko po zarejestrowaniu projektu w głównej aplikacji, co utrudnia modyfikowanie wygenerowanych reguł.

# an ordinary route 
@app.route('/<var1>/<var2>/<var3>') 
def some_view(var1, var2, var3): 
    pass 

# let's find the rule that was just generated 
rule = app.url_map._rules[-1] 

# we create some comparison keys: 
# increase probability that the rule will be near or at the top 
top_compare_key = False, -100, [(-2, 0)] 
# increase probability that the rule will be near or at the bottom 
bottom_compare_key = True, 100, [(2, 0)] 

# rig rule.match_compare_key() to return the spoofed compare_key 
rule.match_compare_key = lambda: top_compare_key 

Należy zauważyć, że w takim przypadku wynikowa funkcja spoofowania nie jest powiązana z obiektem reguły. Dlatego po wywołaniu rule.match_compare_key() funkcja nie otrzymuje argumentu self. Jeśli chcesz powiązać funkcję prawidłowo to zrobić w zamian:

spoof = lambda self: top_compare_key 
rule.match_compare_key = spoof.__get__(rule, type(rule)) 

Możemy uogólnić powyższe z dekorator

def weighted_route(*args, **kwargs): 
    def decorator(view_func): 
     compare_key = kwargs.pop('compare_key', None) 
     # register view_func with route 
     app.route(*args, **kwargs)(view_func) 

     if compare_key is not None: 
      rule = app.url_map._rules[-1] 
      rule.match_compare_key = lambda: compare_key 

     return view_func 
    return decorator 

# can be used like @app.route(). To weight the rule, just provide 
# the `compare_key` param. 
@weighted_route('/<var1>/<var2>/<var3>', compare_key=bottom_compare_key) 
def some_view(var1, var2, var3): 
    pass 

To samo technikę realizowane jako kierownik kontekstowego.

import contextlib 

@contextlib.contextmanager 
def weighted_route(compare_key=None): 
    yield 
    if compare_key is not None: 
     rule = app.url_map._rules[-1] 
     rule.match_compare_key = lambda: compare_key 

# and to use 

with weighted_route(compare_key): 
    @app.route('/<var1>/<var2>/<var3>') 
    def some_view(var1, var2, var3): 
     pass