Правильно сформируйте структуру канонического URL-адреса с помощью URL-процессоров

РЕДАКТИРОВАТЬ: Не могли бы вы более внимательно прочитать мой вопрос? Этот вопрос специфичен, а не дублирован, как было сказано. Очевидно, я прочитал этот question перед публикацией.

В этом демо-коде из docs фрагмент после параметра lang будет статическим. Итак, на английском языке /en/about, и, например, на португальском языке /pt/about. Ну, правильно, должно быть /pt/sobre.

Любая идея о правильном способе сделать это с помощью URL-процессоров?

from flask import Flask, g
app = Flask(__name__)
@app.url_defaults
def add_language_code(endpoint, values):
 if 'lang_code' in values or not g.lang_code:
 return
 if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
 values['lang_code'] = g.lang_code
@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
 g.lang_code = values.pop('lang_code', None)
@app.route('/<lang_code>/')
def index():
 ...
@app.route('/<lang_code>/about')
def about():
</lang_code></lang_code>
1 ответ

Хорошо, у вас уже есть языковой префикс. Поэтому вам нужно иметь несколько переводов для следующей части.

Самый простой способ использования нескольких маршрутов или конвертеров:

@app.route('/<lang_code>/about')
@app.route('/<lang_code>/sobre')
def about():
 pass
</lang_code></lang_code>

или

@app.route('/<lang_code>/<any(about,sobre):about>')
def about(about):
 pass
</any(about,sobre):about></lang_code>

Но трудно поддерживать и добавлять новые языки.

Второй способ - это метод маршрута изменения, чтобы перевести специальные слова или добавить специальный конвертер конвертирования, последний более интересный и неявный:

from werkzeug.routing import AnyConverter
languages = ['en', 'pt']
def translate_url(word, language):
 if language == 'pt' and word == 'about':
 return 'sobre'
 return word
class TranslateConverter(AnyConverter):
 def __init__(self, map, item):
 AnyConverter.__init__(self, map, *[translate_url(item, language) 
 for language in languages])
app.url_map.converters['tr'] = TranslateConverter
@app.route('/<lang_code>/<tr(about):about>')
def about(about):
 pass
</tr(about):about></lang_code>

Но этот пример имеет следующую проблему:

/en/about
/en/sorbe
/pt/about
/pt/sorbe

- действительные URL-адреса, но вы также можете попробовать использовать собственный Rule class (Flask.url_rule_class), где в методе match вы можете обрабатывать эти случаи:

from werkzeug.routing import AnyConverter, Rule
class TranslateConverter(AnyConverter):
 def __init__(self, map, item):
 self.language_pairs = {language: translate_url(item, language)
 for language in languages}
 AnyConverter.__init__(self, map, *tuple(self.language_pairs.values()))
class TranslateCorrelationRule(Rule):
 def match(self, path):
 result = Rule.match(self, path)
 if result is None:
 return result
 lang_code = result.get('lang_code')
 if lang_code is None:
 return result
 for name, value in self._converters.items():
 if not isinstance(value, TranslateConverter):
 continue
 if value.language_pairs[lang_code] != result[name]:
 return
 return result
app.url_map.converters['tr'] = TranslateConverter
app.url_rule_class = TranslateCorrelationRule

Если вы упростите использование url_for для этих примеров, вы можете использовать следующий образец:

@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
 if not values:
 return
 g.lang_code = values.pop('lang_code', None)
 for key, value in values.items():
 if key.startswith('_'):
 values.pop(key)
class TranslateCorrelationRule(Rule):
 def _update_translate_values(self, values):
 lang_code = values.get('lang_code', getattr(g, 'lang_code', None))
 if lang_code is None:
 return values
 values = values.copy()
 for argument in self.arguments:
 if argument in values:
 continue
 converter = self._converters[argument]
 if not isinstance(converter, TranslateConverter):
 continue
 values[argument] = converter.language_pairs[lang_code]
 return values
 def suitable_for(self, values, method=None):
 return Rule.suitable_for(self, self._update_translate_values(values),
 method)
 def build(self, values, append_unknown=True):
 return Rule.build(self, self._update_translate_values(values),
 append_unknown)

licensed under cc by-sa 3.0 with attribution.