Python의 Flask에서 WTForms 양식 프레임워크 사용

高洛峰
풀어 주다: 2017-03-03 14:17:11
원래의
2130명이 탐색했습니다.

다운로드 및 설치

WTForms를 설치하는 가장 쉬운 방법은 easy_install 및 pip를 사용하는 것입니다.

easy_install WTForms
# or
pip install WTForms
로그인 후 복사

PyPI에서 WTForms를 수동으로 다운로드하고 python setup.py install을 실행할 수 있습니다.

모든 위험을 감수하고 싶은 사람이라면 Git에서 최신 버전을 실행하면 됩니다. 최신 변경 세트 패키지 버전을 가져오거나 프로젝트 홈페이지로 이동하여 코드 저장소를 복제할 수 있습니다.

주요 개념
Forms 클래스는 WTForms의 핵심 컨테이너입니다. 필드(필드) 모음 및 필드는 양식의 사전 양식 또는 속성 양식을 통해 액세스할 수 있습니다.
필드(필드)는 가장 무거운 작업을 수행하며 각 필드(필드)는 데이터 유형을 나타내며 필드 작업은 양식을 강제합니다. 예를 들어, InputRequired 및 StringField는 포함된 데이터 외에도 레이블, 설명 및 유효성 검사 오류 목록과 같은 많은 유용한 속성을 나타냅니다. 🎜> 각 필드에는 위젯(부분) 인스턴스가 있습니다. 위젯의 작업은 필드의 HTML 표현을 렌더링하는 것입니다. 각 필드는 위젯 인스턴스를 지정할 수 있지만 기본적으로 일부 필드는 간단하고 편리합니다. 기본 위젯(위젯)인 TextAreaField는 TextArea의
StringField
이므로 유효성 검사 규칙을 지정하기 위해 필드에는 유효성 검사기 목록이 포함됩니다.

시작
자 본론으로 들어가서 첫 번째 A 양식을 정의하세요::

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()])
로그인 후 복사

양식을 만들 때 필드를 정의하는 방법은 ORM이 정의하는 수와 비슷합니다. 해당 열(열): 클래스 변수, 즉 필드 인스턴스를 정의하여

양식은 일반 Python 클래스이므로 원하는 대로 쉽게 확장할 수 있습니다.:

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)])
로그인 후 복사

하위 클래스화를 통해 AdminProfileForm 클래스는 이미 정의된 ProfileForm 클래스의 모든 필드를 가져옵니다. 이를 통해 위의 예에서처럼 관리자를 추가하여 여러 양식 간에 공통 필드 하위 집합을 쉽게 공유할 수 있습니다. -ProfileForm에 대한 필드만.

양식 사용
양식을 사용하는 것은 인스턴스화만큼 쉽습니다. 이전에 정의된 RegistrationForm을 사용하는 다음 Django 스타일 보기 기능을 고려하세요. class::

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)
로그인 후 복사

먼저 양식을 인스턴스화하여 request.POST에서 사용할 수 있는 일부 데이터를 제공합니다. 그런 다음 요청이 POST 방식이라면 양식을 검증하고 사용자가 규칙을 준수하는지 확인합니다. 성공하면 새 사용자 모델을 생성하고 검증된 양식에서 데이터를 할당한 후 마지막으로 저장합니다. >

기존 개체 편집

이전 등록 예제에서는 입력을 받고 새 항목에 대한 유효성을 검사하는 방법을 보여줬습니다. 하지만 기존 개체를 편집하려면 어떻게 해야 할까요? 매우 간단합니다::

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)
로그인 후 복사

여기에서는 request.POST와 사용자 개체를 모두 제공하여 양식을 인스턴스화합니다. 이렇게 하면 Any 형식이 됩니다. 제출된 데이터에 없는 데이터는 사용자 개체에서 검색됩니다.

또한 양식의 populate_obj 메서드를 사용하여 검증된 양식의 콘텐츠로 사용자 개체를 다시 채웁니다. 필드 이름이 데이터를 제공하는 개체의 이름과 일치할 때 사용됩니다. 일반적으로 값을 수동으로 할당하고 싶지만 이 간단한 예에서는 CURD 및 admin과 함께 사용할 수도 있습니다. Forms.

콘솔에서 탐색

WTForms Forms는 매우 간단한 컨테이너 개체로, 아마도 양식에서 어떤 것이 적합한지 알아내는 가장 쉬운 방법은 재생을 제어하는 ​​것입니다. 타이중에서 양식 사용:

>>> 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;]}
로그인 후 복사

우리가 볼 수 있는 것은 양식을 인스턴스화할 때 양식에 모든 필드의 인스턴스가 포함되며 해당 필드는 다음을 통해 액세스할 수 있다는 것입니다. 또는 속성 양식. 이러한 필드에는 포함된 양식과 마찬가지로 자체 속성이 있습니다.

양식의 유효성을 검사하면 하나 이상의 유효성 검사 규칙이 충족되지 않음을 의미하는 false가 반환됩니다. 모든 오류에 대한 요약을 제공합니다.

>>> form2 = UsernameForm(username=u&#39;Robert&#39;)
>>> form2.data
{&#39;username&#39;: u&#39;Robert&#39;}
>>> form2.validate()
True
로그인 후 복사

이번에는 UserForm을 인스턴스화할 때 사용자 이름에 대한 새 값을 전달합니다. form.

양식이 데이터를 가져오는 방법

처음 두 매개변수(formdata 및 obj)를 사용하여 데이터를 제공하는 것 외에도 키워드 매개변수를 전달하여 양식을 채울 수 있습니다. formdata, obj, prefix와 같은 일부 매개변수 이름은 예약되어 있습니다.
formdata는 obj보다 우선순위가 높고 obj는 키워드 매개변수보다 우선순위가 높습니다. 예:

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)
로그인 후 복사

실제로 3가지 방법을 모두 함께 사용하는 경우는 거의 없지만 다음은 WTForms가 사용자 이름 필드를 조회하는 방법에 대한 예입니다.

양식이 제출된 경우(request.POST 비어 있지 않음), 양식 입력을 처리합니다. 실제로 이 필드에 양식 입력이 없더라도 어떤 종류의 양식 입력이 있으면 양식 입력을 처리합니다.

양식 입력이 없으면, 그런 다음 다음 순서가 시도됩니다.


사용자에게 username이라는 속성이 있는지 확인하세요.
  • username이라는 키워드 매개변수가 제공되는지 확인하세요.
  • 마지막으로 모두 실패하면 도메인의 기본값을 사용합니다.
  • 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的源码。。。
效果:

Python의 Flask에서 WTForms 양식 프레임워크 사용

更多Python의 Flask에서 WTForms 양식 프레임워크 사용相关文章请关注PHP中文网!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿