Источником моей проблемы является функция проверки формы, которая определяется как:
def AirlineICAORequired():
"""
Validates whether string is a valid ICAO code
"""
message = '''Must be an airline ICAO code. Please refer to: <a id = "err-icao" href = "{{ url_for('api.airlines') }}" class = "link-danger">List of Airlines</a> for a list of airlines with ICAO codes listed.'''
def _airlineicao(form, field):
airline = get_airline(field.data)
if not airline:
raise ValidationError(message)
return _airlineicao
Обратите внимание, что я отображаю HTML-тег, в который пытаюсь отобразить URL-адрес маршрута «api.airlines». Мне нужно рассмотреть следующий вопрос:
Я не могу использовать app_context ни из from flask import current_app, ни из фактического экземпляра приложения, определенного как app из app.py, по следующим двум причинам:
а) with current_app.app_context():... по-прежнему будет вызывать ошибку RuntimeError, в которой говорится, что я работаю вне контекста приложения.
б) Если я попытаюсь импортировать from app import app в файл, в котором находится функция (forms.py), то столкнусь с циклической ошибкой импорта, поскольку другие файлы, такие как Routes.py (который имеет Blueprint и импортируется в app. py) импортировать из этого файла.
В html-файле я загружаю сообщение об ошибке как:
{% for error in initializer_form.initializer.errors %}
<li class = "list-group-item list-group-item-danger">
{{ error|safe }}
</li>
{% endfor %}
к сожалению, это приводит к следующим строкам html:
<li class = "list-group-item list-group-item-danger">
Must be an airline ICAO code. Please refer to: <a id = "err-icao" href = "{{ url_for('api.airlines') }}" class = "link-danger">List of Airlines</a> for a list of airlines with ICAO codes listed.
</li>
В контексте initializer_form — это форма, в которой тот же валидатор настроен для проверки.
Также вот изображение того, как это выглядит на веб-сайте, внизу показан URL-адрес для перенаправления, если нажать «Список авиакомпаний»:
Структура файла проекта с показанными соответствующими файлами:
routelookup/
├─ Config/
├─ data/
├─ static/
├─ templates/
├─ api.py
├─ app.py
├─ form.py
├─ routes.py
Импорт (для всех файлов импорт находится в верхней части файла):
приложение.py:
# imports
from flask import Flask
from routes import routes
from api import api
from routeparser import reset_contents, fr_api_logout
from Config import config
import atexit
форма.py:
# imports
from flask_wtf import FlaskForm
from routeparser import get_airline
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import InputRequired, Email, Length, ValidationError
маршруты.py:
# Imports
from flask import Blueprint, render_template, redirect, url_for
from pathlib import Path
from forms import AirlineInitForm
Если я хочу получить правильный URL-адрес в href, а не так, как показано выше, с помощью изображения и второго блока кода html, как я могу подойти к этому, учитывая проблему, которая у меня есть, и хочу ли я сделать это, используя url_for или что-то близкое к этому это без жесткого кодирования?
Дата: 2.10.23
Я создал новый файл под названием form_validators, который частично показан ниже:
# imports
from flask import url_for
from app import app
from routeparser import get_airline
from wtforms.validators import ValidationError
# Relavant validator to the issue
def AirlineICAORequired():
"""
Validates whether string is a valid ICAO code
"""
with app.app_context():
message = f'''Must be an airline ICAO code. Please refer to: <a id = "err-icao" href = "{url_for('api.airlines') }" class = "link-danger">List of Airlines</a> for a list of airlines with ICAO codes listed.'''
def _airlineicao(form, field):
airline = get_airline(field.data)
if not airline:
raise ValidationError(message)
return _airlineicao
все валидаторы были перемещены в этот файл с соответствующим импортом.
В forms.py добавлен только импорт:
from form_validators import AtLength, AirlineICAORequired
@PGHE Я обновил ответ, чтобы, надеюсь, лучше отразить то, что вы сказали.
Я бы попробовал поместить вашу функцию валидатора в отдельный файл и сделать ее независимой от routes.py и forms.py. В настоящее время, если вы импортируете свой app в forms.py, ваш циклический импорт будет app -> routes -> forms -> app.
@PGHE не могли бы дать полный ответ на этот вопрос, в том числе сделать его «независимым» от routes.py? Я создал новый файл (form_validators.py), переместил туда всё (необходимый импорт, функции валидатора) и в forms.py импортировал валидаторы из form_validators. Теперь ошибка циклического импорта указывает на routes.py, который использует форму из forms.py.
Сообщение об ошибке ссылки (отредактировано): Traceback (most recent call last): File "/Users/sergioleylanguren/Desktop/Route Decider/routelookup/forms.py", line 5, in <module> from form_validators import ... File "/Users/sergioleylanguren/Desktop/Route Decider/routelookup/form_validators.py", line 3, in <module> from app import app File "/Users/sergioleylanguren/Desktop/Route Decider/routelookup/app.py", line 7, in <module> from routes import routes ImportError: cannot import name 'routes' from partially initialized module 'routes' (most likely due to a circular import)
вероятно, потому, что порядок импорта: app -> routes -> form -> form_validators -> app. и если бы я попытался импортировать current_app в form_validators, я бы снова получил ошибку вне контекста позже, как описано выше в теле в 1.a






Итак, я решил свою проблему. Я хотел бы поблагодарить @PGHE за то, что они помогли мне настолько, насколько могли, и это вдохновило меня глубже вникнуть в мою собственную проблему. Я внес следующие изменения, некоторые из которых можно увидеть в разделе «Правка 1» в тексте выпуска. Подводя итог, я создал новый файл под названием «forms_validators.py», куда я добавлю все пользовательские валидаторы и импортирую их соответственно в «forms.py». Что касается рассматриваемого валидатора, который мне нужно было «исправить», я внес следующие изменения, как показано в блоке кода ниже:
def AirlineICAORequired(msg = None):
"""
Validates whether string is a valid ICAO code
"""
# added method to add custom messages
if isinstance(msg, str):
message = msg
else:
message = "Must be an airline ICAO code. Please double check to make sure it's correct."
def _airlineicao(form, field):
airline = get_airline(field.data)
if not airline:
raise ValidationError(message)
return _airlineicao
Это важно, как будет видно позже в ответе. Это просто добавляет поддержку добавления сообщений для замены сообщения по умолчанию, отображаемого, если валидатор делает данные формы недействительными.
Согласно form.py, я удалил импорт: AirlineICAORequired из form_validators.py и внутри формы я добавил метод для добавления валидаторов после добавления поля ввода в форму с установленными валидаторами:
# forms.py, lines 20-26 within class AirlineInitForm
def add_validator_to(self, attr, *args):
validator_attr = getattr(self, attr)
if hasattr(validator_attr, "validators"):
validator_attr.validators = validator_attr.validators + list(args)
else:
raise AttributeError("Check again if you inputed the correct attribute name.")
Что касается страницы маршрутов, я определил, что смогу использовать url_for без каких-либо проблем с контекстом приложения, а что нет. Таким образом:
@routes.route('/', methods=['GET', 'POST'], subdomain='application')
def main():
form = AirlineInitForm()
form.add_validator_to(
"initializer",
AirlineICAORequired(f'''
Must be an airline ICAO code. Please refer to: <a id = "err-icao" href = "{url_for("api.airlines")}" class = "link-danger">List of Airlines</a> for a list of airlines with ICAO codes listed.
''')
)
if form.validate_on_submit():
icao = form.initializer.data
return redirect(url_for('api.generate', icao=icao), code=307)
return render_template('main.html', initializer_form=form)
перед проверкой правильности формы (if form.validate_on_submit()) я добавил валидатор (AirlineICAORequired). Мне нужно было добавить его в поле инициализатора с помощью указанного ниже метода, добавленного в класс формы, используя url_for, чтобы получить URL-адрес маршрута API авиакомпании, как я хотел, добавив это в строке f, которая представляет собой сообщение об ошибке, отправляемое в случае неудачной проверки.
Вам нужно показать структуру файлов и то, как вы выполняете импорт. Вы сможете решить проблему циклического импорта, если правильно отделите функции валидатора от маршрутов. Похоже, что в одном из ваших файлов произошел непреднамеренный импорт.