Metadata-Version: 2.1
Name: pbumongo
Version: 1.0.1
Summary: Basic MongoDB wrapper for object-oriented collection handling
Home-page: https://github.com/ilfrich/pbu-mongo
Author: Peter Ilfrich
Author-email: das-peter@gmx.de
License: Apache-2.0
Description: # Python Basic Utilities - Mongo `pbumongo`
        
        Available on [PyPi](https://pypi.org/project/pbumongo/)
        
        **Table of Contents**
        
        1. [Installation](#installation)
        2. [Usage](#usage)
        3. [Classes](#classes)
            1. [AbstractMongoStore](#abstractmongostore) - abstract class for handling MongoDB collection access
            2. [AbstractMongoDocument](#abstractmongodocument) - abstract class for wrapping MongoDB BSON documents
        
        
        ## Installation
        
        Install via pip:
        
        ```bash
        pip install pbumongo
        ```
        
        ## Usage
        
        It is good practice associating a sub-class of `AbstractMongoDocument` with a sub-class of `AbstractMongoStore`. This is
        done through the `deserialised_class` parameter in the `super()` constructor call of the store class. Any method for
        querying documents will use that class to deserialise the BSON document into the provided class, which should extend
        `AbstractMongoDocument`.
        
        Example: let's say we want to implement access to a collection containing user documents. We'll define a class `User`
        that extends `AbstractMongoDocument` and a class `UserStore` that extends `AbstractMongoStore`.
        
        ```python
        # main imports
        from pbumongo import AbstractMongoDocument, AbstractMongoStore
        # supporting imports
        import crypt
        from typing import List, Optional
        from time import time
        
        
        # this is an example of a minimum viable class
        class User(AbstractMongoDocument):
            def __init__(self):
                super().__init__()
                # define attributes with meaningful defaults
                self.username: str = None
                self.password: str = None
                self.permissions: List[str] = []
                self.last_login: int = 0
        
            def get_attribute_mapping(self) -> dict:
                # the values are what is used inside MongoDB documents
                return {
                    "username": "username",
                    "password": "password",
                    "permissions": "permissions",
                    "last_login": "lastLogin"
                }
        
            @staticmethod
            def from_json(json: dict):
                user = User()
                user.extract_system_fields(json)
                return user
        
        
        class UserStore(AbstractMongoStore):
            def __init__(self, mongo_url, mongo_db, collection_name):
                super().__init__(mongo_url, mongo_db, collection_name, deserialised_class=User, data_model_version=1)
        
            def login(self, username, password) -> Optional[User]:
                # encrypt the password!
                pw_encrypted = crypt.crypt(password, crypt.METHOD_MD5)
                user: Optional[User] = self.query_one({"username": username, "password": pw_encrypted})
                if user is not None:
                    # update last_login attribute and save it in database as well
                    user.last_login = round(time())
                    self.update_one(AbstractMongoStore.id_query(user.id),
                                    AbstractMongoStore.set_update("lastLogin", user.last_login))
                return user
        
            def create_user(self, username, password) -> User:
                # check if this user already exists
                existing = self.query_one({"username": username})
                if existing is not None:
                    raise ValueError(f"User with username '{username}' already exists.")
                # create new user object
                user = User()
                user.username = username
                user.password = crypt.crypt(password, crypt.METHOD_MD5)
                # store in database and return document
                user_id = self.create(user)
                return self.get(user_id)
        ```
        
        To use these classes in your application, you can use the MongoConnection helper or create the `UserStore` class
        instance directly. The `MongoConnection` helper is useful, when you have a lot of collections and don't want to repeat
        the mongo connection URL and DB name for every constructor.
        
        ```python
        from pbumongo import MongoConnection
        from mypackage import UserStore  # see implementation above
        
        con = MongoConnection("mongodb://localhost:27017", "myDbName")
        user_store = con.create_store(store_class=UserStore, collection_name="users")
        
        user = user_store.login(username="admin", password="mypassword")
        ```
        
        ## Classes
        
        ### AbstractMongoStore
        
        This is an abstract class and cannot be instantiated directly. Instead, define a class that extends this class.
        
        **Constructor**
        
        `__init__(mongo_url, mongo_db, collection_name, deserialised_class, data_model_version=1)`
        
        - `mongo_url` - this is the Mongo connection URL containing the host, port and optional username, password
        - `mongo_db` - this is the Mongo DB name - the one you provide when using `use <dbname>` on the Mongo shell
        - `collection_name` - the name of the collection - e.g. `myCollection` for `db.myCollection.find({})` on the Mongo shell
        - `deserialised_class` - used for all the query methods to deserialise the BSON document into a class with attributes
          for easier access
        - `data_model_version` - a number that can be used for database migration as an app develops over time
        
        **Methods**
        
        - `get(doc_id: str)` - fetches a single document with a matching `doc_id == document["_id"]`
        - `get_all()` - fetches the entire collection content and deserialises every document. Careful, this is not an iterator,
          but returns a `list` of all the documents and can consume quite a bit of compute and memory.
        - `create(document)` - creates a new document and returns the `_id` of the newly created BSON document as string. The
          `document` can be either `dict` or an instance of the `deserialised_class` provided in the `super().__init(..)` call.
          - Since version 1.0.1 a new parameter is available `create(document, return_doc=True)` which will return the entire
            document/object instead of just the `_id` of the newly created document. 
        - `query_one(query: dict)` - fetches a single document and deserialises it or returns `None` if no document can be found
        - `query(query: dict, sorting, paging)` - fetches multiple documents and deserialises them. `sorting` can be an
          attribute name (as provided in the BSON) or a dictionary with the sort order. `paging` is an instance of
          `pbumongo.PagingInformation`.
        - `update_one(query: dict, update: dict)` - proxies the `db.collection.updateOne(..)` function from the Mongo shell
        - `update(query:, update: dict` - same as `update_one`, but will update multiple documents, if the query matches
        - `update_full(document)` - shortcut for updating the entire document with an updated version, the query will be
          constructed from the `id`/`_id` provided by the `document`.
        - `delete(doc_id)` - deletes a single document with the provided document ID
        - `delete_many(query: dict)` - deletes multiple documents matching the query.
        
        **Static Methods**
        
        - `AbstractMongoStore.id_query(string_id: str)` - creates a query `{ "_id": ObjectId(string_id) }`, which can be used to
          query the database
        - `AbstractMongoStore.set_update(keys, values)` - creates a `$set` update statement. If only a single attribute is 
          updated, you can pass them directly as parameters, e.g. updating a key `"checked": True`, can be done by 
          `.set_update("checked", True)`. If you update multiple attributes provide them as list in the matching order.
        - `AbstractMongoStore.unset_update(keys)` - creates an `$unset` update statement with the attributes listed as `keys`.
          Similarly to `.set_update`, you can provide a single key without a list for ease of use.
          
        ### AbstractMongoDocument
        
        This is an abstract class and cannot be instantiated directly. Instead, define a class that extends this class.
        
        **Constructor**
        
        `__init__(doc_id=None, data_model_version=None)`
        
        The parameters are entirely optional. Generally it is recommended to use the static method `from_json(json: dict)` to 
        create BSON documents you've loaded from the database instead of calling the constructor. For new documents, you would
        not provide the `_id` as the store class handles that.
        
        **Methods**
        
        For methods and static methods please see the documentation of `JsonDocument` from `pbu`. `AbstractMongoDocument` 
        extends that class.
        
Platform: UNKNOWN
Description-Content-Type: text/markdown
