Reputation: 61
I've been getting this error using NDB on google app engine and python : Cannot repeat StructuredProperty None that has repeated properties of its own.
It's "None" because I get the error in the process of creating a new empty character so I can fill in properties after creation.
I'm guessing that it doesn't like that I want my entity "Character" to have a property "weaponList" that is repeated and structured containing another entity "Weapon" which also has a repeated, structured property containing and entity "curse".
Being in my 5th or so week at Udacity's cs253 with Steve Huffman, it seems I know just enough to be dangerous, so I thought I'd confirm that this is the issue, and hopefully get a better solution that achieves my goals.
Basically, I'm making a Dungeons and Dragons-ish character management system that has some GM/player realtime messaging and character sheet updating features that I thought would be of use to the growing number of google+ hangout RPG players out there. I have a list of weapons, items, curses, and magical effects all as separate entities so I can mix and match them at will.
Weapons and Items can have any number of curses or good magical effects. Characters can have any number of weapons, yadda yadda. At first, I thought storing a list of entity keys would be the way to go. Then I thought I'd have to individually get each weapon from the database, and each curse for each weapon... it seemed like an inefficient way to do it.
Plus, players and GMs love to customize things and this doesn't lend itself to that. So, it hit me, I can have a list of standard stuff in one table, and, when you "add" it to a character, it gets copied specifically to that character in a repeated structured property, and you can customize the hell out of it without affecting anyone else, or without me doing any extra work.
But it's not working, so I'm thinking it's not allowed because of the very possible, never ending lists in lists in lists that could result. What's the better way?
Upvotes: 3
Views: 2497
Reputation: 5299
The documentation for structured properties clearly states:
Although a
StructuredProperty
can be repeated and aStructuredProperty
can contain anotherStructuredProperty
, beware: if one structred property contains another, only one of them can be repeated. A work-around is to useLocalStructuredProperty
, which does not have this constraint (but does not allow queries on its property values).
Upvotes: 6
Reputation: 61
To answer my own question for my fellow newbs. Short answer - use LocalStructureProperty. My theory is that creating indexes (which Google does automagically) on nested lists would be, well, silly. The documentation doesn't really specify this, at least from my readings, or it might and I just don't know enough to "get it". However, LocalStructuredProperties don't create indexes, and my setup works fine now. If you need local copies (not references) of database objects in list format - which also have local lists as properties - localStructuredProperty might be what you need.
I'd like to go over this in a bit more detail, and a couple of options I tried using the standard datastore, and NDB json property datatype
before my model classes looked something like this
class Character(ndb.Model):
name = stringProperty()
weaponList = StructuredProperty(WeaponModel.Weapon, repeated=True)
class WeaponModel(ndb.Model):
name = stringProperty()
curseList = StructuredProperty(BufferModel.Curse, repeated=True)
class BufferModel(ndb.Model):
name = stringProperty()
I got back the error mentioned above : Cannot repeat StructuredProperty None that has repeated properties of its own.
I decided to do a survey of possibly solutions.
1) I switched to the basic datastore db (not ndb) and changed everything to the List property
weaponList = ListProperty(WeaponModel.Weapon) //you must pass a data type in here so it knows what it will be storing
Turns out the Listproperty only allows lists of known datastore type (keys, strings, integers) - so storing my model entities wasn't going to work. Storing lists of keys would though.
weaponList = ListProperty(db.key)
It works adequately. To allow users to edit or create their own weapons I could just add a creator property on a weapon and then query based on that or some other reference like - public or private.
Didn't really like the idea of that. For whatever reason visions of the spaghetti monster kept coming to my head, so i thought- what about this json object guy in the NDB?
The mostly pseudo code version of building that thing would look like:
aCurse = getcurse
aJsonCurse = json.dump(aCurse)
aWeapon = getWeapon(curseList=[aJsonCurse])
aJsonWeapon = json.dump(aWeapon)
aCharacter.weaponList = [aJsonWeapon]
but it didn't work that easily. the weapon had json objects before is was itself dumped to json, so dumping the weapon with the nested json wasn't very nice. I Worked around it by building things differently, but the spaghetti monster came and started laughing so I stopped and went to lunch.
When I came back I figured I'd look at the docs again. The only written difference between structured and LocalStructured was the lack of indexing, which didn't seem to help, but I figured I'd give it a go anyway - and it works fine. Well, I'm not erroring out and I can retrieve the entities as needed - who knows what dangers lurk ahead.
Hopefully this will point some fellow blind men in the right direction.
Upvotes: 3