tutorial pagina oficial homepage python flask werkzeug

python - pagina - flask session



Conflictos de enrutamiento de URL para archivos estáticos en el servidor Flask dev (3)

Entonces, como tbicr señaló, este comportamiento se establece profundamente en Werkzeug, y realmente no hay una manera elegante de manejarlo desde Flask. La mejor solución que pude encontrar es:

Defina un manejador de archivos estáticos complementarios como:

@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)

Aquí, app.config[''STATIC_FOLDER''] es la ruta completa a la carpeta estática en la máquina que ejecuta la aplicación.

Ahora, este manejador capta cosas como /static/images/img.jpg , dejando mi vista solo con los tres componentes variables.

Quiero definir una regla de URL con tres componentes variables, como:

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

Pero me parece que el servidor de desarrollo evalúa dichas reglas antes de intentar hacer coincidir los archivos estáticos. Entonces algo así como:

/static/images/img.jpg

será capturado por mi regla de URL, en lugar de ser reenviado al controlador de archivos estáticos incorporado. ¿Hay alguna manera de forzar al servidor de desarrollo para que coincida primero con los archivos estáticos?

PD Esto es solo un problema si la regla tiene más de dos componentes variables.


Esta es la función de optimización de ruta werkzeug. Ver Map.add , Map.update y 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

Hay self.arguments : argumentos actuales, self._weights : profundidad de la ruta.

Para ''/<var_1>/<var_2>/<var3>/'' tenemos (True, -3, [(1, 100), (1, 100), (1, 100)]) . Hay (1, 100) - argumento de cadena predeterminado con una longitud máxima de 100.

Para ''/static/<path:filename>'' tenemos (True, -2, [(0, -6), (1, 200)]) . Hay (0, 1) - longitud de cadena de no argumento de ruta static , (1, 200) - argumento de cadena de ruta longitud máxima 200.

Por lo tanto, no encuentro ninguna forma hermosa de establecer la implementación de Map para Flask.url_map o establecer la prioridad para la regla del mapa. Soluciones:

  1. Configurar la aplicación del Flask como app = Flask(static_path=''static'', static_url_path=''/more/then/your/max/variables/path/depth/static'') .
  2. Cambie @app.route(''/<var_1>/<var_2>/<var3>/'') a @app.route(''/prefix/<var_1>/<var_2>/<var3>/'') .
  3. Agregue su propio convertidor y utilícelo como @app.route(''/<no_static:var_1>/<var_2>/<var3>/'') .
  4. Importe werkzeug.routing , cree la implementación de un mapa propio, cambie werkzeug.routing.Map por su propia implementación, importe flask .
  5. Use el servidor como en producción.

Una forma de evitar esto es hacer trampa en el algoritmo de ordenación de reglas falsificando el método match_compare_key() la regla registrada. Tenga en cuenta que este truco solo funciona con rutas que se han registrado directamente con app.route() (el objeto Flask), no con Blueprints. Las rutas de Blueprints se agregan al mapa de url global solo después del registro de blueprint en la aplicación principal, lo que dificulta la modificación de las reglas generadas.

# 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

Tenga en cuenta que en este caso la función falsa resultante no está vinculada al objeto de regla. Por lo tanto, al llamar a rule.match_compare_key() , la función no recibe un self argumento. Si desea vincular la función correctamente, haga esto en su lugar:

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

Podemos generalizar lo anterior con un decorador

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

El mismo truco implementado como un administrador de contexto.

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