В процессе разработки этого блога возникла небольшая проблема, при использовании схемы роутинга урлов в виде:
/<string:val1>/<string:val2>/
возникает конфликт, view с которым связана подобная схема начинает еще и обрабатывать то, что лежит в /static/ и веб-сервер отдает ожидаемую 404 ошибку. Так вот, чтобы избегать подобных конфликтов во Flask (а точнее в его wsgi слое - werkzeug) можно создать кастомный url converter.
Т.е. если есть например такая вьюха:
@app.route('/<string:name>/<string:page_name>/') def page(name, page_name): flat_page = static_blog.get_page_by_name_for(name, page_name) return render_template(getattr(flat_page, 'template'), flat_page=flat_page)
Да еще и такая:
@app.route('/<string:name>/<string:lang>/') def blog(name, lang): articles = static_blog.get_articles(name, language=lang) return render_template('blog.html', articles=articles, language=lang)
То эти вьюхи мало того, что будут "перекрывать" друг друга, так еще и весьма вероятно мешать раздаче статики из static_folder. Как решать? Достаточно просто, нужно написать кастомные url converter для того, чтобы обеспечить правильный url mapping.
Делается это таким образом:
''' Url converters, to avoid wrong url pattern matching ''' from werkzeug.routing import BaseConverter, ValidationError class NoSomethingConverter(BaseConverter): restriction = [] def __ini__(self, url_map): super(NoSomethingConverter, self).__init__(url_map) def to_python(self, value): # в классах наследниках нужно определить функцию # которая будет возвращать урлы которые нужно отклонить # в виде списка if value in self.restriction(): raise ValidationError() return value class NoStaticConverter(NoSomethingConverter): # собственно определяем функцию self.restriction() restriction = lambda x: ['static'] # добавим конвертер в url_map фласка app.url_map.converters['no_static'] = NoStaticConverter class NoBlogsConverter(NoSomethingConverter): restriction = static_blog.get_blogs_names app.url_map.converters['no_blogs'] = NoBlogsConverter class NoPagesConverter(NoSomethingConverter): restriction = static_blog.get_pages_names app.url_map.converters['no_pages'] = NoPagesConverter
И прописываются в route для конфликтных view:
@app.route('/<no_blogs:name>/<string:page_name>/') def page(name, page_name): flat_page = static_blog.get_page_by_name_for(name, page_name) return render_template(getattr(flat_page, 'template'), flat_page=flat_page) @app.route('/<no_pages:name>/<string:lang>/') def blog(name, lang): articles = static_blog.get_articles(name, language=lang) return render_template('blog.html', articles=articles, language=lang) @app.route('/<no_static:name>/<string:lang>/<string:article_name>/') def post(name, lang, article_name): article = static_blog.get_article_by_name(article_name) return render_template('post.html', article=article, language=lang)
Надеюсь, кому-то поможет. Чтобы разобраться в деталях можно сходить по ссылкам: