Heim >Backend-Entwicklung >Python-Tutorial >Verwendung des WTForms-Formularframeworks in Pythons Flask

Verwendung des WTForms-Formularframeworks in Pythons Flask

高洛峰
高洛峰Original
2017-03-03 14:17:112134Durchsuche

Herunterladen und installieren

Der einfachste Weg, WTForms zu installieren, ist die Verwendung von easy_install und pip:

easy_install WTForms
# or
pip install WTForms

Sie können WTForms manuell von PyPI herunterladen und python setup.py install ausführen.

Wenn Sie zu der Art von Person gehören, die gerne alle Risiken eingeht, führen Sie einfach die neueste Version von Git und Sie aus Sie können die neuesten Änderungen in der Paketversion abrufen oder auf die Projekthomepage gehen, um das Code-Repository zu klonen.

Hauptkonzepte
Die Forms-Klasse ist der Kerncontainer von WTForms. Auf Felder kann als Wörterbuch oder als Attribute in einem Formular zugegriffen werden Typ. InputRequired und StringField stellen beispielsweise zwei verschiedene Datentypen dar. Zusätzlich zu den darin enthaltenen Daten enthält das Feld auch eine große Anzahl nützlicher Attribute, wie z. B. Beschriftungen, Beschreibungen und Listen von Validierungsfehlern Die Aufgabe von Widget besteht darin, die HTML-Darstellung des Felds zu rendern. Jedes Feld verfügt jedoch standardmäßig über ein sinnvolles Widget, z. B. TextAreaField Standardwerte Das Widget ist das
StringField von TextArea.
Um die Validierungsregeln festzulegen, enthält das Feld eine Liste von Validatoren.

Start
Kommen wir gleich zur Sache und definieren Unser Das erste Formular::

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()])

Wenn Sie ein Formular erstellen, ähnelt die Art und Weise, wie Sie Felder definieren, vielen ORM-Definitionen. Ihre Spalten: durch Definition der Klasse Variablen, d. h. Instanzen von Feldern.

Da Formulare reguläre Python-Klassen sind, können Sie sie ganz einfach nach Ihren Wünschen erweitern::

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)])

Durch Unterklassen erhält die AdminProfileForm-Klasse alle Felder einer bereits definierten ProfileForm-Klasse. Dadurch können Sie problemlos eine gemeinsame Teilmenge von Feldern zwischen verschiedenen Formularen teilen. Im obigen Beispiel fügen wir beispielsweise ein Nur-Administrator-Feld hinzu zum ProfileForm.

Verwenden des Formulars

Die Verwendung eines Formulars ist so einfach wie seine Instanziierung. Betrachten Sie die folgende Ansichtsfunktion im Django-Stil, die die zuvor definierte RegistrationForm-Klasse verwendet:

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)

Zuerst instanziieren wir das Formular und geben ihm einige in request.POST verfügbare Daten. Anschließend prüfen wir, ob die Anfrage POST verwendet Das heißt, wir validieren das Formular und prüfen, ob der Benutzer die Regeln einhält. Bei Erfolg erstellen wir ein neues Benutzermodell, senden die Daten aus dem validierten Formular an dieses und speichern es schließlich >Vorhandene Objekte bearbeiten

Unser vorheriges Registrierungsbeispiel zeigte, wie man Eingaben empfängt und für neue Einträge validiert, nur wenn wir vorhandene Objekte bearbeiten möchten. Was tun mit dem Objekt? Es ist ganz einfach:

Hier instanziieren wir das Formular, indem wir sowohl den request.POST als auch das Benutzerobjekt für das Formular bereitstellen. Das Formular ruft alle Daten vom Benutzerobjekt ab, die nicht in den übermittelten Daten enthalten sind.

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)
Wir verwenden auch die Methode „populate_obj“ des Formulars, um das Benutzerobjekt erneut mit den Inhalten des validierten Formulars zu füllen Praktischerweise wird es verwendet, wenn der Feldname mit dem Namen des Objekts übereinstimmt, dem Sie Daten bereitstellen. Normalerweise möchten Sie den Wert manuell zuweisen, aber für dieses einfache Beispiel ist es am besten. Es kann auch mit CURD verwendet werden Verwaltungsformulare (Admin).

Erkunden in der Konsole

WTForms-Formulare sind sehr einfache Containerobjekte und vielleicht der einfachste Weg, um herauszufinden, was in einem für Sie funktioniert form is Playing with the form in the console:

Was wir sehen ist, dass, wenn Sie ein Formular instanziieren, das Formular Instanzen aller Felder enthält, Zugriffsfelder können in Wörterbuchform oder Attributform vorliegen. Diese Felder haben ihre eigenen Eigenschaften, genau wie das beigefügte Formular.

>>> 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&#39;test&#39;
>>> form.validate()
False
>>> form.errors
{&#39;username&#39;: [u&#39;Field must be at least 5 characters long.&#39;]}
Wenn wir das Formular validieren, gibt es logisch „falsch“ zurück, was bedeutet, dass mindestens eine Validierungsregel nicht vorhanden ist zufrieden. Formularfehler geben Ihnen eine Zusammenfassung aller Fehler.

Dieses Mal übergeben wir beim Instanziieren der UserForm einen neuen Wert für den Benutzernamen. was ausreicht, um das Formular zu validieren.

>>> form2 = UsernameForm(username=u&#39;Robert&#39;)
>>> form2.data
{&#39;username&#39;: u&#39;Robert&#39;}
>>> form2.validate()
True

Wie das Formular Daten erhält

Zusätzlich zur Verwendung der ersten beiden Parameter (formdata und obj) zur Bereitstellung von Daten können Sie Schlüsselwortparameter übergeben um das Formular auszufüllen. Bitte beachten Sie, dass einige Parameternamen reserviert sind: formdata, obj, prefix.

formdata hat eine höhere Priorität als obj und obj hat eine höhere Priorität als Schlüsselwortparameter 🎜>

Obwohl Sie in der Praxis fast nie alle drei Methoden zusammen verwenden, finden Sie hier ein Beispiel dafür, wie WTForms das Feld „Benutzername“ nachschlägt:

Wenn die Das Formular wird gesendet (request.POST ist nicht leer). In der Praxis verarbeiten wir die Formulareingabe, auch wenn für dieses Feld keine Formulareingabe vorhanden ist.

Wenn keine Formulareingabe vorhanden ist, versuchen Sie es mit der folgenden Reihenfolge:
def change_username(request):
 user = request.current_user
 form = ChangeUsernameForm(request.POST, user, username=&#39;silly&#39;)
 if request.method == &#39;POST&#39; and form.validate():
  user.username = form.username.data
  user.save()
  return redirect(&#39;change_username&#39;)
 return render_response(&#39;change_username.html&#39;, form=form)

Überprüfen Sie, ob der Benutzer ein Attribut mit dem Namen Benutzername hat.


Überprüfen Sie, ob a Der Schlüsselwortparameter „Benutzername“ wird bereitgestellt.
  • Wenn alles andere fehlschlägt, verwenden Sie schließlich den Standardwert der Domain, sofern verfügbar.
  • Validator
  • WTForms中的验证器(Validators)为域(field)提供一套验证器, 当包含域的表单进行验证时运行. 你提供验证器可通过域构造函数的第二个参数validators:

    class ChangeEmailForm(Form):
     email = StringField(&#39;Email&#39;, [validators.Length(min=6, max=120), validators.Email()])

    你可以为一个域提供任意数量的验证器. 通常, 你会想要提供一个定制的错误消息:

    class ChangeEmailForm(Form):
     email = StringField(&#39;Email&#39;, [
      validators.Length(min=6, message=_(u&#39;Little short for an email address?&#39;)),
      validators.Email(message=_(u&#39;That\&#39;s not a valid email address.&#39;))
     ])

    这通常更好地提供你自己的消息, 作为必要的默认消息是通用的. 这也是提供本地化错误消息的方法.

    对于内置的验证器的列表, 查阅 Validators.

    渲染域
    渲染域和强制它为字符串一样简单:

    >>> from wtforms import Form, StringField
    >>> class SimpleForm(Form):
    ... content = StringField(&#39;content&#39;)
    ...
    >>> form = SimpleForm(content=&#39;foobar&#39;)
    >>> str(form.content)
    &#39;<input id="content" name="content" type="text" value="foobar" />&#39;
    >>> unicode(form.content)
    u&#39;<input id="content" name="content" type="text" value="foobar" />&#39;

    然而, 渲染域的真正力量来自于它的 __call__() 方法. 调用(calling)域, 你可以提供关键词参数, 它们会在输出中作为HTML属性注入.

    >>> form.content(style="width: 200px;", class_="bar")
    u&#39;<input class="bar" id="content" name="content" style="width: 200px;" type="text" value="foobar" />&#39;

    现在, 让我们应用这个力量在 Jinja 模板中渲染表单. 首先, 我们的表单:

    class LoginForm(Form):
     username = StringField(&#39;Username&#39;)
     password = PasswordField(&#39;Password&#39;)
    
    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(&#39;Must be 42&#39;)
    
    class FourtyTwoForm(Form):
     num = IntegerField(&#39;Number&#39;, [is_42])

    或者通过提供一个在表单内的特定域(in-form field-specific)的验证器:

    class FourtyTwoForm(Form):
     num = IntegerField(&#39;Number&#39;)
    
     def validate_num(form, field):
      if field.data != 42:
       raise ValidationError(u&#39;Must be 42&#39;)

    编写WTForm扩展示例

    class TagListField(Field):
     widget = TextInput()
    
     def _value(self):
      if self.data:
       return u&#39;, &#39;.join(self.data)
      else:
       return u&#39;&#39;
    
     def process_formdata(self, valuelist):
      if valuelist:
       self.data = [x.strip() for x in valuelist[0].split(&#39;,&#39;)]
      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&#39;&#39;
       for obj in self.data:
        r += self.obj_to_str(obj)
       return u&#39;&#39;
      else:
       return u&#39;&#39;
    
     def process_formdata(self, valuelist):
      print &#39;process_formdata..&#39;
      print valuelist
      if valuelist:
       tags = self._remove_duplicates([x.strip() for x in valuelist[0].split(&#39;,&#39;)])
       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&#39;&#39;
    
    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&#39;&#39;
       for obj in self.data:
        r += self.obj_to_str(obj)
       return u&#39;&#39;
      else:
       return u&#39;&#39;
     
     def process_formdata(self, valuelist):
      print &#39;process_formdata..&#39;
      print valuelist
      if valuelist:
       tags = self._remove_duplicates([x.strip() for x in valuelist[0].split(&#39;,&#39;)])
       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&#39;&#39;

    主要就是在process_formdata这一步处理表单的数据,将字符串转换为需要的数据。最终就可以在forms.py中这样定义表单了:

    ...
    class ArticleForm(Form):
     """编辑文章表单"""
    
     title = StringField(u&#39;标题&#39;, validators=[Required()])
     category = QuerySelectField(u&#39;分类&#39;, query_factory=get_category_factory([&#39;id&#39;, &#39;name&#39;]), get_label=&#39;name&#39;)
     tags = TagListField(u&#39;标签&#39;, validators=[Required()])
     content = PageDownField(u&#39;正文&#39;, validators=[Required()])
     submit = SubmitField(u&#39;发布&#39;)
    ...
    
    ...
    class ArticleForm(Form):
     """编辑文章表单"""
     
     title = StringField(u&#39;标题&#39;, validators=[Required()])
     category = QuerySelectField(u&#39;分类&#39;, query_factory=get_category_factory([&#39;id&#39;, &#39;name&#39;]), get_label=&#39;name&#39;)
     tags = TagListField(u&#39;标签&#39;, validators=[Required()])
     content = PageDownField(u&#39;正文&#39;, validators=[Required()])
     submit = SubmitField(u&#39;发布&#39;)
    ...
    在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(&#39;dashboard/edit.html&#39;, 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(&#39;dashboard/edit.html&#39;, form=form)

    代码是不是很简洁了?^_^。。。

    当然了写一个完整的WTForms扩展还是很麻烦的。这里只是刚刚入门。可以看官方扩展QuerySelectField的源码。。。
    效果:

    Verwendung des WTForms-Formularframeworks in Pythons Flask

    更多Verwendung des WTForms-Formularframeworks in Pythons Flask相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn