Firestore - Django Gcloud Connectors Documentation

# Firestore

Generally Firestore in Datastore mode seems to provide a better "normal" database experience than Firestore in native mode, so it is assumed that most users of `gcloudc` will use the Datastore connector. However, Firestore's native mode has the benefits of its realtime functionality, which may be a significant advantage for some use cases.

If you're using Firestore in native mode, beware of the following limitations.

## Primary keys are stored as strings

Firestore always stores the primary key field as a string, so if you don't override the primary key field on your models then you will get Django's default `AutoField`, which may cause surprises.

The `AutoField` tries to store an integer. In order to make sorting work correctly, `gcloudc` will pad the integer with zeros so that all values are the same length. This means that `str(obj.pk)`, as might be used in a URL path, will not necessarily give you the correct value to query for the object directly in the Firestore web console at console.cloud.google.com.

To avoid this problem, one solution is to use the supplied `AutoCharField`. E.g.:

    from gcloudc.db.models.fields.firestore import AutoCharField

    class MyModel(models.Model):

        id = AutoCharField(primary_key=True)

This generates ID values in the same format that Firestore generates automatically.

Alternatively you can use Django's `UUIDField` for your primary keys.

## You can't sort by PK descending

Currently, sorting a query by primary key descending is not allowed. To get around this you need to duplicate the primary key onto a separate field and then order by that field. For example:

    from gcloudc.db.backends.firestore.transaction import Transaction

    class DenormalizedPK(models.Model):
        uid = models.CharField(max_length=64, editable=False)

        class Meta:
            abstract = True

        def save(self, *args, **kwargs):
            if not self.pk:
                # H! This generates a Firestore ID so that we can denormalize before save.
                self.pk = Transaction(connection, None)._generate_id(str)

            self.uid = self.pk
            return super().save(*args, **kwargs)

## Indexes are not generated by the local emulator

Unlike with the Datastore emulator, the local Firestore emulator does not generate index definitions for you. However, it does provide a URL on localhost from which the indexes which would be required for queries which have been performed during the current run of the emulator. These indexes are provided in a different format to the one required in the `firestore.indexes.json` file in which you must provide your index definitions for deployment.

To extract these indexes from the emulator and merge them into your `firestore.indexes.json` file in the correct format, `gcloudc` provides a utility `gcloudc.utils.firestore_indexes.update_firestore_indexes`.

    update_firestore_indexes(
        emulator_project,
        emulator_config="firebase.json",
        indexes_file="firestore.indexes.json"
    )

You might want to call this in `manage.py` before shutdown, perhaps only for certain commands, or perhaps call it in specific tests. It's up to you.

Note that the `emulator_project` argument appears to need to be the name of the project that the emulator was started with, even if you're using the emulator in multi-project mode. Presumably this may change in the future.

GitLab « Previous Next »