{"id":28416496,"url":"https://github.com/tan-z-tan/pyfireconsole","last_synced_at":"2025-10-13T01:05:09.934Z","repository":{"id":185582255,"uuid":"673767072","full_name":"tan-z-tan/PyFireConsole","owner":"tan-z-tan","description":"PyFireConsole provides a seamless interface to Google's Firestore in Python, simplifying tasks such as connection, ORM, and data associations.","archived":false,"fork":false,"pushed_at":"2024-08-16T07:31:09.000Z","size":219,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-16T06:17:08.301Z","etag":null,"topics":["console","firebase","firestore","google-cloud-firestore","orm","python","python3","repl","web-development"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tan-z-tan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-08-02T11:27:41.000Z","updated_at":"2024-09-19T01:42:51.000Z","dependencies_parsed_at":"2025-04-16T18:09:35.186Z","dependency_job_id":"f38f3cc1-94a6-49f6-b57e-6d2b2d9829ee","html_url":"https://github.com/tan-z-tan/PyFireConsole","commit_stats":null,"previous_names":["tan-z-tan/pyfireconsole"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/tan-z-tan/PyFireConsole","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tan-z-tan%2FPyFireConsole","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tan-z-tan%2FPyFireConsole/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tan-z-tan%2FPyFireConsole/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tan-z-tan%2FPyFireConsole/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tan-z-tan","download_url":"https://codeload.github.com/tan-z-tan/PyFireConsole/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tan-z-tan%2FPyFireConsole/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279013884,"owners_count":26085325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["console","firebase","firestore","google-cloud-firestore","orm","python","python3","repl","web-development"],"created_at":"2025-06-03T21:05:26.987Z","updated_at":"2025-10-13T01:05:09.929Z","avatar_url":"https://github.com/tan-z-tan.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Python package](https://github.com/tan-z-tan/PyFireConsole/actions/workflows/test.yml/badge.svg)](https://github.com/tan-z-tan/PyFireConsole/actions/workflows/test.yml)\n\n\u003ch1 style=\"text-align: center;\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/tan-z-tan/pyfireconsole/main/logo.png\" alt=\"PyFireConsole\"\u003e\n\u003c/h1\u003e\n\n## Introduction\nInspired by Rails console, PyFireConsole provides a seamless interface to Google's Firestore in Python, simplifying tasks such as connection, ORM, and data associations. It makes managing Firestore a breeze.\n\n\u003cimg src=\"https://github.com/tan-z-tan/PyFireConsole/assets/2078683/1e4e51a0-ac3f-4f75-96f3-6cf72fa627b6\"\u003e\u003c/img\u003e\n\n## Features\n- Model Definition and ORM: Define your Firestore data models within Python and use object-relational mapping (ORM) for easier data manipulation and querying.\n- Data Associations: Effortlessly manage relationships between your Firestore data models.\n- Interactive Console: Inspired by the Rails console, PyFireConsole provides a console for interactive data manipulation and querying, making it simple to perform tasks on your Firestore data.\n\n## Installation\n```sh\npip install pyfireconsole\n```\n\n## Getting Started\n### Connect to Firestore\nFirst of all, you need to initialize `FirestoreConnection` with your project id.\n```python\n# Initialize FirestoreConnection using your default credentials of gcloud. (use `gcloud auth application-default login` or set GOOGLE_APPLICATION_CREDENTIALS)\nFirestoreConnection().initialize(project_id=\"YOUR-PROJECT-ID\")\n\n# Or you can specify service_account_key_path\nFirestoreConnection().initialize(service_account_key_path=\"./service-account.json\", project_id=\"YOUR-PROJECT-ID\")\n```\n\n### Find a document by id\nLike Rails, you can define your model class by inheriting `PyfireDoc` class.\n```python\nfrom pyfireconsole.models.pyfire_model import PyfireDoc\n\nclass User(PyfireDoc):\n    name: str\n    email: str\n    role: str = \"user\"\n\n# Find a document by id\nuser = User.find(\"XXX\")\n#=\u003e User[users/XXX](id='XXX', name='John', email='john@example.com', role='user')\n\n# Nested document can be accessed by path\nuser = User.find(\"companies/ZZZ/users/YYY\")\n#=\u003e User[companies/ZZZ/users/YYY](id='YYY', name='John', email='john@example.com', role='user')\n```\n\n### Where query\nYou can use `where` method to query documents.\n```python\nadmin_users = User.where(\"role\", \"==\", \"admin\")\n#=\u003e PyfireCollection\u003cUser\u003e[users]\n\n# admin_user is iterable\nfor user in admin_users:\n    print(user)\n    #=\u003e User[users/YYYYYYYYYY](id='YYYYYYYYYY', name='Mary', email='mary@example.com', role='admin')\n```\n\n### Sub collection\nYou can define sub collection of a document by using `PyfireCollection` class.\n```python\nfrom pyfireconsole.models.pyfire_model import PyfireCollection, DocumentRef, PyfireDoc\n\n# Tag is sub collection of Book\nclass Tag(PyfireDoc):\n    name: str\n\nclass Book(PyfireDoc):\n    title: str\n    user_id: str\n    published_at: datetime\n    tags: PyfireCollection[Tag] = PyfireCollection(Tag)  # Specify sub collection type\n\n\nbook = Book.find(\"XXXX\")\nfor tag in book.tags:\n    print(tag)\n    #=\u003e Tag[books/XXXX/tags/YYYY](id='YYYY', name='Python')\n```\n\n### has_many, belongs_to\nYou can define data associations by using `has_many` and `belongs_to` decorators.\n\nNow `book.user` returns `User` object, and `user.books` returns `PyfireCollection[Book]` object.\n\n```python\nfrom pyfireconsole.models.association import belongs_to, has_many, resolve_pyfire_model_names\nfrom pyfireconsole.models.pyfire_model import PyfireCollection, DocumentRef, PyfireDoc\n\n\n@has_many('Book', db_field=\"user_id\", attr_name=\"my_books\")\nclass User(PyfireDoc):\n    name: str\n    email: str\n\nclass Tag(PyfireDoc):\n    name: str\n\n@belongs_to(User, \"user_id\")\nclass Book(PyfireDoc):\n    title: str\n    user_id: str\n    published_at: datetime\n    tags: PyfireCollection[Tag] = PyfireCollection(Tag)\n\n# call this to resolve model names (this is because of python's circular import problem)\nresolve_pyfire_model_names(globals())\n\nuser = User.find(\"YYYY\")\nuser.my_books\n=\u003e PyfireCollection[Book][books]\n\nuser.my_books.first.user\n=\u003e User[users/XXXX](id='XXXX', name='John', email=\"john@example.com\")\n```\n\n### as_json\nYou can convert PyfireDoc object to json serializable dict by using `as_json` method.\n```python\n# dump all admin users, not including sub collection\nUser.all().as_json()\n\n# dump all admin users, not including sub collection\nUser.where(\"role\", \"==\", \"admin\").as_json()\n\n# dump all admin users, including sub collection\nUser.where(\"role\", \"==\", \"admin\").as_json(recursive=True)\n\n# you can also dump custom attributes\nclass User(PyfireDoc):\n    name: str\n    email: str\n\n    def email_domain(self):\n        return self.email.split(\"@\")[1]\n\nUser.where(\"role\", \"==\", \"admin\").as_json(recursive=True, include=[\"email_domain\"])\n```\n\n### Empty document\nYou can instantiate empty document by using `allow_empty` option. The sub collection of empty document can be accessed.\n\n```python\n# This is empty document which doesn't exist in firestore but sub collection exists.\nuser = User.find(\"XXXXX\", allow_empty=True)\n\n# You can't access empty document's attributes\n# user.name =\u003e AttributeError: 'User' object has no attribute 'name'\n\n# You can access sub collection of empty document\nprint([n.title for n in user.books])\n```\n\n### Example\nWe assume that you have a firestore database with the following structure:\n\n```\n- users Collection\n    - {user_id} Document\n        - name: str\n        - email: str\n- publishers Collection\n    - {publisher_id} Document\n        - name: str\n        - address: str\n- books Collection\n    - {book_id} Document\n        - title: str\n        - user_id: str\n        - published_at: datetime\n        - authors: list[str]\n        - tags: Sub Collection\n            - {tag_id} Document\n                - name: str\n        - publisher_ref: Reference\n```\n\n```python\nfrom datetime import datetime\nfrom typing import Optional\nfrom pyfireconsole.models.association import belongs_to\nfrom pyfireconsole.models.pyfire_model import PyfireCollection, DocumentRef, PyfireDoc\nfrom pyfireconsole.db.connection import FirestoreConnection\n\n\n# Define models\n# PyfireDoc is a subclass of Pydantic(2.x) BaseModel. You can use Pydantic's features.\n@has_many('Book', \"user_id\")\nclass User(PyfireDoc):\n    name: str\n    email: str\n\n\nclass Publisher(PyfireDoc):\n    name: str\n    address: Optional[str]\n\n\nclass Tag(PyfireDoc):\n    name: str\n\n\n@belongs_to(User, \"user_id\")\nclass Book(PyfireDoc):\n    title: str\n    user_id: str\n    published_at: datetime\n    authors: list[str]\n    tags: PyfireCollection[Tag] = PyfireCollection(Tag)\n    publisher_ref: DocumentRef[Publisher]\n\n\n# Initialize FirestoreConnection using your default credentials of gcloud. (use `gcloud auth application-default login` or set GOOGLE_APPLICATION_CREDENTIALS)\nFirestoreConnection().initialize(project_id=\"YOUR-PROJECT-ID\")\n\n# Or you can specify service_account_key_path\n# FirestoreConnection().initialize(service_account_key_path=\"./service-account.json\", project_id=\"YOUR-PROJECT-ID\")\n\nprint(\"==================== find ====================\")\nbook = Book.find(\"XlvQHeGi3cODbI4MQpI3\")  # =\u003e Book\nprint(book.model_dump())  # =\u003e dict\nprint(f\"ID: {book.id} | Title: {book.title} | Authors: {book.authors} | Published At: {book.published_at.isoformat()}\")\n\nprint(\"==================== belongs_to ====================\")\nprint(book.user)  # =\u003e User\nprint(book.user.name)  # =\u003e str\n\nprint(\"==================== reference ====================\")\nprint(book.publisher_ref)  # =\u003e DocumentRef\nprint(book.publisher_ref.path)  # =\u003e str (So far, we can't access publisher_ref.name directly for ref type)\n\nprint(\"==================== where ====================\")\nprint(Book.where(\"title\", \"==\", \"test\"))  # =\u003e Book[] Make sure to create index in firestore for compound queries\n\nprint(\"==================== where \u0026 order ====================\")\nprint(Book.where(\"title\", \"==\", \"test\").order(\"published_at\", \"ASCENDING\"))  # =\u003e Book[] Make sure to create index in firestore for compound queries\n\nprint(\"==================== has_many ====================\")\nprint(book.tags)  # =\u003e PyfireCollection[Tag]\nprint(book.tags.first)  # =\u003e Tag\n```\n\n## Interactive Console\nPyFireConsole comes with an interactive console that allows developer to view and manipulate Firestore data in a live and easily.\nThis feature is inspired by the Rails console.\n\nFirst, you need to define your models.\n```python\n# app/models/models.py\n\nfrom datetime import datetime\nfrom typing import Optional\nfrom pyfireconsole.models.association import belongs_to, has_many, resolve_pyfire_model_names\nfrom pyfireconsole.models.pyfire_model import PyfireCollection, DocumentRef, PyfireDoc\nfrom pyfireconsole.db.connection import FirestoreConnection\nfrom pyfireconsole import PyFireConsole\n\n\n@has_many('Book', \"user_id\")\nclass User(PyfireDoc):\n    name: str\n    email: str\n\n\nclass Publisher(PyfireDoc):\n    name: str\n    address: Optional[str]\n\n\nclass Tag(PyfireDoc):\n    name: str\n\n\n@belongs_to(User, \"user_id\")\nclass Book(PyfireDoc):\n    title: str\n    user_id: str\n    published_at: datetime\n    authors: list[str]\n    tags: PyfireCollection[Tag] = PyfireCollection(Tag)\n    publisher_ref: DocumentRef[Publisher]\n```\n\nStart interactive console by using `pyfireconsole` command.\n```bash\npyfireconsole --model-dir app/models\n```\nThis command loads all python classes in `app/models` directory and starts interactive console.\n\n- --model-di: model directory path\n- --project-id: project id (optional)\n- --service-account-key-path: service account key path (optional)\n\n### Invoke console from your code\nYou can also call `PyFireConsole().run()` from your code.\n\n```python\nfrom datetime import datetime\nfrom typing import Optional\nfrom pyfireconsole.models.association import belongs_to, has_many, resolve_pyfire_model_names\nfrom pyfireconsole.models.pyfire_model import PyfireCollection, DocumentRef, PyfireDoc\nfrom pyfireconsole.db.connection import FirestoreConnection\nfrom pyfireconsole import PyFireConsole\n\n\n@has_many('Book', \"user_id\")\nclass User(PyfireDoc):\n    name: str\n    email: str\n\n\nclass Publisher(PyfireDoc):\n    name: str\n    address: Optional[str]\n\n\nclass Tag(PyfireDoc):\n    name: str\n\n\n@belongs_to(User, \"user_id\")\nclass Book(PyfireDoc):\n    title: str\n    user_id: str\n    published_at: datetime\n    authors: list[str]\n    tags: PyfireCollection[Tag] = PyfireCollection(Tag)\n    publisher_ref: DocumentRef[Publisher]\n\n# Resolve PyfireModel names\n# Call this function when you define your models by using str class name. e.g. @has_many('Book', \"user_id\")\nresolve_pyfire_model_names(globals())\n\n\nFirestoreConnection().initialize(project_id=\"YOUR_PROJECT_ID\")\nPyFireConsole().run()\n```\n\nThrough the interactive console, you can conveniently test and experiment with your Firestore data models.\n\n## Contributing\nYour contributions to PyFireConsole are warmly welcomed! Feel free to submit a pull request directly if you have any improvements or features to suggest. For any questions or issues, please create an issue on Github. Thank you for your interest in improving PyFireConsole!\n\n## License\nPyFireConsole is released under the MIT License.\n\nThis means you are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software. This permission is granted provided that the above copyright notice and this permission notice are included in all copies or substantial portions of the software.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftan-z-tan%2Fpyfireconsole","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftan-z-tan%2Fpyfireconsole","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftan-z-tan%2Fpyfireconsole/lists"}