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:
- Configurar la aplicación del
Flask
comoapp = Flask(static_path=''static'', static_url_path=''/more/then/your/max/variables/path/depth/static'')
. - Cambie
@app.route(''/<var_1>/<var_2>/<var3>/'')
a@app.route(''/prefix/<var_1>/<var_2>/<var3>/'')
. - Agregue su propio convertidor y utilícelo como
@app.route(''/<no_static:var_1>/<var_2>/<var3>/'')
. - Importe
werkzeug.routing
, cree la implementación de un mapa propio, cambiewerkzeug.routing.Map
por su propia implementación, importeflask
. - 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