{"id":7996,"library":"bunnet","title":"Bunnet - Synchronous Python ODM for MongoDB","description":"Bunnet is a synchronous Python Object-Document Mapper (ODM) for MongoDB, built upon Pydantic for data modeling. It simplifies interaction with MongoDB collections by mapping Python classes to documents, reducing boilerplate code for common database operations like adding, updating, and deleting. Bunnet is a synchronous fork of the popular Beanie ODM, and its current version is 1.3.0 with active development and maintenance.","status":"active","version":"1.3.0","language":"en","source_language":"en","source_url":"https://github.com/BeanieODM/bunnet","tags":["mongodb","odm","pydantic","sync","database"],"install":[{"cmd":"pip install bunnet","lang":"bash","label":"pip"},{"cmd":"poetry add bunnet","lang":"bash","label":"Poetry"}],"dependencies":[{"reason":"Bunnet uses Pymongo client under the hood for MongoDB interaction.","package":"pymongo","optional":false},{"reason":"Data models in Bunnet are based on Pydantic models for validation and serialization.","package":"pydantic","optional":false}],"imports":[{"symbol":"Document","correct":"from bunnet import Document"},{"symbol":"init_bunnet","correct":"from bunnet import init_bunnet"},{"symbol":"Indexed","correct":"from bunnet import Indexed"},{"symbol":"MongoClient","correct":"from pymongo import MongoClient"},{"note":"BaseModel should be imported from pydantic for nested models, not directly from bunnet.","wrong":"from bunnet import BaseModel","symbol":"BaseModel","correct":"from pydantic import BaseModel"}],"quickstart":{"code":"from typing import Optional\nfrom pymongo import MongoClient\nfrom pydantic import BaseModel\nfrom bunnet import Document, Indexed, init_bunnet\nimport os\n\n# Define a Pydantic model for a nested object\nclass Category(BaseModel):\n    name: str\n    description: str\n\n# Define a Bunnet Document model\nclass Product(Document):\n    name: str\n    description: Optional[str] = None\n    price: Indexed(float)\n    category: Category\n\n    # Optional: Configure collection name and other settings\n    class Settings:\n        name = \"products_collection\"\n\nasync def main():\n    # Connect to MongoDB (replace with your connection string)\n    # Use os.environ.get for secure credentials in production\n    MONGO_DETAILS = os.environ.get('MONGO_URI', 'mongodb://localhost:27017')\n    client = MongoClient(MONGO_DETAILS)\n\n    # Initialize Bunnet with the database and document models\n    # Ensure 'your_db_name' exists or will be created upon first write\n    init_bunnet(database=client.get_database('your_db_name'), document_models=[Product])\n\n    # Create a category instance\n    chocolate_category = Category(name=\"Chocolate\", description=\"A preparation of roasted and ground cacao seeds.\")\n\n    # Create a product instance\n    tonybar = Product(name=\"Tony's Chocolonely\", price=5.95, category=chocolate_category)\n\n    # Insert the document into the database\n    inserted_product = tonybar.insert()\n    print(f\"Inserted product: {inserted_product.name}\")\n\n    # Find a document using Pythonic syntax\n    found_product = Product.find_one(Product.price < 10).run()\n    if found_product:\n        print(f\"Found product: {found_product.name}\")\n\n    # Update a document\n    updated_product = found_product.set({Product.name: \"Gold Bar\"})\n    print(f\"Updated product to: {updated_product.name}\")\n\nif __name__ == \"__main__\":\n    # Bunnet is synchronous, so no asyncio.run() needed directly for document operations.\n    # The main function is defined as async only for consistency if a project mixed sync/async components\n    # For this quickstart, you would typically run the synchronous operations directly.\n    # However, to simulate an async context often seen in examples for its counterpart (Beanie),\n    # we wrap it here. For a purely synchronous application, you'd call main() directly and remove 'async/await'.\n    main()","lang":"python","description":"This quickstart demonstrates how to connect to MongoDB, define a Bunnet Document, insert data, and perform basic queries and updates. It uses Pydantic for defining nested data structures and `pymongo.MongoClient` for the underlying database connection. Remember to replace `mongodb://localhost:27017` and `your_db_name` with your actual MongoDB connection string and database name."},"warnings":[{"fix":"Use synchronous methods provided by Bunnet (e.g., `find_one().run()`) and ensure your application context is synchronous. If an async context is required, consider using BeanieODM instead.","message":"Bunnet is the synchronous version of Beanie ODM. Methods in Bunnet do not use `await` and attempts to call `async` methods (like `find_one_async`) will result in `AttributeError` or `TypeError`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Refer to Pydantic's official migration guide. If issues persist, check Bunnet's GitHub discussions or issues for specific Pydantic V2 related workarounds or bug fixes.","message":"Pydantic V2 introduced significant internal changes. While Bunnet aims for compatibility, developers migrating from Pydantic V1-based applications might encounter validation or serialization issues, especially with custom types or complex data structures.","severity":"breaking","affected_versions":"Bunnet versions built against Pydantic V2 (post-Pydantic V2 release)"},{"fix":"For upsert functionality with `replace_one`, you might need to implement a check-then-insert/replace logic or use `save()` on a new or existing document instance. For more complex scenarios, consider using `update_one` with `upsert=True` or a custom bulk write operation. Raising a feature request/PR to the Bunnet maintainers might also be an option.","message":"The `replace_one` method in Bunnet (and Beanie) does not natively support `upsert=True` as a direct parameter in the same way `update_one` does in PyMongo. Attempting to use it for an upsert might not behave as expected.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always pass the `database` object obtained from the client (e.g., `client.get_database('your_db_name')`) to `init_bunnet`.","message":"Initialization with `init_bunnet` requires a PyMongo `database` object, not the client. Passing the client directly will raise an error or lead to incorrect behavior.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Bunnet is synchronous. Use `find_one().run()` instead of `find_one_async()`. All operations are synchronous and do not require `await`.","cause":"Attempting to use an asynchronous method from BeanieODM (the async counterpart) on a Bunnet Document model.","error":"AttributeError: 'Product' object has no attribute 'find_one_async'"},{"fix":"Ensure `init_bunnet(database=client.get_database('your_db_name'), document_models=[YourDocumentClass])` is called with both a valid `pymongo.database.Database` instance and a list of your Document classes.","cause":"The `init_bunnet` function was called without a valid `database` object or `document_models` list, preventing proper collection initialization.","error":"pymongo.errors.CollectionInvalid: collection name cannot be empty"},{"fix":"First create an instance of your document (e.g., `my_product = Product(...)`), then call the method on the instance (`my_product.save()`).","cause":"Trying to call an instance method (`.save()`, `.insert()`, `.update()`) on the `Document` class itself, rather than on an instantiated document object.","error":"TypeError: Document.save() missing 1 required positional argument: 'self'"},{"fix":"Access collection-level settings via `Product.Settings` or define `class Settings:` within your `Document` class to configure collection options like `name`. Do not use `Document.Collection` directly.","cause":"Attempting to access a database collection property (e.g., `Product.Collection`) which was a pattern in older Beanie versions but is now deprecated or unsupported in Bunnet, replaced by `Settings`.","error":"TypeError: 'Collection' object is not callable"}]}