Transactions using the Firebase Integration

Continuing the discussion from Unofficial Anvil Firebase Integration v0.1🔥:

I am trying to implement a transaction with the following function:

  def add_data(self,data,uid):
    transaction = fs.transaction() #THIS LINE RAISES AN ERROR
    doc1_ref  = fs.doc(fs.db,'collection_1',uid)
    doc2_ref = fs.doc(fs.db,'collection_2','<uid>')
    doc2=transaction.get(doc2_ref)
    count = doc2[1]['counter']
    transaction.update(doc1_ref,{'count':count})
    transaction.update(doc2_ref,{'counter':count+1})
    return True

But I get an error stating fire store does not have a transaction attribute.

Looking at the documentation (Linked above) the transaction instance in the example doesn’t show how the object is instantiated.

here is the example:

doc_ref1 = fs.doc(fs.db,'test_collection','uid1')
doc_ref2 = fs.doc(fs.db,'test_collection','uid2')
#read from doc 1
doc1 = transaction.get(doc_ref1)
#write to doc 2
transaction.update(doc_ref2, { 'new_value': 1234 })

The example in the firestore documentation:

transaction = db.transaction()
city_ref = db.collection("cities").document("SF")

@firestore.transactional
def update_in_transaction(transaction, city_ref):
    snapshot = city_ref.get(transaction=transaction)
    new_population = snapshot.get("population") + 1
...

Do I need to use the transactional decorator? and how do I instantiate the transaction object?

HI @anthonys,

I updated the docs with an up to date transaction example.
Basically, what you do is give a function to the run_transaction method and this function will then be executed as a transaction.
Docs

new_order_dict = {'name':'my example order'}

def add_new_order(transaction):
  #get the document which stores the order ids
  order_id_ref = fs.doc(fs.db,'ids','current_order_number')
  order_id_doc = transaction.get(order_id_ref)
  	
  #calculate the next free order id and write it to the dict
  next_order_id = order_id_doc.data()['count'] + 1
  new_order_dict['id'] = next_order_id
  
  #save new order document
  new_order_ref = fs.doc(fs.db,'orders') 
  transaction.set(new_order_ref,new_order_dict)
  
  #increment the order id for the next order
  transaction.update(order_id_ref,{'count': fs.increment(1)})


#Run the above database operations in a transaction     
fs.run_transaction(add_new_order)```
2 Likes

This is my EXACT use case :slight_smile: Thank you very much @mark.breuss

I get the following error on the transaction.set call, but I do not get this error when adding the document without the transcation.

FirebaseError: [code=invalid-argument]: Function Transaction.set() called with invalid data. Unsupported field value: a custom constructor object (found in document transactions

I suspect it has something to do with using datetime objects in the transaction, but I not entirely sure. I ran the transactions with a simply dictionary like the example above and it worked.

Are there other data manipulation being done when adding a document using firestore.add_doc that isn’t done when using transaction.set?

The reason for that is that you are essentially calling a javascript function, thus datetimes are not serialized automatically.

The reason why you don’t get an errror for normal updates is that the library takes care of the conversion.
Good thing is that these helper methods are exposed and you can convert them manually.

You can use:
fs.utility.to_proxy({})
To convert a python dict with to a js dict that handles the datetime conversions.

That did it! :+1: Thank you!