Firebase Realtime Database is a NoSQL cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client. Using Firebase Admin SDK, we can read and write Realtime Database data with full admin privileges, or limited privileges.
In my recent project, I had the opportunity to work with Firebase Realtime Database. So, I decided to write a basic guide to using Firebase Realtime Database using Python SDK.
To use the Firebase Admin SDK, we’ll need a Firebase project, a service account to communicate with the Firebase service, and a configuration file with your service account’s credentials. Follow Add Firebase to your app to get the configuration file, which is a JSON file.
The Firebase Admin Python SDK enables server-side (backend) Python developers to integrate Firebase into their services and applications. To install Firebase Admin Python SDK, simply execute the following command in a terminal:
pip install firebase-admin
Initialize the SDK with this code snippet:
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
# Fetch the service account key JSON file contents
cred = credentials.Certificate('firebase-adminsdk.json')
# Initialize the app with a service account, granting admin privileges
firebase_admin.initialize_app(cred, {
'databaseURL': 'https://<app_name>.firebaseio.com/'
})
In Firebase Realtime Database, data is structured as a JSON tree. Unlike a SQL database, there are no tables or records. When we add data to the JSON tree, it becomes a node in the existing JSON structure with an associated key.
Best practice for a data structure is to avoid nesting data and to keep the data structure as flat as possible. Follow Structure Your Database for a detailed guideline.
Create a database reference and then pass it an object using set
method:
ref = db.reference('/')
ref.set({
'boxes':
{
'box001': {
'color': 'red',
'width': 1,
'height': 3,
'length': 2
},
'box002': {
'color': 'green',
'width': 1,
'height': 2,
'length': 3
},
'box003': {
'color': 'yellow',
'width': 3,
'height': 2,
'length': 1
}
}
})
Follow Saving Data for more information.
To update a node of a database location without overwriting other child nodes, use the update
method as shown below:
ref = db.reference('boxes')
box_ref = ref.child('box001')
box_ref.update({
'color': 'blue'
})
This will update box001 color to blue.
The Firebase Realtime Database also supports multi-path updates. Using it, we can update color to both box001 and box002 at the same time:
ref = db.reference('boxes')
ref.update({
'box001/color': 'red',
'box002/color': 'blue'
})
In Python Admin SDK all write methods are blocking. That is, the write methods do not return until the writes are committed to the database.
If we want to store a list of boxes, then we might want to use a unique key for each new box instead of box001, box002, box003. To solve this, the Firebase clients provide a push()
function that generates a unique key for each new child
ref = db.reference('boxes')
ref.push({
'color': 'purple',
'width': 7,
'height': 8,
'length': 6
})
The unique key is based on a timestamp, so list items will automatically be ordered chronologically. Our database data now looks like this:
{
"boxes": {
"-L5_dtlqoO2K8rgwdxJ3": {
"color": "purple",
"width": 7,
"height": 8,
"length": 6
}
}
}
Calling push() will return a reference to the new data path, which you can use to get the key or set data to it. The following code will result in the same data as the above example, but now we’ll have access to the unique key that was generated:
ref = db.reference('boxes')
# Generate a reference to a new location and add some data using push()
new_box_ref = ref.push({
'color': 'purple',
'width': 7,
'height': 8,
'length': 6
})
# Get the unique key generated by push()
box_id = new_box_ref.key
The Python Admin SDK currently only supports blocking reads. It cannot be used to add event listeners that receive realtime update notifications. In python, data stored in a Firebase Realtime Database is retrieved by invoking a blocking method on a database reference, which returns the data stored at the reference. Each method call is a onetime operation.
The get()
method in Python returns a Python representation of the data directly:
ref = db.reference('boxes')
print(ref.get())
Firebase Realtime Database supports some queries to selectively retrieve data based on various factors.
We can order data in three ways: by child key, by key, or by value. A basic database query starts with one of these ordering functions, each of which is explained below.
If we want to use order query, we need to add the values/keys to the .indexOn
rule.
{
"rules": {
"boxes": {
".indexOn": ["color", "width", "height", "length"]
},
"weights": {
".indexOn": [".value"]
},
".read": "auth != null",
".write": "auth != null"
}
}
Read Index your data on the .indexOn
rule for more information.
To get data of all boxes ordered by height, we can do the following:
ref = db.reference('boxes')
snapshot = ref.order_by_child('height').get()
for key, val in snapshot.items():
print('{0} => {1}'.format(key, val))
We can also order nodes by their keys using the orderByKey()
method:
ref = db.reference('boxes')
snapshot = ref.order_by_key().get()
for key, val in snapshot.items():
print('{0} => {1}'.format(key, val))
We can order nodes by the value of their child keys using the orderByValue()
method:
{
"weights": {
"person001" : 60,
"person002" : 65,
"person003" : 80,
"person004" : 55,
"person005" : 72
}
}
ref = db.reference('weights')
snapshot = ref.order_by_value().get()
for key, val in snapshot.items():
print('{0} => {1}'.format(key, val))
The limitToFirst()
and limitToLast()
queries are used to set a maximum number of children to be retrieved:
ref = db.reference('boxes')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
print(key)
ref = db.reference('boxes')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
print(key)
weights_ref = db.reference('weights')
snapshot = weights_ref.order_by_value().limit_to_last(3).get()
for key, val in snapshot.items():
print('{0} => {1}'.format(key, val))
Using startAt()
, endAt()
, and equalTo()
allows to choose arbitrary starting and ending points for our queries:
ref = db.reference('boxes')
snapshot = ref.order_by_child('color').start_at('g').get()
for key in snapshot:
print(key)
ref = db.reference('boxes')
snapshot = ref.order_by_key().end_at('w').get()
for key in snapshot:
print(key)
ref = db.reference('boxes')
snapshot = ref.order_by_key().start_at('g').end_at(u'n\uf8ff').get()
for key in snapshot:
print(key)
ref = db.reference('boxes')
snapshot = ref.order_by_child('length').equal_to(3).get()
for key in snapshot:
print(key)
Visit Data security to know about security rules.
Visit firebase_admin.db module to get information about the module containing functions and classes that facilitate interacting with the Firebase Realtime Database.
Download source code from Github.
#python #databases #firebase #web-development