Interacting with Syncano¶
This tutorial will walk you through our ORM syntax and how to use it to make proper API calls.
Creating a Connection¶
In each example I’ll be assuming that you have configured connection to syncano:
>>> import syncano
>>> connection = syncano.connect(email='YOUR_EMAIL', password='YOUR_PASSWORD')
Accessing models¶
All models are defined in syncano.models.base but syncano simplifies access to them by attaching all of them directly to connection. Thus:
from syncano.models.base import Instance
and:
Instance = connection.Instance
are equivalent.
Creating objects¶
A model class represents a single Syncano API endpoint, and an instance of that class represents a particular record in this endpoint.
To create an object, instantiate it using keyword arguments to the model class, then call save() to save it to the Syncano API.
Here’s an example:
>>> instance = Instance(name='test-one', description='')
>>> instance.save()
This performs a POST request to Syncano API behind the scenes. Syncano doesn’t hit the API until you explicitly call save().
Note
To create and save an object in a single step, use the create() method. To create and save multiple objects in a single step, use the bulk_create() method.
Saving changes to objects¶
To save changes to an object that’s already in the Syncano API, use save(). Regarding our instance from previous example, this example changes its description and updates its record in the Syncano API:
>>> instance.description = 'new description'
>>> instance.save()
This performs a PUT request to Syncano API behind the scenes. Syncano doesn’t hit the API until you explicitly call save().
Note
To change and save an object in a single step, use the update() method.
Retrieving objects¶
To retrieve objects from Syncano API, construct a query via a Manager on your model class.
Each model has only one Manager, and it’s called please by default. Access it directly via the model class, like so:
>>> Instance.please
[<Instance: test>, <Instance: test-two>, '...(remaining elements truncated)...']
>>> i = Instance(name='Foo', description='Bar')
>>> i.please
Traceback:
...
AttributeError: Manager isn't accessible via Instance instances.
Note
Managers are accessible only via model classes, rather than from model instances, to enforce a separation between “table-level” operations and “record-level” operations.
Retrieving all objects¶
The simplest way to retrieve objects from a Syncano API is to get all of them. To do this, use the all() or list() method on a Manager:
>>> Instance.please
>>> Instance.please.all()
>>> Instance.please.list()
This performs a GET request to Syncano API list endpoint behind the scenes.
Manager is lazy¶
Manager is lazy – the act of creating a Manager doesn’t involve any API activity. You can stack Manager methods all day long, and Syncano won’t actually run the API call until the Manager is evaluated. Take a look at this example:
>>> query = Class.please.list('test-instance')
>>> query = query.limit(10)
>>> print(query)
Though this looks like two API calls, in fact it hits API only once, at the last line (print(query)). In general, the results of a Manager aren’t fetched from API until you “ask” for them.
Retrieving a single object¶
If you know there is only one object that matches your API call, you can use the get() method on a Manager which returns the object directly:
>>> instance = Instance.please.get('instance-name')
This performs a GET request to Syncano API details endpoint behind the scenes.
If there are no results that match the API call, get() will raise a SyncanoDoesNotExist exception. This exception is an attribute of the model class that the API call is being performed on - so in the code above, if there is no Instance object with a name equal “instance-name”, Syncano will raise Instance.DoesNotExist.
Removing a single object¶
The delete method, conveniently, is named delete(). This method immediately deletes the object and has no return value. Example:
>>> instance = Instance.please.get('test-one')
>>> instance.delete()
This performs a DELETE request to Syncano API details endpoint behind the scenes.
Limiting returned objects¶
Use a subset of Python’s array-slicing syntax to limit your Manager to a certain number of results.
For example, this returns the first 5 objects:
>>> Instance.please[:5]
This returns the sixth through tenth objects:
>>> Instance.please[5:10]
Negative indexing (i.e. Instance.please.all()[-1]) is not supported.
Note
If you don’t want to use array-slicing syntax there is a special manager method called limit().
Warning
Python’s array-slicing syntax is a expensive operation in context of API calls so using limit() is a recommended way.
Lookups that span relationships¶
Syncano API has nested architecture so in some cases there will be a need to provide a few additional arguments to resolve endpoint URL.
For example ApiKey is related to Instance and its URL patter looks like this:
/v1/instances/{instance_name}/api_keys/{id}
This example will not work:
>>> ApiKey.please.list()
Traceback:
...
SyncanoValueError: Request property "instance_name" is required.
So how to fix that? We need to provide instance_name as an argument to list() method:
>>> ApiKey.please.list(instance_name='test-one')
[<ApiKey 1>...]
This performs a GET request to /v1/instances/test-one/api_keys/.
Note
Additional request properties are resolved in order as they occurred in URL pattern. So if you have pattern like this /v1/{a}/{b}/{c}/ list() method can be invoked like any other Python function i.e list('a', 'b', 'c') or list('a', c='c', b='b').
Backward relations¶
For example Instance has related ApiKey model so all Instance objects will have backward relation to list of ApiKey‘s:
>>> instance = Instance.please.get('test-one')
>>> instance.api_keys.list()
[<ApiKey 1>...]
>>> instance.api_keys.get(id=1)
<ApiKey 1>
Note
Related objects do not require additional request properties passed to list() method.
Falling back to raw JSON¶
If you find yourself needing to work on raw JSON data instead of Python objects just use raw() method:
>>> Instance.please.list()
[<Instance: test>, <Instance: test-two>, '...(remaining elements truncated)...']
>>> Instance.please.list().raw()
[{u'name': u'test-one'...} ...]
>>> Instance.please.list().limit(1).raw()
[{u'name': u'test-one'...}]
>>> Instance.please.raw().get('test-one')
{u'name': u'test-one'...}
Environmental variables¶
Some settings can be overwritten via environmental variables e.g:
$ export SYNCANO_LOGLEVEL=DEBUG
$ export SYNCANO_APIROOT='https://127.0.0.1/'
$ export SYNCANO_EMAIL=admin@syncano.com
$ export SYNCANO_PASSWORD=dummy
$ export SYNCANO_APIKEY=dummy123
$ export SYNCANO_INSTANCE=test
Warning
DEBUG loglevel will disable SSL cert check.