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()
andsave()
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
- 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()
andsave()
, respectively.- Parameters
id (
str
) – the id of the attachment- Return type
- 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
- Returns
If the request succeeded, returns the
HTTPResponse
instance.
- property data: Optional[Dict[str, Any]]¶
Returns the document as a dict
If
exists()
isFalse
, this function returnsNone
.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
ConflictError – if the local data has changed without saving
ConflictError – if the local revision is different from the server. See Conflict handling.
- Return type
- 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 usingsave()
.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
ConflictError – if the local data has changed without saving
BadRequestError – if the given rev is invalid or missing
- 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
- 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
- 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
Ifexists()
isFalse
, 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
- Returns
If a successful request was made, returns the
HTTPResponse
instance.
- class aiocouch.remote.HTTPResponse(resp)¶
Represents an HTTP response from the CouchDB server.