Показаны сообщения с ярлыком WTForm. Показать все сообщения
Показаны сообщения с ярлыком WTForm. Показать все сообщения

27 ноября 2011

WTForm валидация

Продолжение статьи готовим Pylons + WTForms . Рассмотрим как создать свой класс для валидации. Здесь можно найти стандартные валидаторы http://wtforms.simplecodes.com/docs/0.6/validators.html

Добавим файл equipments.py в папку validators
|~forms/
| |~mycontrollers/
| | |-__init__.py
| | `-equipments.py
| |~validators/
| | |-__init__.py
| | `-equipments.py
| `-__init__.py
Напишем валидатор IP адресов, вобще в WTForm есть класс wtforms.validators.IPAddress, но он работает только с IPv4 адресами.
import ipaddr
from wtforms.validators import ValidationError

class IPNetwork(object):
    """
    Validates an IP(v4 and v6) address.

    :param message:
        Error message to raise in case of a validation error.
    """
    def __init__(self, message=None):
        self.message = message

    def __call__(self, form, field):
        data = field.data
        # Use PEP3144 for validation
        if not ipaddr.IPNetwork(data) and data:
            self.message = field.gettext(u'Invalid IP address.')
            raise ValidationError(self.message)
Сначала получаем значение поля, а потом при помощи модуля ipaddr проверяем. Если проверка не проходит то генерируем исключение ValidationError. Теперь напишем похожий валидатор для MAC адреса.
...
from wtforms.validators import Regexp
...
class MACAddress(Regexp):
    """
    Validates an MAC address.

    :param message:
        Error message to raise in case of a validation error.
    """
    def __init__(self, message=None):
        super(MACAddress, self).__init__(r'^([a-f\d]{1,2}\:){5}[a-f\d]{1,2}$',\
                                        message=message)

    def __call__(self, form, field):
        if self.message is None:
            self.message = field.gettext(u'Invalid MAC address.')

        if field.data:
            super(MACAddress, self).__call__(form, field)
Здесь мы использовали встроенный класс Regexp. В файле формы это используется так
...
from myapp.forms.validators.mycontroller import IPNetwork, MACAddress
...
class EditForm(Form):

    ip = TextField('IP address', [IPNetwork()])
    mac = TextField('MAC address', [MACAddress()])
WTForm валидация

22 ноября 2011

готовим Pylons + WTForms

WTForm простая, но довольно удобная библиотека для создания форм. И еще WTForm очень похожа на формы в Django - одно из немногово что в джанге сделано хорошо. Посмотрим как это работает с Pylons. Для удобства будем хранить формы отдельно
|+config/
|+controllers/
|~forms/
| |~mycontroller/
| | |-__init__.py
| | `-equipments.py
| |+validators/
| `-__init__.py
Создаем форму для редактирования оборудования equipments.py
from myapp.model.meta import Session as s
from myapp.model.mymodel import EquipmentType
from wtforms import Form, TextField, validators
from wtforms.ext.sqlalchemy.fields import QuerySelectField

# Выбор всех разновидностей оборудования для списка type в форме
def all_equipment_types():
    return s.query(EquipmentType).all()

class EditForm(Form):
    ip = TextField('ip address')
    netmask = TextField('network mask')
    mac = TextField('mac address')
    type = QuerySelectField('type of equipment',
                            query_factory=all_equipment_types)
QuerySelectField это поле из расширения WTForm для SQLAlchemy, которое позволяет создавать списки на основе выборок. Также есть расширения для Django и GAE. Инициализируем нашу форму в контроллере и передадим шаблону
from myapp.forms.mycontroller.equipments import EditForm

class EquipmentsController(BaseController):
    ...
    def edit(self, id, format='html'):
        """GET /myapp/equipments/id/edit: Form to edit an existing item"""
        # url('myapp_edit_equipment', id=ID)
        c.equipment = s.query(Equipment).filter(id==id)
        c.form = EditForm(ip=c.equipment.ip, mac=c.equipment.mac,
                        type=c.equipment.equipmenttype,
                        netmask=c.equipment.netmask)

        return render('/myapp/equipments/edit.html')
После передачи одноименных полям параметров в форму, значения этих параметров будут по умолчанию выведены в форме. Напишем шаблон на Jinja
{% extends "base.html" %}

{% block content %}
<form action="{{ url(controller='myapp/equipments', action='update', id=c.equipment.id) }}" method="POST">
<table class="simpletable">
<caption>Edit equipment of {{ c.equipment.equipmenttype }}
    ({{ c.equipment.ip }})</caption>
<tbody>
{% for field in c.form.data %}
<tr><td>{{ c.form[field].label }}</td>
    <td>{{ c.form[field] }}</td>
</tr>
{% endfor %}
<tr><td colspan="5">
<button><img src="/img/common/save.png" />Save</button>
<input type="hidden" name="_method" value="PUT" />
</td></tr>
</tbody>
</table>
</form>
{% endblock %}
В примере поля выводятся в цикле из списка form.data. Я использую REST контроллеры поэтому обновление происходит в методе update
from myapp.forms.mycontroller.equipments import EditForm

class EquipmentsController(BaseController):
    ...
    def update(self, id):
        """PUT /myapp/equipments/id: Update an existing item"""
        # Forms posted to this method should contain a hidden field:
        #    
        # Or using helpers:
        #    h.form(url('myapp_equipment', id=ID),
        #           method='put')
        # url('myapp_equipment', id=ID)
        equipment = s.query(Equipment).filter(id=id)
        if request.POST['ip']:
            equipment.ip = request.POST['ip']
        if request.POST['netmask']:
            equipment.netmask = request.POST['netmask']
        if request.POST['mac']:
            equipment.mac = request.POST['mac']
        type = EquipmentType.by_id(request.POST['type'])
        equipment.equipmenttype = type
        s.commit()

        came_from = str(request.environ.get('HTTP_REFERER', '')) or url('/')

        redirect(came_from)
Последние две строки возвращают пользователя обратно на страницу редактирования. Выглядит это как-то так:
Пример WTForm и Pylons