Welcome to Motorturbine’s documentation!¶
Motorturbine is an adapted version of the Motorengine ORM. The main goals are proper asyncio integration as well as a way to have more control over safe updates. Many ORMs suffer from parallelism issues and one big part of this package is to introduce transactions with retry capabilities when updating the fields of a document.
Tutorial¶
Using Motorturbine is meant to be a streamlined experience, creating an environment where it’s the only needed connection to interact with any object in a database.
Connecting to the database¶
At first we need to establish a connection to the database.
Using Connection
’s connect()
all future operations will be made by utilising that connection.
Connection.connect(host='localhost', port=27017)
Creating a document¶
The next step after a global connection is established is to model your documents using the BaseDocument
class. Modeling is achieved by populating the documents attributes using the supplied Fields
from motorturbine import BaseDocument, fields
def class Person(BaseDocument):
name = fields.StringField(default='Nobody')
age = fields.IntField(required=True)
Working with documents¶
From here on out each document object can be considered like a typed object.
person1 = Person(name="Steve", age=25)
person2 = Person(age=44)
person2.age = 60
When all transformations are done objects can be inserted into the database by calling save()
.
Note
save()
is a coroutine function and therefore requires awaiting.
async def save_person(person):
await person.save()
Querying objects¶
The created collections (or document classes) can be queried by using one of the classmethodds get_object()
or get_objects()
. These methods will search the collection that is automatically created when inserting a new document. To specify the parameters it is possible to use one or multiple instances of QueryOperator
.
async def get_sixty_plus():
oldies = await Person.get_objects(age=Gte(60))
return oldies
In this example motorturbine.queryset.Gte
is used to look for all entries with Person.age >= 60.
Updating fields¶
Once everything is set up, instead of just setting values directly there is a fancier way to update your fields by utilising mongos inbuilt atomic update capabilities.
Values that are updated this way don’t need to match their old state since they just add to the state instead of completely changing it.
async def happy_birthday(person):
person.age = Inc(1)
await person.save()
In this example the motorturbine.updateset.Inc
operator is used to increase the persons age by one year.
For more information about updating see UpdateOperator
.
Reference¶
Documents¶
Create new documents by subclassing the base class.
BaseDocument¶
-
class
BaseDocument
(**kwargs)¶ The BaseDocument is used to create new Documents which can be used to model your data structures.
Simple example using a
StringField
and anIntField
:class ExampleDocument(BaseDocument): name = StringField(default='myname') number = IntField(default=0)
When instantiating a Document object it is possible to use keyword arguments to initialise its fields to the given values.
>>> doc = ExampleDocument(name='Changed My Name', number=15) >>> print(doc) <ExampleDocument name='Changed My Name' number=15> >>> await doc.save() >>> print(doc) <ExampleDocument id=ObjectId('$oid') name='Changed My Name' number=15>
Raises: FieldNotFound – On access of a non-existent field Caution
The id field is reserved and will be set after a successful save. The field has the same properties as when using an
ObjectIdField
. Will raise an exception if the field is set regardless.-
classmethod await
get_object
(**kwargs)¶ A find_one wrapper for
get_objects()
. Queries the collection for a single document. Will return None if there is no or more than one document.
-
classmethod await
get_objects
(**kwargs)¶ Queries the collection for multiple objects as defined by the supplied filters. For querying Motorturbine supplies its own functionality in form of
QueryOperator
.
-
await
get_reference
(field_name, collections=None)¶ When using
ReferenceField
this method allows loading the reference by the fields name. Returns None if the given field exists but is not aReferenceField
type.Parameters: - field_name (str) – The name of the ReferenceField
- collections (list) – optional (None) –
A list of
BaseDocument
classes. In case you allowed subclassing in aReferenceField
you can specify the additional document collections that will be searched if they are not the same as the specified documents type.
Raises: FieldNotFound – On access of a non-existent field
-
await
save
(limit=0)¶ Calling the save method will start a synchronisation process with the database. Every change that was made since the last synchronisation is considered specifically to only update based on the condition that no fields that changed were updated in the meantime. In case that any conflicting fields did update we make sure to pull these changes first and only then update them to avoid critical write errors.
If a document has not been saved before the ‘id’ field will be set automatically after the update is done.
Parameters: limit (int) – optional (0) – The maximum amount of tries before a save operation fails. Can be used as a way to catch problematic state or to probe if the current document has changed yet if set to 1. Raises: RetryLimitReached – Raised if limit is reached
-
to_json
()¶ Returns the entire document as a json dictionary.
-
classmethod await
Fields¶
Used to populate your own Documents. Allow to set defaults, unique indexes and other field specific parameters.
BaseField¶
-
class
BaseField
(*, default=None, required=False, unique=False)¶ The base class for any field. Used for connecting to the parent document and calling general methods for setting and validating values.
Parameters: - default – optional (None) – Defines a default value based on the field type.
- required (bool) – optional (False) – Defines if the fields value can be None.
- unique (bool) – optional (False) – Defines if the fields value has to be unique.
Raises: TypeMismatch – Trying to set a value with the wrong type
FloatField¶
-
class
FloatField
(*, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows a float type to be set as its value.
IntField¶
-
class
IntField
(*, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows an int type to be set as its value.
StringField¶
-
class
StringField
(*, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows a str type to be set as its value.
BooleanField¶
-
class
BooleanField
(*, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows an bool type to be set as its value.
ObjectIdField¶
-
class
ObjectIdField
(*, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows a
bson.ObjectId
to be set as its value.
DateTimeField¶
-
class
DateTimeField
(*, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field allows multiple types to be set as its value but will always parse them to a
datetime
object.- Accepted types:
- str - Any accepted by
dateutil.parser.parse()
(docs) - int - Unix timestamp
- float - Unix timestamp
datetime.date
datetime.datetime
- str - Any accepted by
Note
Make sure to always use UTC times when trying to insert times to avoid issues between timezones! For example use
datetime.utcnow()
instead ofdatetime.now()
ReferenceField¶
-
class
ReferenceField
(reference_doc, *, allow_subclass=False, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.objectid_field.ObjectIdField
This field allows another Document to be set as its value. A ReferenceField does not auto-insert other fields. Therefore make sure to insert them before you try to set them as a reference.
Parameters: - reference_doc (BaseDocument) – Sets the document type that will be checked when setting the reference.
- allow_subclass (bool) – optional (False) – Controls whether or not it should be possible to set instances of a subclass of the specified document as a reference.
DocumentField¶
-
class
DocumentField
(embed_doc, *, default=None, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field allows another Document to be set as its value. Any document inserted as an embedded field will be treated like an object inside of its parent. It enables to create more complex document trees than by just using
MapField
.Example usage:
class Identifier(BaseDocument): serial = fields.StringField() stamp = fields.DateTimeField() location = fields.StringField() class Part(BaseDocument): name = fields.StringField() ident = fields.DocumentField(Identifier) now = datetime.utcnow() ident = Identifier(serial='9X1-33D-52A', stamp=now, location='US') part = Part(name='Xerxes', ident=ident) await part.save()
In this example an Identifier is attached to each Part that is produced. It wouldn’t have been easily possibly to create this structure by using a
MapField
because the Identifier is built from more than one data types.Parameters: embed_doc (BaseDocument) – Sets the document type that will be checked when embedding an instance.
ListField¶
-
class
ListField
(sub_field, *, default=[], required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows a list type to be set as its value.
If an entire list is set instead of singular values each entry in the new list has to match the subfield that was set when initialising the field.
Parameters: sub_field (BaseField) – Sets the field type that will be used for the entires of the list.
MapField¶
-
class
MapField
(value_field, key_field=StringField(), *, default={}, required=False, unique=False)¶ Bases:
motorturbine.fields.base_field.BaseField
This field only allows a dict type to be set as its value.
If an entire dict is set instead of singular values each key-value pair in the new dict has to match the subfields that were set when initialising the field.
Parameters: value_field (BaseField) – Sets the field type that will be used for the values of the dict.
Querying¶
Operators that allow to create field specific, mongo-like queries.
QueryOperator¶
-
class
QueryOperator
(value, requires_sync=True)¶ QueryOperators can be used to automatically generate queries that are understood by mongo. Each of the operators can be used as defined in the mongo manual as they’re just a direct mapping. See
BaseDocument
to use it with querying methods likeget_objects()
.Note
Please note that because of the overlap in keywords all these classes are capitalised!
Eq¶
-
class
Eq
(value, requires_sync=True)¶ Checks for any value that is equal to the given value. Not using it is the default case and functionally the same as just leaving out a QueryOperator completely.
Example usage:
>>> await Document.get_objects(num=5) >>> await Document.get_objects(num=Eq(5))
Query:
>>> Eq(5)() {'$eq': 5}
Ne¶
-
class
Ne
(value, requires_sync=True)¶ Checks for any value that is not equal to the given value.
Example usage:
>>> await Document.get_objects(num=Ne(5))
Query:
>>> Ne(5)() {'$ne': 5}
Lt¶
-
class
Lt
(value, requires_sync=True)¶ Checks for any value that is lesser than the given value.
Example usage:
>>> await Document.get_objects(num=Lt(5))
Query:
>>> Lt(5)() {'$lt': 5}
Lte¶
-
class
Lte
(value, requires_sync=True)¶ Checks for any value that is lesser than or equal to the given value.
Example usage:
>>> await Document.get_objects(num=Lte(5))
Query:
>>> Lte(5)() {'$lte': 5}
Gt¶
-
class
Gt
(value, requires_sync=True)¶ Checks for any value that is greater than the given value.
Example usage:
>>> await Document.get_objects(num=Gt(5))
Query:
>>> Gt(5)() {'$gt': 5}
Gte¶
-
class
Gte
(value, requires_sync=True)¶ Checks for any value that is greater than or equal to the given value.
Example usage:
>>> await Document.get_objects(num=Gte(5))
Query:
>>> Gte(5)() {'$gte': 5}
Updating¶
The Updateset enables updating of fields by using atomic operators.
Note
Makes use of write_bulk to enable the usage of multiple update operators to compress all changes to just on save call on the user side.
UpdateOperator¶
-
class
UpdateOperator
(update)¶ UpdateOperators can be used to automatically generate update queries that are understood by mongo. Each of the operators can be used as defined in the mongo manual as they’re just a direct mapping.
Note
Please note that because of the overlap in keywords all these classes are capitalised!
Note
Makes use of write_bulk to enable the usage of multiple update operators to compress all changes to just on save call on the user side.
Set¶
-
class
Set
(update)¶ Is used to set the specified field to any given value. Not using it is the default case and functionally the same as just leaving out an UpdateOperator completely.
Example usage:
>>> doc.num = 5 >>> doc.num = Set(5)
Query:
>>> Set(5)() {'$set': 5}
Inc¶
Note
Like in mongo Inc can be used with positive and negative numbers. For continuity Dec can also be used and is used for implicit substraction.
-
class
Inc
(update)¶ Is used to modify a numeric value by a given amount.
Example usage:
>>> doc.num = Inc(5) >>> doc.num = Inc(-5)
Query:
>>> Inc(5)() {'$inc': 5}
-
class
Dec
(update)¶ Is used to decrease a numeric value.
Example usage:
>>> doc.num = Dec(5)
Query:
>>> Dec(5)() {'$inc': -5}
Max¶
-
class
Max
(update)¶ Update the field to the maximum of database and current value.
Example usage:
>>> doc.num = Max(5)
Query:
>>> Max(5)() {'$max': 5}
Min¶
-
class
Min
(update)¶ Update the field to the minimum of database and current value.
Example usage:
>>> doc.num = Min(5)
Query:
>>> Min(5)() {'$min': 5}
Mul¶
-
class
Mul
(update)¶ Is used to multipy a numeric value by a given amount.
Example usage:
>>> doc.num = Mul(5)
Query:
>>> Mul(5)() {'$mul': 5}
Push¶
-
class
Push
(update)¶ Is used to append a value to a list.
Example usage:
>>> doc.num_list = Push(5)
Query:
>>> Push(5)() {'$push': 5}
Connection¶
A singleton to enable a global connection that can be used by the documents.
Connection¶
-
class
Connection
¶ This singleton is used to connect motor to your database. When initialising your application call
Connection.connect()
and all subsequent operations on the database will be automatically done by the documents.-
classmethod
connect
(host='localhost', port=27017, database='motorturbine')¶ Connects motorturbine to your database
Parameters: - host (str) – optional (‘localhost’) –
- port (int) – optional (27017) –
- database (str) – optional (‘motorturbine’) –
-
classmethod
Errors¶
FieldExpected¶
-
class
FieldExpected
(received)¶ Is raised when a Document is created with an attribute that is not a
BaseField
.>>> raise FieldExpected(str) Expected instance of BaseField, got str!
Parameters: received – The received type
TypeMismatch¶
-
class
TypeMismatch
(expected, received)¶ Is raised when an incorrect type was supplied.
>>> raise TypeMismatch(int, str) Expected instance of int, got str!
Parameters: - expected – The expected type.
- received – The received type
FieldNotFound¶
-
class
FieldNotFound
(field_name, document)¶ Is raised when trying to access a property that isn’t present as a field.
>>> raise FieldNotFound(doc, 'attr') Field 'attr' was not found on object <ExampleDocument name='Changed My Name' number=15>.
Parameters: - field_name (str) – Name of the field
- document (BaseDocument) – The document that was being accessed.
RetryLimitReached¶
-
class
RetryLimitReached
(limit, document)¶ Is raised during the synchronisation process if the specified retry limit is reached.
>>> raise RetryLimitReached(10, doc) Reached the retry limit (10) while trying to save <ExampleDocument name='Changed My Name' number=15>.
Parameters: - limit (int) – The retry limit
- received (BaseDocument) – The document that couldn’t be synced