<br />

<br />

Each entity is identified by a key that is unique within the application's
Datastore instance, and consists of the following:

- **kind** . The kind is normally the name of the model class to which the entity belongs, but you can change this to some other string by overriding the classmethod `_get_kind()`.
- **identifier** . You specify your own *key name* as the identifier or let Datastore automatically generate an integer numeric ID.

## Specifying your own key name

The following example implicitly creates a key with a string identifier
using the named parameter `id`:

    account = Account(
        username='Sandy', userid=1234, email='sandy@example.com',
        id='sandy@example.com')

    return account.key.id()  # returns 'sandy@example.com'

You could alternatively set the key name directly:

    account.key = ndb.Key('Account', 'sandy@example.com')

    # You can also use the model class object itself, rather than its name,
    # to specify the entity's kind:
    account.key = ndb.Key(Account, 'sandy@example.com')

## Letting Datastore generate an ID to use for the key

This code shows how to use an auto-generated ID as the key:

    # note: no id kwarg
    account = Account(username='Sandy', userid=1234, email='sandy@example.com')
    account.put()
    # account.key will now have a key of the form: ndb.Key(Account, 71321839)
    # where the value 71321839 was generated by Datastore for us.

## Using the ancestor path in the key

The sequence of entities beginning with a root entity and proceeding from parent
to child, leading to a given entity, constitute that entity's *ancestor path* .
An entity, its parent, parent's parent, and so on recursively, are the entity's
*ancestors*. The entities in Datastore form a hierarchical key space
similar to the hierarchical directory structure of a file system.

The complete key identifying an entity consists of a sequence of kind-identifier
pairs specifying its ancestor path and terminating with those of the entity
itself. The constructor method for class `Key` accepts such a sequence of kinds and
identifiers and returns an object representing the key for the corresponding entity.

The following example shows a blogging service that stores messages by revision.
Messages are organized under accounts, and revisions are under messages.


    class Revision(ndb.Model):
        message_text = ndb.StringProperty()

`...`

    ndb.Key('Account', 'sandy@example.com', 'Message', 123, 'Revision', '1')
    ndb.Key('Account', 'sandy@example.com', 'Message', 123, 'Revision', '2')
    ndb.Key('Account', 'larry@example.com', 'Message', 456, 'Revision', '1')
    ndb.Key('Account', 'larry@example.com', 'Message', 789, 'Revision', '2')

In the sample, `('Account', 'sandy@example.com')`, `('Message', 123)`, and `('Revision', '1')`
are all examples of kind-identifier pairs.

Notice that `Message` is not a model class; it is used only as a way to group
revisions, not to store data.

As shown in the sample code, the entity's kind is designated by the *last*
kind-name pair in the list: `ndb.Key('Revision', '1')`.

### Using named parameters

You can use the named parameter `parent` to designate any entity in the ancestor
path directly. All of the following notations represent the same key:

    ndb.Key('Account', 'sandy@example.com', 'Message', 123, 'Revision', '1')

    ndb.Key('Revision', '1', parent=ndb.Key(
        'Account', 'sandy@example.com', 'Message', 123))

    ndb.Key('Revision', '1', parent=ndb.Key(
        'Message', 123, parent=ndb.Key('Account', 'sandy@example.com')))

### Specifying a root entity

For a root entity, the ancestor path is empty and the key consist solely of the
entity's own kind and identifier.

    sandy_key = ndb.Key(Account, 'sandy@example.com')

### Specifying an entity with ancestors

To insert a new message with parent keys

    account_key = ndb.Key(Account, 'sandy@example.com')

    # Ask Datastore to allocate an ID.
    new_id = ndb.Model.allocate_ids(size=1, parent=account_key)[0]

    # Datastore returns us an integer ID that we can use to create the message
    # key
    message_key = ndb.Key('Message', new_id, parent=account_key)

    # Now we can put the message into Datastore
    initial_revision = Revision(
        message_text='Hello', id='1', parent=message_key)
    initial_revision.put()

For keys that were created with a parent, the `parent()` method returns a key
representing the parent entity:

    message_key = initial_revision.key.parent()

## Using Numeric Key IDs

You can create an entity without specifying an ID, in which case the data store
automatically generates a numeric ID. If you choose to specify some IDs and then
let Datastore automatically generate some IDs, you could
violate the requirement for unique keys. To avoid this, reserve a range of
numbers to use to choose IDs or use string IDs to avoid this issue entirely.

To reserve a range of IDs, use the model class'
[`allocate_ids()`](https://docs.cloud.google.com/appengine/docs/standard/services/ndb/modelclass#Model_allocate_ids)
class method:

- to allocate a specified number of IDs
- to allocate all IDs up to a given maximum value.

### Allocating IDs

To allocate 100 IDs for a given model class `MyModel`:

    first, last = MyModel.allocate_ids(100)

To allocate 100 IDs for entities with parent key `p`:

    first, last = MyModel.allocate_ids(100, parent=p)

The returned values, `first` and `last`, are the first and last IDs (inclusive)
that are allocated. You can use these to construct keys as follows:

    keys = [ndb.Key(MyModel, id) for id in range(first, last+1)]

These keys are guaranteed not to have been returned previously by the data
store's internal ID generator, nor will they be returned by future calls to the
internal ID generator. However, the `allocate_ids()` method does not check
whether the IDs returned are present in the data store; it only interacts with
the ID generator.

To allocate all IDs up to a given maximum value:

    first, last = MyModel.allocate_ids(max=N)

This form ensures that all IDs less than or equal to `N` are considered
allocated. The return values, `first` and `last`, indicate the range of IDs
reserved by this operation. It is not an error to attempt to reserve IDs already
allocated; if that happens, `first` indicates the first ID not yet allocated and
`last` is the last ID allocated.

> [!NOTE]
> **Note:** you cannot call `allocate_ids()` in a [transaction](https://docs.cloud.google.com/appengine/docs/standard/services/ndb/transactions).

<br />

<br />