2013-06-16 2 views
7

내가 좋아하는 세 가지 변수 구성 요소의 URL 규칙을 정의하려면 :URL 라우팅 충돌

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

하지만 개발 서버가 정적에 대한 일치하기 전에 이러한 규칙을 평가하는 것을 발견 파일. 따라서 다음과 같은 것 :

/static/images/img.jpg 

은 내장 정적 파일 처리기로 전달되지 않고 내 URL 규칙에 의해 포착됩니다. 개발 서버에서 정적 파일을 먼저 검색하도록하는 방법이 있습니까?

P. 규칙에 두 개 이상의 변수 구성 요소가있는 경우에만 문제가됩니다.

+0

흥미로운 몇 읽기 : [플라스크 라우팅 시스템 (http://flask.pocoo.org/docs/design/#the-routing-system)와 [WERKZEUG 라우팅 (HTTP : // WERKZEUG .pocoo.org/docs/routing/# module-werkzeug.routing). [Blueprints] (http://flask.pocoo.org/docs/blueprints/)를 사용하여 탐구하고 싶을 수도 있습니다. –

답변

16

이 WERKZEUG 경로 최적화 기능입니다. Map.add, Map.updateRule.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 

self.arguments있다 - 현재 인수, self._weights - 경로 깊이를. '/<var_1>/<var_2>/<var3>/'를 들어

우리는 (True, -3, [(1, 100), (1, 100), (1, 100)]) 있습니다. (1, 100) - 최대 길이 100의 기본 문자열 인수입니다.

'/static/<path:filename>'의 경우 (True, -2, [(0, -6), (1, 200)])입니다. 경로가 아닌 인수 문자열 길이 static, (1, 200) - - 경로 문자열 인수의 최대 길이 (200)

그래서지도 규칙에 대한 Flask.url_map에 대한 자신의 Map 구현 또는 설정 우선 순위를 설정할 수있는 아름다운 방법을 찾을 수없는 (0, 1) 있습니다. 솔루션 :

  1. app = Flask(static_path='static', static_url_path='/more/then/your/max/variables/path/depth/static')으로 설정하십시오.
  2. 변경 @app.route('/prefix/<var_1>/<var_2>/<var3>/')-@app.route('/<var_1>/<var_2>/<var3>/').
  3. 는 자신의 컨버터를 추가하고 @app.route('/<no_static:var_1>/<var_2>/<var3>/')로 사용합니다.
  4. werkzeug.routing을 가져 와서 자신의지도 구현을 만들고 werkzeug.routing.Map을 자신의 구현으로 변경하고 flask을 가져옵니다.
  5. 서버를 프로덕션 환경에서 사용하십시오.
+0

좋습니다. 처음에는 답을 잘못 이해했습니다. 옵션 1을 사용하면 정적 파일이 제공되는 url을 변경하고 변수 구성 요소로 구성된 기존 URL 규칙과 충돌 할 수 없도록 해당 URL에 충분한 "더미"하위 디렉토리를 추가합니다. 예를 들어서,'static_url_path'는'/ static/static/static/static'과 같이 변경되어야하고,'@ app.route ('/ ///')' –

+0

옵션 1을 사용했습니다. 이유는 모르지만 static_path 인수가 있지만 제거했을 때 작동하지 않습니다. 'app = Flask (__ name__, static_url_path = '/ r/s/static')' – trevor

6

따라서이 동작은 Werkzeug 내부 깊숙이 설정되어 있으므로 Flask에서 처리 할 수있는 우아한 방법은 없습니다. 내가 가지고 올 수있는 가장 좋은 해결 방법은 다음과 같습니다

이 같은 보완 정적 파일 처리기를 정의 : 여기

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

app.config['STATIC_FOLDER'] 응용 프로그램을 실행하는 컴퓨터에 정적 폴더의 전체 경로입니다.

자,이 핸들러는 혼자 세 가지 변수 구성 요소와 함께 내 시야를 떠나, /static/images/img.jpg 같은 일을 잡는다. 이 문제를 해결 갈

+0

'Flask' ​​응용 프로그램 설정으로 할 수 있기 때문에 자체 처리기를 추가 할 필요가 없습니다. 내 대답 솔루션 1 참조하십시오 : 응용 프로그램 = Flask (static_path = '정적', static_url_path = '/ 더/다음/너/최대/변수/경로/깊이/정적')'로 플라스크 응용 프로그램을 설정'하지만 그것은 정적 URL 경로를 변경합니다 ('static', filename = 'images/img.jpg'에 대해서는 아무런 문제가 없습니다.) – tbicr

+0

당신의 해결책은 여기에 언급 된 것이 가장 좋으며, 더미 하위 디렉토리를 도입하는 것은 분명히 URL 규칙에서 명시 적으로 잡는 것보다 못생긴 것입니다. –

3

한 가지 방법은 등록 된 규칙의 match_compare_key() 방법을 위장하여 정렬 알고리즘 규칙을 속임수입니다.이 해킹은 청사진이 아닌 app.route() (Flask 개체)으로 직접 등록 된 경로에서만 작동합니다. Blueprints의 경로는 기본 앱의 청사진 등록시에만 글로벌 URL Map에 추가되므로 생성 된 규칙을 수정하기가 어렵습니다.

# 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 

이 경우 생성 된 스푸핑 된 함수는 규칙 객체에 바인딩되지 않습니다. 따라서 rule.match_compare_key()을 호출하면 함수에 self 인수가 수신되지 않습니다. 제대로 기능을 결합 할 경우, 대신에이 작업을 수행 :

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

우리는 장식

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 

컨텍스트 매니저로 구현 된 같은 해킹과 위를 일반화 할 수 있습니다.

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