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.