Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@lukwam
Copy link
Contributor

@lukwam lukwam commented Feb 28, 2025

This PR includes support for Firebase transactions. Specifically, the following methods now take an optional transaction argument.

  • Model.delete(transaction=transaction)
  • Model.find_one(transaction=transaction)
  • Model.find(transaction=transaction)
  • Model.get_by_doc_id(transaction=transaction)
  • Model.get_by_id(transaction=transaction)
  • Model.reload(transaction=transaction)
  • Model.save(transaction=transaction)
  • SubModel.get_by_id(transaction=transaction)

I've included a new section in the README with an example of using async transactions and there is a unit test that covers the same case as the README.

Let me know if you have any questions and thanks for your consideration!

@fbjorn
Copy link
Contributor

fbjorn commented Mar 5, 2025

Hi @lukwam, thanks for your contribution! Just want to inform you that we're pretty busy in the upcoming weeks and the review process might be a bit long. But we will definitely find time for that!

@lukwam
Copy link
Contributor Author

lukwam commented Mar 5, 2025

Hi @lukwam, thanks for your contribution! Just want to inform you that we're pretty busy in the upcoming weeks and the review process might be a bit long. But we will definitely find time for that!

No problem. I'm installing it from git+https://github.com/lukwam/firedantic.git@transactions#egg=firedantic for now.

Copy link
Contributor

@joakimnordling joakimnordling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for great work on a an important feature to the library! And sorry it took us this long to get to review it. We now had a look at it with @fbjorn.

There's some small things we'd want to see changed before we can merge it; see the detailed comments in the code.

README.md Outdated

### Transactions Example

In this example, we are creating a `Profile` model in a transaction that verifies the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could replace this example with a simpler example using a City model mimicking the very first example from https://firebase.google.com/docs/firestore/manage-data/transactions instead?

We'd also like to see the transactional function used as a classmethod of the class rather than a standalone function to better show the benefits of using models.

There's no need to do a "second pass" of it; it's not really testing the transaction logic in any way, just testing the exception in the function, not running at the same time as the first transaction and thus not causing any race conditions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have replaced the Profile example in the README.md with a City example that exactly mimics the first transactions example in the official Firestore transactions docs for Python here: https://firebase.google.com/docs/firestore/manage-data/transactions#python_1.

Copy link
Contributor

@joakimnordling joakimnordling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had a look at the latest updates with @fbjorn. It seems like the tests are not passing and you also seem to have accidentally included a partial version of the configuration refactoring you mentioned you were working on. We'd prefer if you would not mix in the configuration refactoring into this PR, rather make it as separate PR. We think it should still be possible to without that to make a get_transaction() helper.

It also looks like in addition to the tests not passing, there's some logic errors in the test that uses the City; it's not saved before it's reloaded and the version of it in the README is out of sync with the one in the tests; the version in the README is not even initializing the City model before calling the update_in_transaction. And it's a bit conusing it's using a city_ref (makes me think it's a document reference instead of the city_id).

We also earlier requested to make the transactional function a classmethod for nicer encapsulation.

@lukwam
Copy link
Contributor Author

lukwam commented Jun 2, 2025

Yeah, sorry. I wanted to push up some changes and reply to some of your comments but I didn't get it fully working yet. I still have more to do, when I have time, but I think this gives you the ability to review the responses to many of the comments that I did resolve while I continue to complete the rest of the pr and get the tests working.

@joakimnordling
Copy link
Contributor

@lukwam, we actually realized we would in one of our internal projects, where we use firedantic, see some benefit of using transactions (not required, but would be nice). And I think it wouldn't require too much work to finish this, so I got an OK to spend the time necessary to complete this in order to be able to use it in that project. But I don't either want to step on your toes with it in case you want to complete it. Thus I'd want to check that would you be OK with it, if I'd continue from this step to complete it? I would of course keep the commit history etc, so it'll be visible that you've done most of it.

@lukwam
Copy link
Contributor Author

lukwam commented Jun 5, 2025

I won't be offended if you push this through. I am excited about continuing to contribute to this project, but I don't have time at the moment to wrap up this PR so you can merge it. If you are able to do it, I would be grateful. I have a dire need for transactions in my current project, so however it gets done I will be happy about it.

The other thing I want to work on when I have some cycles is to add support for multiple firestore databases. Currently, the single client configuration makes that impossible. I also have a project that I want to move from sync to async and I want to be able to do it one collection at a time, and so being able to have some models use an async client and some use a sync client would help with that.

Thanks for reaching out to me! 🀝

@joakimnordling
Copy link
Contributor

I won't be offended if you push this through. I am excited about continuing to contribute to this project, but I don't have time at the moment to wrap up this PR so you can merge it. If you are able to do it, I would be grateful. I have a dire need for transactions in my current project, so however it gets done I will be happy about it.

The other thing I want to work on when I have some cycles is to add support for multiple firestore databases. Currently, the single client configuration makes that impossible. I also have a project that I want to move from sync to async and I want to be able to do it one collection at a time, and so being able to have some models use an async client and some use a sync client would help with that.

Thanks for reaching out to me! 🀝

Great, I'll try to get it done pretty soon, actually planning to get started on it later today.

When it comes to supporting multiple databases, I need to admit I've not looked into it basically at all; it was a feature added pretty recently. When firedantic was originally created it wasn't supported. And we actually ended up using collection name prefixes for different pieces of software that stored data in the same database and that has worked pretty well. But indeed it would be nice to have support for multiple databases. Would be nice to hear that would you want to be able to configure the database then per model or how.

When it comes to using some models with sync and some with async, I don't think there should really be any issue; should be a matter just of subclassing the models from the right models. I could also mention that I've had a need to use some async models in sync code (in practice CLI tools) where I just made a simple @async_to_sync decorator that wraps the function and runs it with asyncio.run().

@joakimnordling
Copy link
Contributor

When looking deeper into the codebase, I realized I was incorrect... Due to the way the configuration is stored, it is indeed not possible (at least easily/conveniently) use both async and sync models at the same time, because you have either a Client or an AsyncClient configured. Sorry about that mistake. Some sort of refactoring of the configuration would make sense. Not sure exactly how yet. And I also encountered a bit of an issue with typing when making the wrapper for getting a new transaction due to the fact that the config can either have a Client or an AsyncClient configured. Unless I find some other way than what I'm currently working on, I won't be 100% happy with it, but I don't on the other hand right now want to also refactor the configuration. But I think you could open an issue here on GitHub about refactoring the configuration so that we could support both multiple DBs and at the same time sync/async models in projects using the library.

Still need to update the example in the README and consider if there should be some more advanced test also.

The main test case and the City model now also show how you can work with an instance method and transactions. Seems like you need to wrap the transactional function in order to make it work. The `@async_transactional` decorator expects the first argument to be `transaction` and not for example `self`.
Joakim Nordling added 7 commits June 5, 2025 16:03
Let me know if you see any reason for them. My PyCharm does no complain and mypy is also happy without them.
This new field was not really in the end used anywhere.
Copy link
Contributor

@joakimnordling joakimnordling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'm of course a bit biased, since I did some of the last changes. Please @fbjorn have a look at this PR.

And of course feedback from @lukwam also welcomed if there's something I changed that you are not happy with.

@lukwam
Copy link
Contributor Author

lukwam commented Jun 5, 2025

Looks good to me! Thanks so much for finishing this up!

Copy link
Contributor

@fbjorn fbjorn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you guys. LGTM

@joakimnordling joakimnordling merged commit 2287fa9 into ioxiocom:main Jun 6, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants