【问题标题】:Query filtering on property with validator setted设置了验证器的属性查询过滤
【发布时间】:2013-04-03 06:41:48
【问题描述】:

我的模型 User 和属性 username 带有验证器,用于验证 username 的可用字符、非空字符和 存在 在数据库中。因此,例如,在处理注册表单时,我不需要任何其他检查 - 我只需将表单的 username 值分配给模型的属性,并在输入 username 时捕获验证错误已经存在...

但是,这行不通。

因为 NDB 也会验证属性的比较参数(请参阅 ndb/model.py 中的 Property._comparison 方法),并且它会在 Query.filter(User.username == [somevalue]) 中进行无休止的请求,最后引发RuntimeError: maximum recursion depth exceeded。 NDB 尝试使用 validate_username 验证 [somevalue] 并一次又一次地转到此查询...

可以将用户名分配给实体的ID并使用User.get_by_id(),但需要username才能更改,所以我需要使用Query.get()

所以这是我的User 模型:

class User(ndb.Model):

  def validate_username(self, value):

    value = str(value).strip()

    # Other useful checks - length, available symbols, etc

    if User.get_user(value):
        raise ValueError('Username already exists')

    return value

  @classmethod
  def get_user(cls, username):

    username = str(username)

    user_q = User.query()
    user_q = user_q.filter(User.username == username) # Here is the problem
    return user_q.get()

  username = ndb.StringProperty(validator=validate_username)

例如:

# Trying to add user, get RuntimeError exception
u = User()
u.username = 'John'

我做错了什么?解决此类问题的最佳方法是什么?

更新到Tim Hoffman: 谢谢。是的,我错过了 prop 参数,但是方法在 selfvalval 参数中收到了 prop - 因此我没有提到这个错误。但是,您错过了关键问题 - 您没有在验证器中使用带有过滤器的查询(User.get_user 方法)。试试这个,有没有感觉函数或者方法验证器是:

def validate_username2(prop, value):

    if User.get_user(value):
        raise Exception('User exists!')

    return value

class User(ndb.Model):

    def validate_username(self, value):

        if User.get_user(value):
            raise Exception('User exists!')

        return value

    @classmethod
    def get_user(self, username):
        user_q = User.query()
        user_q = user_q.filter(User.username == username)
        return user_q.get()

    # Try both please    
    username = ndb.StringProperty(validator=validate_username)
    #username = ndb.StringProperty(validator=validate_username2)

【问题讨论】:

  • 文档说 - 将使用参数 (prop, value) 调用,并且应该返回(可能是强制的)值或引发异常。但是,您的 validate_username 方法仅使用值定义。尝试将其定义为函数而不是带有 args(属性、值)的方法。

标签: python google-app-engine app-engine-ndb


【解决方案1】:

我相信您的问题是由于错误地将您的验证器定义为一种方法并且不接受正确的参数。请参阅下面的快速示例,确实可以使用过滤器。

The db, ndb, users, urlfetch, and memcache modules are imported.
dev~cash-drawer> def vla(prop,val):
...    if val == "X":
...      raise ValueError
...    return val
... 
dev~cash-drawer> 
dev~cash-drawer> 
dev~cash-drawer> class X(ndb.Model):
...    name = ndb.StringProperty(validator=vla)
... 
dev~cash-drawer> y = X()
dev~cash-drawer> y.name = "X"
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 1258, in __set__
    self._set_value(entity, value)
  File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 1004, in _set_value
    value = self._do_validate(value)
  File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 953, in _do_validate
    newvalue = self._validator(self, value)
  File "<console>", line 3, in vla
ValueError
dev~cash-drawer> y.name = "aaa"
dev~cash-drawer> y.put()
Key('X', 5060638606280884224)
dev~cash-drawer> z=X.query().filter(X.name == "aaa")
dev~cash-drawer> list(z)
[X(key=Key('X', 5060638606280884224), name=u'aaa')]

    dev~cash-drawer> z=X.query().filter(X.name == "X")
    Traceback (most recent call last):
        File "<console>", line 1, in <module>
        File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 859, in __eq__
        return self._comparison('=', value)
        File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 847, in _comparison
        value = self._do_validate(value)
         File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 953, in _do_validate
        newvalue = self._validator(self, value)
         File "<console>", line 3, in vla
    ValueError
    dev~cash-drawer> 

【讨论】:

  • 是的,我错过了prop 参数,但是方法在selfval 中收到了prop val 参数-因此我没有提到这个错误。但是,您错过了关键问题 - 您没有在验证器中使用带有过滤器的查询。试试这个,没有感觉函数或方法验证器是:[我已经在主要问题中添加了代码,因为 cmets 打破了行]
  • 同意,但是如果您查看我的示例,我会在具有有效验证器的模型上执行查询。事实上,我只是用我提供的示例运行测试,验证器确实启动了查询。因此,如果我查询未通过验证器的内容,您会看到上述值错误。这确实是有道理的,在您的情况下,您的验证器正在有效地调用自己,因此是递归。我您曾经尝试更新对象,您也会收到错误,因为验证器将再次运行并且会失败。我认为你应该使用工厂方法来创建/检查重复
  • 是的,我理解你的例子,我的工作类似,直到我在验证器中添加 query.filter。我无法理解一件事 - 为什么 NDB 验证方程运算符中的值?如果值故意不正确,不进行查询?
  • 它甚至没有达到验证器在构造过滤器参数时所执行的查询,并且在验证器内部,如果您考虑一下,您实际上正在执行相同的查询因此递归,使用验证器验证查询的输入是有意义的——理论上这样的值(无效的)不应该存在,并且属性定义的相同机制正在用于定义查询。
  • 顺便提一下,蒂姆,有什么方法可以从被调用的验证器中访问user 模型对象?
猜你喜欢
  • 2014-04-18
  • 2017-03-24
  • 2021-06-21
  • 2011-07-24
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 2021-06-20
  • 1970-01-01
相关资源
最近更新 更多