Пользовательские атрибуты для Flask WTForms

Разрабатываю сайт на Flask и AngularJS. Мне нужно отправить форму с AJAX с помощью AngularJS, но для этого требуется настраиваемый атрибут для поля ввода. Например, у меня есть форма в шаблоне Jinja2:

<form method="post" action="/">
    {{ form.hidden_tag() }}
    {{ form.name(placeholder="Name") }}
</form>

Итак, как я могу добавить атрибут из AngularJS, скажем «ng-model» для моего поля «name»?

Спасибо за вашу помощь!


person kuynik    schedule 07.12.2013    source источник
comment
Хрм; Я не думаю, что Jinja2 поддерживает передачу **{...} ключевого слова splash, поэтому form.name(placeholder="Name", **{'ng-model': 'value'}) не сработает. Я думаю.   -  person Martijn Pieters    schedule 07.12.2013
comment
Я нашел решение с помощью WTForms Custom Widget. В документации есть пример добавления атрибута class.   -  person kuynik    schedule 07.12.2013
comment
Да, здесь я бы использовал настраиваемый виджет, чтобы обойти ограничение в Jinja2.   -  person Martijn Pieters    schedule 07.12.2013


Ответы (2)


В идентификаторах Python нельзя использовать дефисы, и только идентификаторы Python могут использоваться в качестве пар keyword_argument=value в вызове.

Но у вас есть несколько способов обойти это здесь; вы можете передать параметры с префиксом ng- в сопоставлении **kwargs, указать класс Meta, который вы используете для формы, для преобразования _ в - для атрибутов ng_ или использовать настраиваемый виджет для выполнения того же преобразования.

Передайте карту ** kwargs

С **kwargs вы можете передавать аргументы, которые не являются идентификаторами Python, если они являются строками. Используйте это для отображения полей формы:

{{ form.name(placeholder="Name", **{'ng-model': 'NameModel'}) }}

Вы можете поместить ту же информацию в отображение render_kw в определении поля:

class MyForm(Form):
    name = StringField(u'Full Name', render_kw={'ng-model': 'NameModel'})

и он будет использоваться каждый раз, когда вы визуализируете поле; render_kw добавляется к любым аргументам, которые вы передаете при рендеринге, поэтому:

{{ form.name(placeholder="Name") }}

отобразит атрибуты placeholder и ng-model.

Подкласс Meta и используйте его в своей форме

Начиная с WTForm 2.0, действительно запрашивается Meta класс, который вы прикрепляете к своей форме для отображения полей с помощью Meta.render_field() hook:

import wtform.meta

class AngularJSMeta:
    def render_field(self, field, render_kw):
        ng_keys = [key for key in render_kw if key.startswith('ng_')]
        for key in ng_keys:
            render_kw['ng-' + key[3:]] = render_kw.pop(key)
        # WTForm dynamically constructs a Meta class from all Meta's on the
        # form MRO, so we can use super() here:
        return super(AngularJSMeta, self).render_field(field, render_kw)

Либо используйте это прямо в вашей форме:

class MyForm(Form):
    Meta = AngularJSMeta

    name = StringField(u'Full Name')

или создать подкласс класса Form:

class BaseAngularJSForm(Form):
    Meta = AngularJSMeta

и используйте это как основу для всех ваших форм:

class MyForm(BaseAngularJSForm):
    name = StringField(u'Full Name')

и теперь вы можете использовать этот шаблон с:

{{ form.name(placeholder="Name", ng_model='NameModel') }}

Виджеты подкласса

Вы можете создать подкласс виджета по вашему выбору с помощью:

class AngularJSMixin(object):
    def __call__(self, field, **kwargs):
        for key in list(kwargs):
            if key.startswith('ng_'):
                kwargs['ng-' + key[3:]] = kwargs.pop(key)
        return super(AngularJSMixin, self).__call__(field, **kwargs)

class AngularJSTextInput(AngularJSMixin, TextInput):
    pass

Это преобразует любой аргумент ключевого слова, начинающийся с ng_, в аргумент ключевого слова, начинающийся с ng-, обеспечивая возможность добавления правильных атрибутов HTML. AngularJSMixin можно использовать с любым классом виджетов.

Используйте это как атрибут widget в своем поле:

class MyForm(Form):
    name = StringField(u'Full Name', widget=AngularJSTextInput())

и снова вы можете использовать ng_model при рендеринге вашего шаблона:

{{ form.name(placeholder="Name", ng_model='NameModel') }}

Во всех случаях атрибуты будут добавлены как placeholder="Name" ng-model="NameModel" в обработанный HTML:

<input id="name" name="name" ng-model="NameModel" placeholder="Name" type="text" value="">
person Martijn Pieters    schedule 07.12.2013
comment
Как мне вызвать их в макросе jinja2? Просто field.ng-model у меня как-то не работает. Я пошел с {% if field.ng-model %} {{ field.ng-model }} {% endif %}, но он не работает - person blkpingu; 14.10.2019
comment
@TobiasKolb: вы пытаетесь получить доступ к атрибуту, и имена атрибутов подчиняются тем же правилам; - - оператор вычитания в коде Python, поэтому не может быть частью идентификатора. Даже в этом случае ng-model не является атрибутом поля. Если вы использовали параметр render_kw={...} для определения значения атрибута ng-model для своего поля, вы можете использовать field.render_kw['ng-model'] для доступа к значению. - person Martijn Pieters; 07.04.2020
comment
@TobiasKolb: (и извиняюсь за то, что нашел ваш комментарий только сейчас, через 6 месяцев после факта, он, должно быть, ускользнул от моего внимания в то время). - person Martijn Pieters; 07.04.2020

{{ form.username(placeholder='your name', size=20, **{'ng-model':'hello', 'class':'hello'}) }}

Я думаю лучше

person flyingzl    schedule 25.11.2014
comment
работает как шарм! Спасибо! Если кому-то интересно, он также печатает все ваши другие ключи и значения. - person Jonathan; 15.08.2016
comment
По-прежнему прекрасно работает с Flask 1.x, я использую его для добавления некоторых настраиваемых атрибутов для проверки на стороне клиента. - person sofly; 04.12.2018