1

我遵循了这个例子,但我对其进行了一些修改以适合我的项目

这就是我所拥有的:

class AgentFormValidation(object):        

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def __call__(self, form, value):
        number = value['identity_information']['number']
        print validateID(number)
        type = value['identity_information']['type']
        q = sqlahelper.get_session().query(Agents.id_number).filter(Agents.id_number == number).first()

        if type == "IDNumber":
            if not validateID(number):
                if q:
                    exc = colander.Invalid(form["identity_information"], "ID Number %s already exists in Database" % number)
                    exc.number = "ID Number already exists " 
                    raise exc
            else:
                exc = colander.Invalid(form["identity_information"], "ID Number %s is not a valid SA ID Number" % number)
                exc.number = "Invalid ID number" 
                raise exc
        elif type == "Passport":
            if q:
                exc = colander.Invalid(form["identity_information"], "Passport number %s already exists in Database" % number)
                exc.number = "Passport number already exists"
                raise exc


def gen_agent_schema_form(self):
        _but = ('create agent',) 
        _title = "Create Agent"
        if not self.context.__is_new__:
            _but = ('update agent',)
            _title = "Agent Details"
        deals = []
        if self.context.ou:
            deals = [(deal.id, str(deal)) for deal in self.context.ou[0].org_deals]

        schema = Agent(validator=AgentFormValidation(self.context, self.request), title=_title).bind(deals=deals)
        form = Form(schema, buttons=_but)
        return schema, form

验证工作得很好。它只是不想突出显示元素。

当我更换:

exc.number = "ID Number already exists"

exc['number'] = "ID Number already exists"  

它确实突出显示,但它突出显示表单上的第一个元素,即first_name,这也是错误的。

我觉得我错过了一些小东西。

更新

所以我玩了一点,当我这样做时:

  exc = colander.Invalid(form, "ID Number %s already exists in Database" % number)
  exc["identity_information"] = "ID Number already exists " 
  raise exc

我在正确的字段上方收到一个警报消息框(不是 js 警报):

我得到的图像

而不是这个,我需要在上面的例子中突出显示该字段。

4

1 回答 1

3

在您的自定义验证器中,您始终将表单作为第一个参数传递给 colander.Invalid()。通过这种方式,您可以将验证消息添加到表单顶部,但不会触发模式节点/元素的突出显示。开始使用处理单个模式节点/元素的简单验证器。

exc = colander.Invalid(form["identity_information"], "ID Number %s already exists in Database" % number)

无论如何,我没有看到对字段间验证的明确要求。您可以使用可用的滤锅验证器或在每个代理模式节点上创建自定义验证器,而不是在代理模式上应用复杂的基于类的验证器,从而使验证器尽可能简单。您当前的验证器实现过于关心太多用例。

我假设您有两个不同的用例 - 注册和其他一些与代理相关的任务。通过发明不同的模式,我可以保持特定于模式节点和用例的验证。ID 的唯一性可能仅在使用添加/创建表单创建内容时很重要,在更新表单中,用户可能无法更改所有这些值,但只能更改一组有限的个人信息。

主要思想是通常应用模式节点验证器来获取模式节点的验证消息。在特殊情况下,应用表单级验证器(代理注册)。

您的用例说明了表单验证的领域。一个根据模式验证表单输入并处理与持久性相关的主题(主键唯一性)的类做得太多了。两者都应该被封装起来分别进化。您的业​​务规则会及时更改,数据库表将始终要求主键的唯一性。

模式

import colander

class AgentRegistration(colander.Schema): 
    """schema for agent registration with email validation

    Note the form validator is invoked only if none of the individual field validators raise an error.
    """

    first_name = colander.SchemaNode(colander.String())
    number = colander.SchemaNode(colander.Integer(), validator=can_register_agent)
    email = colander.SchemaNode(colander.String(), validator=colander.Email())
    verify_email = colander.SchemaNode(colander.String(), validator=colander.Email())

    validator = verify_email_validator


class AgentDeals(colander.Schema):
    "schema for managing agent deals"
    first_name = colander.SchemaNode(colander.String())
    number = colander.SchemaNode(colander.Integer(), validator=validateID)
    email = colander.SchemaNode(colander.String(), validator=colander.Email())  

模式验证器

def agent_unique(node, value):
    "validate uniqueness of value in database table Agents"
    if sqlahelper.get_session().query(Agents.id_number).filter(Agents.id_number == value).first()
         raise Invalid(node,
                  'ID Number %r is already given to another agent. Please change it' % value)

def valid_SA_ID(node, value):
    "validates SA ID Number - just a copy of your requirement calling your custom function"
    if not validateID(value):
         raise Invalid(node,
                  'SA ID Number %r is not valid.' % value)

def can_register_agent(node, value):
    "ensure Agent ID Number is a valid and not already existing in database"
    valid_SA_ID(node, value)
    agent_unique(node, value)  

def verify_email_validator(form, values):
    """schema level validator with access to all values

    validates emails are same
    validation messages are displayed on top of form"""

    if values['email'] != values['verify_email']:
        raise colander.Invalid(form, 'Email values do not match.')

形式

class AgentRegistrationView(object)

    def __init__(self, request):
        """Set some common variables needed for each view.
        """
        self.request = request
        self.context = request.context

    @view_config(route_name='add_agent', permission='admin', renderer='add_agent.mako')
    def add_agent(self):
        """return form to create new agents

        may be we do not need to bind any deals data here"""

        schema = AgentRegistration() 
        form = deform.Form(schema, action=self.request.route_url('add_agent'), buttons=('Add Agent','Cancel'))


class AgentDealsView(object)

    def __init__(self, request):
        """Set some common variables needed for each view.
        """
        self.request = request
        self.context = request.context


    def get_deals(self)
        """return deals to be bound to Agent Schema"""
        if self.context.ou:
            return [(deal.id, str(deal)) for deal in self.context.ou[0].org_deals]


    @view_config(route_name='edit_agent' permission='edit', renderer='edit_agent.mako')
    def edit_agent(self):

        # we bind deals data to form
        schema = AgentDeals().bind(deals=self.get_deals())
        form = deform.Form(schema, action=self.request.route_url('edit_agent'), buttons=('Save','Cancel'))

参考

除此之外,您可能对ColanderAlchemy感兴趣,但它增加了另一层抽象。其实我不推荐给你。

于 2014-01-27T23:29:30.227 回答