Maison > Article > développement back-end > Utilisation du framework de formulaire WTForms dans Python's Flask
Télécharger et installer
Le moyen le plus simple d'installer WTForms est d'utiliser easy_install et pip :
easy_install WTForms # or pip install WTForms
Vous pouvez télécharger manuellement WTForms depuis PyPI et exécuter python setup.py install .
Si vous êtes le genre de personne qui aime prendre tous les risques, exécutez simplement la dernière version de Git et vous pourra obtenir les dernières modifications de la version packagée de l'ensemble, ou accéder à la page d'accueil du projet pour cloner le référentiel de code
Concepts principaux
La classe Forms est le conteneur principal. des WTForms. Les formulaires représentent une collection de champs. Les champs sont accessibles sous forme de dictionnaire ou d'attributs sur un formulaire
Les champs font le plus gros du travail. Chaque champ représente un type de données, et les opérations sur le terrain forcent la saisie de ces données. type. Par exemple, InputRequired et StringField représentent deux types de données différents. En plus des données qu'il contient, le champ contient également un grand nombre d'attributs utiles, tels que des étiquettes, des descriptions et des listes d'erreurs de validation
Chaque champ. a une instance de Widget (Widget). Le travail de Widget est de restituer la représentation HTML du champ. Chaque champ peut spécifier une instance de Widget, mais chaque champ a un widget raisonnable par défaut. Certains champs sont simples et pratiques, comme TextAreaField. defaults Le widget est le
StringField de TextArea.
Afin de préciser les règles de validation, le champ contient une liste de validateurs.
Démarrer
Entrons droit au but et définissons notre Le premier formulaire ::
from wtforms import Form, BooleanField, StringField, validators class RegistrationForm(Form): username = StringField('Username', [validators.Length(min=4, max=25)]) email = StringField('Email Address', [validators.Length(min=6, max=35)]) accept_rules = BooleanField('I accept the site rules', [validators.InputRequired()])
Lorsque vous créez un formulaire, la façon dont vous définissez les champs est similaire à de nombreuses définitions ORM Leurs colonnes : en définissant la classe variables, c'est-à-dire des instances de champs
Étant donné que les formulaires sont des classes Python classiques, vous pouvez facilement les étendre pour obtenir ce que vous voulez ::
class ProfileForm(Form): birthday = DateTimeField('Your Birthday', format='%m/%d/%y') signature = TextAreaField('Forum Signature') class AdminProfileForm(ProfileForm): username = StringField('Username', [validators.Length(max=40)]) level = IntegerField('User Level', [validators.NumberRange(min=0, max=10)])
Par sous-classement, la classe AdminProfileForm obtient tous les champs d'une classe ProfileForm déjà définie. Cela vous permet de partager facilement un sous-ensemble commun de champs entre différents formulaires. Par exemple, dans l'exemple ci-dessus, nous ajoutons un champ réservé aux administrateurs. au ProfileForm.
Utiliser le formulaire
Utiliser un formulaire est aussi simple que de l'instancier. Considérez la fonction d'affichage de style Django suivante, qui utilise la classe RegistrationForm définie précédemment :
def register(request): form = RegistrationForm(request.POST) if request.method == 'POST' and form.validate(): user = User() user.username = form.username.data user.email = form.email.data user.save() redirect('register') return render_response('register.html', form=form)
Tout d'abord, nous instancions le formulaire en lui donnant certaines données disponibles dans request.POST. Nous vérifions ensuite si la requête utilise le POST, et si. si c'est le cas, nous validons le formulaire et vérifions que l'utilisateur respecte les règles. En cas de succès, nous créons un nouveau modèle Utilisateur et lui envoyons les données du formulaire validé, et enfin le sauvegardons
Modifier les objets existants
Notre exemple d'inscription précédent montrait comment recevoir des entrées et les valider pour de nouvelles entrées, juste si nous voulons modifier des objets existants. Que faire de l'objet ? C'est simple ::def edit_profile(request): user = request.current_user form = ProfileForm(request.POST, user) if request.method == 'POST' and form.validate(): form.populate_obj(user) user.save() redirect('edit_profile') return render_response('edit_profile.html', form=form)Ici, nous instancions le formulaire en fournissant à la fois le request.POST et l'utilisateur qui s'oppose au formulaire. le formulaire obtiendra toutes les données de l'objet utilisateur qui n'apparaissent pas dans les données soumises.Nous utilisons également la méthode populate_obj du formulaire pour repeupler l'objet utilisateur avec le contenu du formulaire validé. commodité, en utilisant Utilisé lorsque le nom du champ correspond au nom de l'objet auquel vous fournissez des données. Normalement, vous souhaiterez attribuer la valeur manuellement, mais pour cet exemple simple, c'est le meilleur. Il peut également être utilisé avec CURD et. formulaires de gestion (admin).
Exploration dans la console
Les formulaires WTForms sont des objets conteneurs très simples, et peut-être le moyen le plus simple de découvrir ce qui fonctionne pour vous dans un form joue avec le formulaire dans la console :>>> from wtforms import Form, StringField, validators >>> class UsernameForm(Form): ... username = StringField('Username', [validators.Length(min=5)], default=u'test') ... >>> form = UsernameForm() >>> form['username'] <wtforms.fields.StringField object at 0x827eccc> >>> form.username.data u'test' >>> form.validate() False >>> form.errors {'username': [u'Field must be at least 5 characters long.']}Ce que nous voyons, c'est que lorsque vous instanciez un formulaire, le formulaire contient des instances de tous les champs, champs d'accès Peuvent être sous forme de dictionnaire ou sous forme d'attribut. Ces champs ont leurs propres propriétés, tout comme le formulaire ci-joint Lorsque nous validons le formulaire, il renvoie un faux logique, ce qui signifie qu'au moins une règle de validation ne l'est pas. Le formulaire satisf. error vous donnera un résumé de toutes les erreurs.
>>> form2 = UsernameForm(username=u'Robert') >>> form2.data {'username': u'Robert'} >>> form2.validate() TrueCette fois, nous transmettons une nouvelle valeur pour le nom d'utilisateur lors de l'instanciation du UserForm, ce qui suffit pour valider le formulaire .
Comment le formulaire récupère les données
En plus d'utiliser les deux premiers paramètres (formdata et obj) pour fournir des données, vous pouvez transmettre des paramètres de mots clés pour remplir le formulaire. Veuillez noter que certains noms de paramètres sont réservés : formdata, obj, prefix.
def change_username(request): user = request.current_user form = ChangeUsernameForm(request.POST, user, username='silly') if request.method == 'POST' and form.validate(): user.username = form.username.data user.save() return redirect('change_username') return render_response('change_username.html', form=form)Bien qu'en pratique vous n'utilisiez presque jamais les 3 méthodes ensemble, voici un exemple de la façon dont WTForms recherche le champ du nom d'utilisateur : Si le le formulaire est soumis (request.POST n'est pas vide), puis traitez la saisie du formulaire. En pratique, même s'il n'y a pas de saisie de formulaire pour ce champ, s'il existe un type de saisie de formulaire, nous traiterons la saisie du formulaire.
S'il n'y a pas de saisie de formulaire, essayez l'ordre suivant :
Validateur
WTForms中的验证器(Validators)为域(field)提供一套验证器, 当包含域的表单进行验证时运行. 你提供验证器可通过域构造函数的第二个参数validators:
class ChangeEmailForm(Form): email = StringField('Email', [validators.Length(min=6, max=120), validators.Email()])
你可以为一个域提供任意数量的验证器. 通常, 你会想要提供一个定制的错误消息:
class ChangeEmailForm(Form): email = StringField('Email', [ validators.Length(min=6, message=_(u'Little short for an email address?')), validators.Email(message=_(u'That\'s not a valid email address.')) ])
这通常更好地提供你自己的消息, 作为必要的默认消息是通用的. 这也是提供本地化错误消息的方法.
对于内置的验证器的列表, 查阅 Validators.
渲染域
渲染域和强制它为字符串一样简单:
>>> from wtforms import Form, StringField >>> class SimpleForm(Form): ... content = StringField('content') ... >>> form = SimpleForm(content='foobar') >>> str(form.content) '<input id="content" name="content" type="text" value="foobar" />' >>> unicode(form.content) u'<input id="content" name="content" type="text" value="foobar" />'
然而, 渲染域的真正力量来自于它的 __call__() 方法. 调用(calling)域, 你可以提供关键词参数, 它们会在输出中作为HTML属性注入.
>>> form.content(style="width: 200px;", class_="bar") u'<input class="bar" id="content" name="content" style="width: 200px;" type="text" value="foobar" />'
现在, 让我们应用这个力量在 Jinja 模板中渲染表单. 首先, 我们的表单:
class LoginForm(Form): username = StringField('Username') password = PasswordField('Password') form = LoginForm()
然后是模板文件:
<form method="POST" action="/login"> <p>{{ form.username.label }}: {{ form.username(class="css_class") }}</p> <p>{{ form.password.label }}: {{ form.password() }}</p> </form>
相同的, 如果你使用 Django 模板, 当你想要传送关键词参数时, 你可以使用我们在Django扩展中提供的模板标签form_field:
{% load wtforms %} <form method="POST" action="/login"> <p> {{ form.username.label }}: {% form_field form.username class="css_class" %} </p> <p> {{ form.password.label }}: {{ form.password }} </p> </form>
这两个将会输出:
<form method="POST" action="/login"> <p> <label for="username">Username</label>: <input class="css_class" id="username" name="username" type="text" value="" /> </p> <p> <label for="password">Password</label>: <input id="password" name="password" type="password" value="" /> </p> </form>
WTForms是模板引擎不可知的, 同时会和任何允许属性存取、字符串强制(string coercion)、函数调用的引擎共事. 在 Django 模板中, 当你不能传送参数时, 模板标签 form_field 提供便利.
显示错误消息
现在我们的表单拥有一个模板, 让我们增加错误消息::
<form method="POST" action="/login"> <p>{{ form.username.label }}: {{ form.username(class="css_class") }}</p> {% if form.username.errors %} <ul class="errors">{% for error in form.username.errors %}<li>{{ error }}</li>{% endfor %}</ul> {% endif %} <p>{{ form.password.label }}: {{ form.password() }}</p> {% if form.password.errors %} <ul class="errors">{% for error in form.password.errors %}<li>{{ error }}</li>{% endfor %}</ul> {% endif %} </form>
如果你喜欢在顶部显示大串的错误消息, 也很简单:
{% if form.errors %} <ul class="errors"> {% for field_name, field_errors in form.errors|dictsort if field_errors %} {% for error in field_errors %} <li>{{ form[field_name].label }}: {{ error }}</li> {% endfor %} {% endfor %} </ul> {% endif %}
由于错误处理会变成相当冗长的事情, 在你的模板中使用 Jinja 宏(macros, 或者相同意义的) 来减少引用是更好的. (例子)
定制验证器
这有两种方式定制的验证器. 通过定义一个定制的验证器并在域中使用它:
from wtforms.validators import ValidationError def is_42(form, field): if field.data != 42: raise ValidationError('Must be 42') class FourtyTwoForm(Form): num = IntegerField('Number', [is_42])
或者通过提供一个在表单内的特定域(in-form field-specific)的验证器:
class FourtyTwoForm(Form): num = IntegerField('Number') def validate_num(form, field): if field.data != 42: raise ValidationError(u'Must be 42')
编写WTForm扩展示例
class TagListField(Field): widget = TextInput() def _value(self): if self.data: return u', '.join(self.data) else: return u'' def process_formdata(self, valuelist): if valuelist: self.data = [x.strip() for x in valuelist[0].split(',')] else: self.data = []
根据上面的代码,将TagListField中的字符串转为models.py中定义的Tag对象即可:
class TagListField(Field): widget = TextInput() def __init__(self, label=None, validators=None, **kwargs): super(TagListField, self).__init__(label, validators, **kwargs) def _value(self): if self.data: r = u'' for obj in self.data: r += self.obj_to_str(obj) return u'' else: return u'' def process_formdata(self, valuelist): print 'process_formdata..' print valuelist if valuelist: tags = self._remove_duplicates([x.strip() for x in valuelist[0].split(',')]) self.data = [self.str_to_obj(tag) for tag in tags] else: self.data = None def pre_validate(self, form): pass @classmethod def _remove_duplicates(cls, seq): """去重""" d = {} for item in seq: if item.lower() not in d: d[item.lower()] = True yield item @classmethod def str_to_obj(cls, tag): """将字符串转换位obj对象""" tag_obj = Tag.query.filter_by(name=tag).first() if tag_obj is None: tag_obj = Tag(name=tag) return tag_obj @classmethod def obj_to_str(cls, obj): """将对象转换为字符串""" if obj: return obj.name else: return u'' class TagListField(Field): widget = TextInput() def __init__(self, label=None, validators=None, **kwargs): super(TagListField, self).__init__(label, validators, **kwargs) def _value(self): if self.data: r = u'' for obj in self.data: r += self.obj_to_str(obj) return u'' else: return u'' def process_formdata(self, valuelist): print 'process_formdata..' print valuelist if valuelist: tags = self._remove_duplicates([x.strip() for x in valuelist[0].split(',')]) self.data = [self.str_to_obj(tag) for tag in tags] else: self.data = None def pre_validate(self, form): pass @classmethod def _remove_duplicates(cls, seq): """去重""" d = {} for item in seq: if item.lower() not in d: d[item.lower()] = True yield item @classmethod def str_to_obj(cls, tag): """将字符串转换位obj对象""" tag_obj = Tag.query.filter_by(name=tag).first() if tag_obj is None: tag_obj = Tag(name=tag) return tag_obj @classmethod def obj_to_str(cls, obj): """将对象转换为字符串""" if obj: return obj.name else: return u''
主要就是在process_formdata这一步处理表单的数据,将字符串转换为需要的数据。最终就可以在forms.py中这样定义表单了:
... class ArticleForm(Form): """编辑文章表单""" title = StringField(u'标题', validators=[Required()]) category = QuerySelectField(u'分类', query_factory=get_category_factory(['id', 'name']), get_label='name') tags = TagListField(u'标签', validators=[Required()]) content = PageDownField(u'正文', validators=[Required()]) submit = SubmitField(u'发布') ... ... class ArticleForm(Form): """编辑文章表单""" title = StringField(u'标题', validators=[Required()]) category = QuerySelectField(u'分类', query_factory=get_category_factory(['id', 'name']), get_label='name') tags = TagListField(u'标签', validators=[Required()]) content = PageDownField(u'正文', validators=[Required()]) submit = SubmitField(u'发布') ... 在views.py中处理表单就很方便了: def edit_article(): """编辑文章""" form = ArticleForm() if form.validate_on_submit(): article = Article(title=form.title.data, content=form.content.data) article.tags = form.tags.data article.category = form.category.data try: db.session.add(article) db.session.commit() except: db.session.rollback() return render_template('dashboard/edit.html', form=form) def edit_article(): """编辑文章""" form = ArticleForm() if form.validate_on_submit(): article = Article(title=form.title.data, content=form.content.data) article.tags = form.tags.data article.category = form.category.data try: db.session.add(article) db.session.commit() except: db.session.rollback() return render_template('dashboard/edit.html', form=form)
代码是不是很简洁了?^_^。。。
当然了写一个完整的WTForms扩展还是很麻烦的。这里只是刚刚入门。可以看官方扩展QuerySelectField的源码。。。
效果:
更多Utilisation du framework de formulaire WTForms dans Pythons Flask相关文章请关注PHP中文网!