Documents

A key role in aiocouch takes the Document class. Every data send and retrieved from the server is represented by an instance of that class. There are no other ways in aiocouch to interact with documents.

Getting a Document instance

While the constructor can be used to get an instance representing a specific document, the canonical way is the usage of member functions of instances of the Database class.

butterfly_doc = await database["butterfly"]
wolpertinger = await database.get("wolpertinger")

These methods create a Document and fetch the data from the server. For some cases, though, a precise control other the performed requests are required. The above code snippet is equivalent to this:

butterfly_doc = Document(database, "butterly")
await butterfly_doc.fetch()

Creating new Documents

The creation of a new document on the server consists of three steps. First, you need a local document handle, i.e., an Document instance. Then you set the contents of the document. And finally, the local document is saved to the server.

# get an Document instance
doc = await database.create("new_doc")

# set the document content
doc["name"] = "The new document"

# actually perform the request to save the document on the server
await doc.save()

Modify existing documents

The modification of an existing document works very similarly to the creation. Retrieving the document, updating its contents, and finally saving the modified data to the server.

# get an Document instance
doc = await database["existing_doc"]

# update the document content
doc["name"] = "The modified document"

# actually perform the request to save the modification to the server
await doc.save()

Using Async Context Managers

To simplify the process of retrieving a document from remote server (or creating a new one if it didn’t exist before), modifying it, and saving changes on remote server, you can also use asynchronous context managers.

Using context managers saves you from having to manually perform a lot of these operations as the context managers handle these operations for you automatically.

aiocouch provides async context managers for both Document and SecurityDocument.

Document Context Manager Example

from aiocouch import CouchDB
from aiocouch.document import Document

async with CouchDB(SERVER_URL, USER, PASSWORD) as client:
    # Create database on remote server (fetching it if it already exists)
    my_database = await client.create("my_database", exists_ok=True)

    # If document exists, it's fetched from the remote server
    async with Document(my_database, "secret_agents") as document:
        # Changes are made locally
        document["name"] = "James Bond"
        document["code"] = "007"
    # Upon exit from above context manager, document is saved remotely

    # Display the newly created document after fetching from remote server
    document = await my_database["secret_agents"]
    print(document)

Warning

Uncaught exceptions inside the async with block will prevent your document changes from being saved to the remote server.

Security Document Context Manager Example

Similarly, you can also use Security Document context manager to add or remove admins or members from a CouchDB database

from aiocouch import CouchDB
from aiocouch.document import Document

async with CouchDB(SERVER_URL, USER, PASSWORD) as client:
    # Create database on remote server (fetching it if it already exists)
    my_database = await client.create("my_database", exists_ok=True)

    async with SecurityDocument(my_database) as security_doc:
        # Give user 'bond' member access to 'my_database' database
        security_doc.add_member("bond")
        # Give user 'fleming' admin access to 'my_database' database
        security_doc.add_admin("fleming")
    # Upon exit from above context manager, document is saved remotely

    # Display the recent changes made to security document
    security_doc = await my_database.security()
    print(security_doc)

Warning

Uncaught exceptions inside the async with block will prevent your security document changes from being saved to the remote server.

Conflict handling

Whenever, two or more different Document instances want to save the same document on the server, a ConflictError can occur. To cope with conflicts, there are a set of different strategies, which can be used.

One trivial solution is to simply ignore conflicts.This is a viable strategy if only the existance of the document matters.

with contextlib.suppress(aiocouch.ConflictError):
    await doc.save()

Another straight-forward solution is to override the contents of the existing document. Though, this example code isn’t a complete solution either, as the second call to save() might raise a ConflictError again.

try:
    await doc.save()
except aiocouch.ConflictError:
    info = await doc.info()
    doc.rev = info["rev"]
    await doc.save()

Other use cases may require a more sophisticated merging of documents. However, there isn’t a generic solution to such an approach. Thus, we forego to show example code here.

Reference

class aiocouch.document.Document(database, id, data=None)

A local representation for the referenced CouchDB document

An instance of this class represents a local copy of the document data on the server. This class behaves like a dict containing the document data and allows to fetch() and save() documents. For details about the dict-like interface, please refer to the Python manual.

Constructing an instance of this class does not cause any network requests.

Variables

id – the id of the document

Parameters
  • database (Database) – The database of the document

  • id (str) – the id of the document

  • data (Optional[Dict[str, Any]]) – the initial data used to set the body of the document

attachment(id)

Returns the attachment object

The attachment object is returned, but this method doesn’t actually fetch any data from the server. Use fetch() and save(), respectively.

Parameters

id (str) – the id of the attachment

Return type

Attachment

Returns

Returns the attachment object

await copy(new_id)

Create a copy of the document on the server

Creates a new document with the data currently stored on the server.

Note

This method uses the COPY /{db}/{docid} endpoint.

If you need to know the rev of the created document, use the Etag header entry.

Parameters

new_id (str) – the id of the new document

Return type

HTTPResponse

Returns

If the request succeeded, returns the HTTPResponse instance.

property data: Optional[Dict[str, Any]]

Returns the document as a dict

If exists() is False, this function returns None.

This method does not perform a network request.

Returns

Returns the data of the document or None

await delete(discard_changes=False)

Marks the document as deleted on the server

Calling this method deletes the local data and marks document as deleted on the server. Afterwards, the instance can be filled with new data and call save() again.

Note

This method uses the DELETE /{db}/{docid} endpoint.

If you want to remove the data from the server, you’d need to use the _purge endpoint instead.

Raises
Return type

HTTPResponse

Returns

If the request succeeded, returns the HTTPResponse instance.

property exists: bool

Denotes whether the document exists

A document exists, if an existing was fetch() ed from the server and retrieved data doesn’t contain the _deleted field. Or a new document was saved using save().

This method does not perform a network request.

Returns

True if the document exists, False overwise

await fetch(discard_changes=False, *, rev=None)

Retrieves the document data from the server

Fetching the document will retrieve the data from the server using a network request and update the local data.

Raises
Parameters
  • discard_changes (bool) – If set to True, the local data object will the overridden with the retrieved content. If the local data was changed, no exception will be raised.

  • rev (Optional[str]) – The requested rev of the document. The requested rev might not or not anymore exist on the connected server.

Return type

None

await info()

Returns a short information about the document.

This method sends a request to the server to retrieve the current status.

Raises

NotFoundError – if the document does not exist on the server

Return type

Dict[str, Any]

Returns

A dict containing the id and revision of the document on the server

property json: Dict[str, Any]

Returns the document content as a JSON-like dict

In particular, all CouchDB-internal document keys will be omitted, e.g., _id, _rev If exists() is False, this function returns an empty dict.

This method does not perform a network request.

property rev: Optional[str]

Allows to set and get the local revision

If the local document wasn’t fetched or saved, this is None.

await save()

Saves the current state to the CouchDB server

Only sends a request, if the local state has been changed since the retrieval of the document data.

Raises

ConflictError – if the local revision is different from the server. See Conflict handling.

Return type

Optional[HTTPResponse]

Returns

If a successful request was made, returns the HTTPResponse instance.

class aiocouch.remote.HTTPResponse(resp)

Represents an HTTP response from the CouchDB server.

property etag: Optional[str]

Convenient property to access the ETag header in a usable format

headers: Dict[str, str]

The HTTP headers of the response

status: int

The HTTP response status, usually 200, 201 or 202