2

Hier ist mein ndb ModellValidator für wiederholten ndb.StructuredProperty nicht abzufeuern

from google.appengine.ext import ndb 
from mainsite.rainbow.models.CFCSocialUser import CFCSocialUser 


class CFCSocialGroup(ndb.Model): 

    def remove_duplicate(self, value): 
     raise Exception("Duplicate user detected") 

    name = ndb.StringProperty(required=True) 
    created_on = ndb.DateTimeProperty(auto_now_add=True) 
    updated_on = ndb.DateTimeProperty(auto_now=True) 
    created_by = ndb.StructuredProperty(CFCSocialUser) 
    members = ndb.StructuredProperty(CFCSocialUser, repeated=True, validator=remove_duplicate) 

    @staticmethod 
    def create_group(name): 
     """Create a new group""" 
     group = CFCSocialGroup(name=name) 
     return group 

    def add_member(self, social_user): 
     """Add a member to the local group""" 
     self.members.append(social_user) 

Ich versuche, um sicherzustellen, dass ich den gleichen Benutzer nicht zu einer bestimmten Gruppe hinzuzufügen. Also versuche ich den Wert der Member-Eigenschaft (StructuredProperty) zu validieren.

Meine Tests ist

from unittest import TestCase 
from mainsite.rainbow.models.CFCSocialGroup import CFCSocialGroup 
from tests.test_CFCSocialUser import create_user 
from tests.cfcsocialtests.testbase import CFCTestBase_NDB 
from nose.tools import * 
from nose.plugins.attrib import attr 


class TestCFCSocialGroup(CFCTestBase_NDB): 
    @attr("CRUD") 
    @raises(Exception) 
    def test_duplicate_addition(self): 
     """Test to detect duplicate users in groups""" 
     user1 = create_user() 
     user2 = create_user() 
     group = CFCSocialGroup.create_group('Group1') 
     group.add_member(user1) 
     group.add_member(user2) 

Der Test eine Ausnahme erhöhen ausfällt. Hier

ist die Debug-Code

FAILED (errors=1) 
MacBook-Pro:tests vinay$ nosetests -v test_CFCSocialGroup.py 
Test to detect duplicate users in groups ... FAIL 

====================================================================== 
FAIL: Test to detect duplicate users in groups 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/Library/Python/2.7/site-packages/nose/tools/nontrivial.py", line 67, in newfunc 
    raise AssertionError(message) 
AssertionError: test_duplicate_addition() did not raise Exception 
-------------------- >> begin captured logging << -------------------- 
root: DEBUG: Using threading.local 
root: WARNING: No ssl package found. urlfetch will not be able to validate SSL certificates. 
root: DEBUG: all_pending: add <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator put(context.py:787) 
root: DEBUG: all_pending: add <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); pending> 
root: DEBUG: AutoBatcher(_memcache_set_tasklet): creating new queue for ('set', 32, '', None) 
root: DEBUG: initial generator put(context.py:787) yielded <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); pending> 
root: DEBUG: <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787) suspended generator put(context.py:810); pending> is now blocked waiting for <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); pending> 
root: DEBUG: idler: _on_idle 
root: DEBUG: AutoBatcher(_memcache_set_tasklet): 1 items 
root: DEBUG: all_pending: add <Future 10d0ec250 created by run_queue(context.py:185) for tasklet _memcache_set_tasklet(context.py:1111); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator _memcache_set_tasklet(context.py:1111) 
root: DEBUG: initial generator _memcache_set_tasklet(context.py:1111) yielded <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x10d0ec490> 
root: DEBUG: idler: _on_idle 
root: DEBUG: idler _on_idle removed 
root: DEBUG: rpc: memcache.Set 
root: DEBUG: Sending {'NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw': 1} to suspended generator _memcache_set_tasklet(context.py:1122) 
root: DEBUG: all_pending: success: remove <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); result True> 
root: DEBUG: suspended generator _memcache_set_tasklet(context.py:1122) returned None 
root: DEBUG: all_pending: success: remove <Future 10d0ec250 created by run_queue(context.py:185) for tasklet _memcache_set_tasklet(context.py:1111); result None> 
root: DEBUG: nowevent: _on_future_completion 
root: DEBUG: <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> is no longer blocked waiting for <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); result True> 
root: DEBUG: Sending True to suspended generator put(context.py:810) 
root: DEBUG: all_pending: add <Future 10d0ec510 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); pending> 
root: DEBUG: AutoBatcher(_put_tasklet): creating new queue for None 
root: DEBUG: suspended generator put(context.py:810) yielded <Future 10d0ec510 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); pending> 
root: DEBUG: <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787) suspended generator put(context.py:824); pending> is now blocked waiting for <Future 10d0ec510 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); pending> 
root: DEBUG: nowevent: _finished_callback 
root: DEBUG: idler: _on_idle 
root: DEBUG: AutoBatcher(_put_tasklet): 1 items 
root: DEBUG: all_pending: add <Future 10d0ec610 created by run_queue(context.py:185) for tasklet _put_tasklet(context.py:348); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator _put_tasklet(context.py:348) 
root: DEBUG: initial generator _put_tasklet(context.py:348) yielded <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x10d0ec890> 
root: DEBUG: idler: _on_idle 
root: DEBUG: idler _on_idle removed 
root: DEBUG: rpc: datastore_v3.Put 
root: DEBUG: Sending [Key('CFCSocialUser', 'Vinay Joseph')] to suspended generator _put_tasklet(context.py:358) 
root: DEBUG: all_pending: success: remove <Future 10d0ec510 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); result Key('CFCSocialUser', 'Vinay Joseph')> 
root: DEBUG: suspended generator _put_tasklet(context.py:358) returned None 
root: DEBUG: all_pending: success: remove <Future 10d0ec610 created by run_queue(context.py:185) for tasklet _put_tasklet(context.py:348); result None> 
root: DEBUG: nowevent: _on_future_completion 
root: DEBUG: <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> is no longer blocked waiting for <Future 10d0ec510 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); result Key('CFCSocialUser', 'Vinay Joseph')> 
root: DEBUG: Sending Key('CFCSocialUser', 'Vinay Joseph') to suspended generator put(context.py:824) 
root: DEBUG: all_pending: add <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); pending> 
root: DEBUG: AutoBatcher(_memcache_del_tasklet): creating new queue for (0, '', None) 
root: DEBUG: suspended generator put(context.py:824) yielded <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); pending> 
root: DEBUG: <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787) suspended generator put(context.py:833); pending> is now blocked waiting for <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); pending> 
root: DEBUG: nowevent: _finished_callback 
root: DEBUG: idler: _on_idle 
root: DEBUG: AutoBatcher(_memcache_del_tasklet): 1 items 
root: DEBUG: all_pending: add <Future 10d0ec850 created by run_queue(context.py:185) for tasklet _memcache_del_tasklet(context.py:1130); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator _memcache_del_tasklet(context.py:1130) 
root: DEBUG: initial generator _memcache_del_tasklet(context.py:1130) yielded <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x10d0ec210> 
root: DEBUG: idler: _on_idle 
root: DEBUG: idler _on_idle removed 
root: DEBUG: rpc: memcache.Delete 
root: DEBUG: Sending [2] to suspended generator _memcache_del_tasklet(context.py:1141) 
root: DEBUG: all_pending: success: remove <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); result 2> 
root: DEBUG: suspended generator _memcache_del_tasklet(context.py:1141) returned None 
root: DEBUG: all_pending: success: remove <Future 10d0ec850 created by run_queue(context.py:185) for tasklet _memcache_del_tasklet(context.py:1130); result None> 
root: DEBUG: nowevent: _on_future_completion 
root: DEBUG: <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> is no longer blocked waiting for <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); result 2> 
root: DEBUG: Sending 2 to suspended generator put(context.py:833) 
root: DEBUG: suspended generator put(context.py:833) returned Key('CFCSocialUser', 'Vinay Joseph') 
root: DEBUG: all_pending: success: remove <Future 10d0d2e90 created by _put_async(model.py:3467) for tasklet put(context.py:787); result Key('CFCSocialUser', 'Vinay Joseph')> 
root: DEBUG: all_pending: add <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> 
root: DEBUG: nowevent: _finished_callback 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator put(context.py:787) 
root: DEBUG: all_pending: add <Future 10d0ec990 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); pending> 
root: DEBUG: AutoBatcher(_memcache_set_tasklet): creating new queue for ('set', 32, '', None) 
root: DEBUG: initial generator put(context.py:787) yielded <Future 10d0ec990 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); pending> 
root: DEBUG: <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787) suspended generator put(context.py:810); pending> is now blocked waiting for <Future 10d0ec990 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); pending> 
root: DEBUG: idler: _on_idle 
root: DEBUG: AutoBatcher(_memcache_set_tasklet): 1 items 
root: DEBUG: all_pending: add <Future 10d0ecd10 created by run_queue(context.py:185) for tasklet _memcache_set_tasklet(context.py:1111); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator _memcache_set_tasklet(context.py:1111) 
root: DEBUG: initial generator _memcache_set_tasklet(context.py:1111) yielded <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x10d0ecf50> 
root: DEBUG: idler: _on_idle 
root: DEBUG: idler _on_idle removed 
root: DEBUG: rpc: memcache.Set 
root: DEBUG: Sending {'NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw': 1} to suspended generator _memcache_set_tasklet(context.py:1122) 
root: DEBUG: all_pending: success: remove <Future 10d0ec990 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); result True> 
root: DEBUG: suspended generator _memcache_set_tasklet(context.py:1122) returned None 
root: DEBUG: all_pending: success: remove <Future 10d0ecd10 created by run_queue(context.py:185) for tasklet _memcache_set_tasklet(context.py:1111); result None> 
root: DEBUG: nowevent: _on_future_completion 
root: DEBUG: <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> is no longer blocked waiting for <Future 10d0ec990 created by add(context.py:211) for AutoBatcher(_memcache_set_tasklet).add(('NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw', 0), ('set', 32, '', None)); result True> 
root: DEBUG: Sending True to suspended generator put(context.py:810) 
root: DEBUG: all_pending: add <Future 10d0ecfd0 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); pending> 
root: DEBUG: AutoBatcher(_put_tasklet): creating new queue for None 
root: DEBUG: suspended generator put(context.py:810) yielded <Future 10d0ecfd0 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); pending> 
root: DEBUG: <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787) suspended generator put(context.py:824); pending> is now blocked waiting for <Future 10d0ecfd0 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); pending> 
root: DEBUG: nowevent: _finished_callback 
root: DEBUG: idler: _on_idle 
root: DEBUG: AutoBatcher(_put_tasklet): 1 items 
root: DEBUG: all_pending: add <Future 10d132110 created by run_queue(context.py:185) for tasklet _put_tasklet(context.py:348); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator _put_tasklet(context.py:348) 
root: DEBUG: initial generator _put_tasklet(context.py:348) yielded <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x10d132450> 
root: DEBUG: idler: _on_idle 
root: DEBUG: idler _on_idle removed 
root: DEBUG: rpc: datastore_v3.Put 
root: DEBUG: Sending [Key('CFCSocialUser', 'Vinay Joseph')] to suspended generator _put_tasklet(context.py:358) 
root: DEBUG: all_pending: success: remove <Future 10d0ecfd0 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); result Key('CFCSocialUser', 'Vinay Joseph')> 
root: DEBUG: suspended generator _put_tasklet(context.py:358) returned None 
root: DEBUG: all_pending: success: remove <Future 10d132110 created by run_queue(context.py:185) for tasklet _put_tasklet(context.py:348); result None> 
root: DEBUG: nowevent: _on_future_completion 
root: DEBUG: <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> is no longer blocked waiting for <Future 10d0ecfd0 created by add(context.py:211) for AutoBatcher(_put_tasklet).add(CFCSocialUser(key=Key('CFCSocialUser', 'Vinay Joseph'), date_of_birth=datetime.date(1900, 3, 2), email='[email protected]', username='Vinay Joseph'), None); result Key('CFCSocialUser', 'Vinay Joseph')> 
root: DEBUG: Sending Key('CFCSocialUser', 'Vinay Joseph') to suspended generator put(context.py:824) 
root: DEBUG: all_pending: add <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); pending> 
root: DEBUG: AutoBatcher(_memcache_del_tasklet): creating new queue for (0, '', None) 
root: DEBUG: suspended generator put(context.py:824) yielded <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); pending> 
root: DEBUG: <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787) suspended generator put(context.py:833); pending> is now blocked waiting for <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); pending> 
root: DEBUG: nowevent: _finished_callback 
root: DEBUG: idler: _on_idle 
root: DEBUG: AutoBatcher(_memcache_del_tasklet): 1 items 
root: DEBUG: all_pending: add <Future 10d0eca10 created by run_queue(context.py:185) for tasklet _memcache_del_tasklet(context.py:1130); pending> 
root: DEBUG: nowevent: _help_tasklet_along 
root: DEBUG: Sending None to initial generator _memcache_del_tasklet(context.py:1130) 
root: DEBUG: initial generator _memcache_del_tasklet(context.py:1130) yielded <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x10d0ece10> 
root: DEBUG: idler: _on_idle 
root: DEBUG: idler _on_idle removed 
root: DEBUG: rpc: memcache.Delete 
root: DEBUG: Sending [2] to suspended generator _memcache_del_tasklet(context.py:1141) 
root: DEBUG: all_pending: success: remove <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); result 2> 
root: DEBUG: suspended generator _memcache_del_tasklet(context.py:1141) returned None 
root: DEBUG: all_pending: success: remove <Future 10d0eca10 created by run_queue(context.py:185) for tasklet _memcache_del_tasklet(context.py:1130); result None> 
root: DEBUG: nowevent: _on_future_completion 
root: DEBUG: <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787); pending> is no longer blocked waiting for <Future 10b0ae1d0 created by add(context.py:211) for AutoBatcher(_memcache_del_tasklet).add(NDB9:agx0ZXN0YmVkLXRlc3RyHwsSDUNGQ1NvY2lhbFVzZXIiDFZpbmF5IEpvc2VwaAw, (0, '', None)); result 2> 
root: DEBUG: Sending 2 to suspended generator put(context.py:833) 
root: DEBUG: suspended generator put(context.py:833) returned Key('CFCSocialUser', 'Vinay Joseph') 
root: DEBUG: all_pending: success: remove <Future 10d0ec150 created by _put_async(model.py:3467) for tasklet put(context.py:787); result Key('CFCSocialUser', 'Vinay Joseph')> 
--------------------- >> end captured logging << --------------------- 

---------------------------------------------------------------------- 
Ran 1 test in 0.035s 

FAILED (failures=1) 

Antwort

1

diesen Code in models.py Gegeben:

class Member(ndb.Model): 

    name = ndb.StringProperty() 


def remove_duplicates(prop, value): 
    raise Exception('Duplicate') 


class Club1(ndb.Model): 

    members = ndb.StructuredProperty(Member, repeated=True, validator=remove_duplicates) 

Ich kann erstellen Member Instanz

> m = Member(name='Alice')

Schaffung einer Club1 Instanz mit diese Member Instanz löst die Validierung:

> c1 = models.Club1(members=[m]) 
Traceback (most recent call last): 
    <snip> 
    File "models.py", line 60, in remove_duplicates 
    raise Exception('Duplicate') 
Exception: Duplicate 

jedoch eine leere Club1 Instanz erstellen und dann ein Anfügen Member nicht: dies effektiv Ihr Testfall ist.

> c1 = models.Club1() 
> c1.members.append(m) 
> c1.put() 
Key('Club1', 6682831673622528) 

Wir können ndb.StructuredProperty Unterklasse und die Validierung in der Unterklasse setzen:

class MembersStructuredProperty(ndb.StructuredProperty): 

    def _validate(self, value): 
     raise Exception('Duplicate') 


class Club2(ndb.Model): 

    members = MembersStructuredProperty(Member, repeated=True) 

eine Club2 Instanz mit einem Member Erstellen löst die Validierung nach wie vor:

> c2 = models.Club2(members=[m]) 
Traceback (most recent call last): 
    <snip> 
    File "models.py", line 56, in _validate 
    raise Exception('Duplicate') 
Exception: Duplicate 

Und jetzt so tut Anhängen eines Member und dann versuchen, in den Datenspeicher zu schreiben:

> c2 = models.Club2() 
> c2.members.append(m) 
> c2.put() 
Traceback (most recent call last): 
    <snip> 
    File "models.py", line 56, in _validate 
    raise Exception('Duplicate') 
Exception: Duplicate 

So Unterklasse ndb.StructuredProperty sollte Ihren Test erlauben, zu bestehen.

Ich weiß nicht, warum die Property-Validierung von ndb sich so verhält, es ist wohl ein Bug oder zumindest undokumentiertes Verhalten.

EDIT:

Wie @DanCornilescu in den Kommentaren beobachtet, dies ist ein known bug im SDK

+0

Vielleicht zu: „Wenn eine wiederholte Eigenschaft aktualisieren, können Sie entweder eine neue Liste zuweisen oder die vorhandene Liste mutieren "(aus https://cloud.google.com/appengine/docs/python/ndb/entity-property-reference#repeated)? –

+0

Ich nehme an, sie wollen, dass wir alles unterklassifizieren. –

+0

Nur aus Neugier, warum haben Sie die Validierungsmethode außerhalb der Klasse verschoben? –