Denisigo
Denisigo

Reputation: 640

Query filtering on property with validator setted

I have model User and property username with validator which validates username for available chars, non-empty and existence in DB. So, I don't need any other checks when handling registration form for example - I just assign form's username value to model's property and catching validation error if entered username already exists...

But, it isn't work.

Because NDB validates property's comparison arguments too (see Property._comparison method in ndb/model.py) and it goes to endless reqursion in Query.filter(User.username == [somevalue]) and finally raises RuntimeError: maximum recursion depth exceeded. NDB trying to validate [somevalue] with validate_username and go to this query again and again...

It's possible to assign username to entity's ID and use User.get_by_id(), but it's needed username to be changeable, so I need to use Query.get().

So it is my User model:

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)

For example:

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

What I'm doing wrong? What's the best way to solve such problem?

UPDATE to Tim Hoffman: Thanks. Yes, I've missed prop argument, but method received prop in self and val in val arguments - thus I didn't mention this mistake. But, you've missed the key issue - you don't use query with filter in validator (User.get_user method). Try this, there is no sense function or method validator is:

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)

Upvotes: 0

Views: 898

Answers (1)

Tim Hoffman
Tim Hoffman

Reputation: 12986

I believe you problem is due to incorrectly defining you validator as a method and not accepting the correct arguments. See the quick example below, does work with filters.

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> 

Upvotes: 2

Related Questions