Flask-WTF SelectMultipleField заполняет идентификаторы базы данных вместо имен

Я хотел бы предварить это, заявив, что английский не является моим родным языком, если какие-либо из моих объяснений расплывчаты или не имеют смысла, сообщите мне, и я постараюсь сделать их более ясными.

У меня проблема с заполнением SelectMultipleField правильными данными в Flask-WTF. Как видно из приведенного ниже кода, я передаю объект пользователя в форму. Это работает безупречно, так как заполняет все поля при отображении страницы. Проблема, с которой я сталкиваюсь, заключается в том, что SelectMultipleField заполняется идентификаторами ролей, а не именами ролей.

В разделе форм я создаю список choices, содержащий все доступные варианты, которые должны отображаться. Я могу преобразовать это в choices.append((role.id, role.name)), и оно заполнит поле именами вместо идентификаторов. Однако, если я это сделаю, роли, назначенные пользовательскому объекту (из базы данных), не будут отображаться как предварительно выбранные.

Как я могу сделать так, чтобы поле предварительно заполнялось именами, а не идентификаторами, и сохраняло предварительно выбранные назначенные роли?

Любое руководство или толчок в правильном направлении будет принят с благодарностью. Если не хватает какой-либо информации, которую я не догадался добавить, дайте мне знать, и я ее добавлю.

# Models
class Role(db.Document, RoleMixin):
    meta = {'collection': 'role'} 
    name = db.StringField(max_length=80, unique=True)
    description = db.StringField(max_length=255)

    def __repr__(self):
        return '<Role {}>'.format(self.name)

    def __str__(self):
        return self.name

class User(db.Document, UserMixin):
    meta = {'collection': 'User'}   
    first_name = db.StringField(max_length=30)
    last_name = db.StringField(max_length=30)
    email = db.StringField(max_length=120)
    password_hash = db.StringField(max_length=255)
    active = db.BooleanField(default=True)
    roles = db.ListField(db.ReferenceField(Role), default=[])

    def __repr__(self):
        return '<User {}>'.format(self.email)

    def __str__(self):
        return self.email

# Forms
class EditUserForm(FlaskForm):
    choices = []
    for role in Role.objects:
        choices.append((role.name, role.id))

    first_name = StringField('First name', validators=[DataRequired()])
    last_name = StringField('Last name', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password')
    password2 = PasswordField('Repeat password', validators=[EqualTo('password')])
    roles = SelectMultipleField('User roles', choices=choices)
    submit = SubmitField('Save')

# Routes
@app.route('/users/edit/<id>', methods=['GET', 'POST'])
@roles_required('admin')
def edit_user(id):
    user = User.objects(id=id).first()
    form = EditUserForm(obj=user)

    if form.validate_on_submit() and request.method == 'POST':
        user.first_name=form.first_name.data
        user.last_name=form.last_name.data
        user.email=form.email.data
        user.roles = []

        for role in form.roles.data:
            r = Role.objects(name=role).first()
            user.set_role(r.id)

        if form.password.data:
            user.set_password(form.password.data)

        user.save()
        return redirect(url_for('users'))

    return render_template('edit_user.html', title='Edit user', user=user, form=form)

Шаблон:

{% extends "layout.html" %}
{% set active_page = "edit_user" %}
{% block jumbotron %}

    <h1>{{ title }}</h1>
    <form action="" method="post">
        {{ form.hidden_tag() }}
        <p>
            {{ form.first_name.label }}<br>
            {{ form.first_name(size=32) }}<br>
            {% for error in form.first_name.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.last_name.label }}<br>
            {{ form.last_name(size=32) }}<br>
            {% for error in form.last_name.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.email.label }}<br>
            {{ form.email(size=32) }}<br>
            {% for error in form.email.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.password.label }}<br>
            {{ form.password(size=32) }}<br>
            {% for error in form.password.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.password2.label }}<br>
            {{ form.password2(size=32) }}<br>
            {% for error in form.password2.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.roles.label }}<br>
            {{ form.roles(size=32) }}<br>
            {% for error in form.roles.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ form.submit() }}</p>
    </form>

{% endblock %}

Я попытался создать свой собственный объект с кодом ниже, чтобы перейти к форме, однако мне это не удалось. При передаче этого объекта в форму поля не заполнялись.

user = User.objects(id=id).first()
temps = []

u = user.to_mongo()
u['_id'] = {'$oid': str(u['_id'])}
temp_roles = []
for role in user.roles:
    r = role.to_mongo()
    r['_id'] = {'$oid': str(r['_id'])}
    temp_roles.append(r)

u['roles'] = temp_roles

person Kristoffer    schedule 08.07.2018    source источник
comment
если вам нужно поле имени в обоих случаях, почему бы не использовать choices.append((role.name, role.name)) ?   -  person PRMoureu    schedule 08.07.2018


Ответы (1)


Я думаю, что PRMoureu прав.

Вы могли бы choices.append((role.name, role.name))

к вашему сведению.

Варианты выбора SelectField — это список пар кортежей (значение, метка), перейдите к этому ссылка для более подробной информации.

Поля выбора содержат свойство выбора, которое представляет собой последовательность пар (значение, метка). Теоретически часть значения может быть любого типа, но поскольку данные формы отправляются браузером в виде строк, вам потребуется предоставить функцию, которая может принудительно преобразовать строковое представление в сопоставимый объект.

coerce по умолчанию — unicode, int, если вы используете (id, name).

person hotsing    schedule 08.07.2018
comment
Ну, теперь я чувствую себя дураком. Я провел пару часов, глядя на это ранее сегодня, и не мог в жизни понять это. Это делает именно то, что я ищу. Я очень благодарен за развернутый ответ. - person Kristoffer; 08.07.2018