If you allocate IDs for an entity called 'Sequence' you can use them
for any other entity kind, including entities with parents which would
otherwise use a unique range if auto allocated. IDs from 'Sequence'
will be unique within your datastore, which is a superset of the
requirement that IDs be unique per-entity group. However, you must
always then use an ID from Sequence and never allow auto-allocation.
If some of your entities have parents you can't universally Kind.get_by_id().
IDs are 64bit positive integers.
If you allocate IDs for an entity called 'Sequence' you can use them
for any other entity kind, including entities with parents which would
otherwise use a unique range if auto allocated. IDs from 'Sequence'
will be unique within your datastore, which is a superset of the
requirement that IDs be unique per-entity group. However, you must
always then use an ID from Sequence and never allow auto-allocation.
If some of your entities have parents you can't universally Kind.get_by_id().
I thought your concern was allocating IDs in advance of knowing
whether an entity has a parent. Entities with different parents (or no
parent) have IDs allocated from different sequence counters by
default. All I'm saying is: rather than jump through hoops trying to
allocate an ID from the correct range, allocate all IDs from one. But
if so, be consistent. Maybe I misunderstand your question...
This is a bit of an advanced question for the experts with regards to Keys, IDs, Models and the db.allocate_ids() functionality - those who can answer it won't need much more explanation, so apologies if it doesn't make much sense to everyone else (but you may want to file it away in case you ever need to do the same).My one-page-webapp uses a datastore framework that gets a bit distraught when it has create new items and allocate temporary IDs which will later get replaced with "true" IDs from the datastore (I've been using the actual Key strings as ids), so I thought I'd have a look at pre-allocating IDs.In the absence of cookbook examples, I've concluded
- I can pre-allocate a range of IDs for a given class using db.allocate_ids(), but as individual keys encode the id and the parent instance (if any) then I can't convert these pre-allocated IDs into pre-allocated Keys if I may be using parent objects.
- So I should change the client to use IDs (or maybe "type+id" tuples), and have queries to the GAE server return items with 'obj.key().id()' rather than 'str(obj.key())'
- The client can be given a range of pre-allocated IDs that it can safely assign to new records as it creates them, knowing they won't change when the item is created in the datastore
- For updating/deleting items on GAE, I'll replace calls to "db.get(key)" (where the key came from the client, and yes I then check the object is valid etc before I proceed) with "MyModel.get_by_id(ids)" (I know the types of objects by this stage so it's not like I'm doing a heterogeneous fetch)
- I'll still store references to other objects by db.ReferenceProperty type (ie key) rather than simple id as it makes migration of data easier (ie use of IDs rather than Keys is a client layer convenience only)
- I'll implement a new model base class for all db.Model derived classes, that defines a new optional "id" parameter and constructs the correct key (my python knowledge of the constructs is bit green, so I think this does what I expect - kind() is a reserved but undocumented instance method that looks safer than using __name__ by looking at the SDK source)
class BaseModel(db.Model):def __init__(self, id=None, parent=None, **args):if id != None:args["key"] = db.Key.from_path(self.kind(), id, parent=parent)db.Model.__init__(self, parent=parent, **args)The db.Model ctor docs say that "key" can't be used with parent or key_name, looking at the SDK source implies the last line of the above should be fine, but would I do better to omit the "parent=parent" parameter and rely on the fact that, if needed, the parent object is looked up form the key?
- I can then make instances as before 'obj = MyModel(someproperty=97, another="Hello world")' but I now have a special optional "id" property for construction. I haven't changed parent or property semantics or the like.
Does the above look reasonable enough? I take it the efficiency is pretty much the same (ie there's no great added overhead such as extra database calls introduced by any of the above) but as I'm no great python expert, and the calls above are documented but not always explained, does the above look reasonable enough or am I laying myself open to a world of pain to come?Cheers--Tim
--
You received this message because you are subscribed to the Google Groups "Google App Engine" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-appengine/-/EICnNig9mKkJ.
To post to this group, send email to google-a...@googlegroups.com.
To unsubscribe from this group, send email to google-appengi...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-appengine?hl=en.
In the absence of cookbook examples, I've concluded
- I can pre-allocate a range of IDs for a given class using db.allocate_ids(), but as individual keys encode the id and the parent instance (if any) then I can't convert these pre-allocated IDs into pre-allocated Keys if I may be using parent objects.
False - see above.
- I'll implement a new model base class for all db.Model derived classes, that defines a new optional "id" parameter and constructs the correct key (my python knowledge of the constructs is bit green, so I think this does what I expect - kind() is a reserved but undocumented instance method that looks safer than using __name__ by looking at the SDK source)
You can do this if you want, but it's purely convenience. Don't override the constructor, create a class method instead. The constructor is used to reconstitute existing entities from the datastore in addition to creating new ones, so overriding it correctly is difficult.
Even though you're allocating from two ranges rather than one, that's
still less than the datastore would do things automatically, so you
still have to be careful.
For example, suppose you later add an admin web interface that can
also create new cars and add them to garages. If you let the datastore
allocate IDs, the first car you add to any garage through this new
code path will get ID 1 or thereabouts, because children get an ID
from the range associated with their parent. Eventually the
admin-generated car IDs may clash with those pre-allocated from the
global car sequence given out to remote clients, and old data will be
over written.
So you might want to enforce that a Car model can't be created without
passing in an ID.
Even though you're allocating from two ranges rather than one, that's
still less than the datastore would do things automatically, so you
still have to be careful.For example, suppose you later add an admin web interface that can
also create new cars and add them to garages. If you let the datastore
allocate IDs, the first car you add to any garage through this new
code path will get ID 1 or thereabouts, because children get an ID
from the range associated with their parent. Eventually the
admin-generated car IDs may clash with those pre-allocated from the
global car sequence given out to remote clients, and old data will be
over written.So you might want to enforce that a Car model can't be created without
passing in an ID.
There's nothing extra to manage here except making sure you're
consistent. Enforce this in Car's make() class method.
It's not much different than using Key.from_path('Car', '123') and
Car.get_by_name('123'), which is a fully supported way of managing IDs
in the datastore.
On Monday, 1 August 2011 11:16:40 UTC+1, Stephen wrote:Even though you're allocating from two ranges rather than one, that's
still less than the datastore would do things automatically, so you
still have to be careful.For example, suppose you later add an admin web interface that can
also create new cars and add them to garages. If you let the datastore
allocate IDs, the first car you add to any garage through this new
code path will get ID 1 or thereabouts, because children get an ID
from the range associated with their parent. Eventually the
admin-generated car IDs may clash with those pre-allocated from the
global car sequence given out to remote clients, and old data will be
over written.So you might want to enforce that a Car model can't be created without
passing in an ID.I think I'm only now getting the subtlety of my faulty assumption.db.allocate_ids for a given kind does not give unique IDs for that kind, it gives IDs that are only unique for that kind AND for a specific parent as specified by the key used to make the call to db.allocate_ids().
So I can't pre-allocate a bunch of IDs that can be used to make keys with whatever parent may be required without running the risk of duplicate IDs because the ID allocation mechanism, like the Keys themselves, is actually parent specific.
This pretty much blows pre-allocated IDs out of the water for me, at least for what I had in mind, unless I either drop entity groups altogether, or do something like like put all items into an an entity group per user (so the parent is something that represents the user, meaning that I know the parent to use). Of course I could just manage my own ID allocation, but I'm never really keen on that when a data persistance layer has its own ID/Key mechanism.
Oh well, back to the drawing board, thanks for the patient explanations--Tim
--
You received this message because you are subscribed to the Google Groups "Google App Engine" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-appengine/-/fZyPanewo-AJ.
To post to this group, send email to google-a...@googlegroups.com.
To unsubscribe from this group, send email to google-appengi...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-appengine?hl=en.
I'm not sure if you're tied to using integer ids, but you could also
generate UUIDs. Pretty low collision risk, especially if you're using
entity groups. If I'm not mistaken with the new pricing you'll be
charged for each id allocated by the datastore, another bonus for
UUIDs. ;)
Robert
> --
> You received this message because you are subscribed to the Google Groups
> "Google App Engine" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/google-appengine/-/fZyPanewo-AJ.