Skip to content

Commit 9d4783d

Browse files
author
Daniel Kopka
committed
[SYNPYTH-8] first big draft
1 parent 6a84563 commit 9d4783d

File tree

4 files changed

+189
-16
lines changed

4 files changed

+189
-16
lines changed

docs/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,4 @@
106106
# texinfo_no_detailmenu = False
107107

108108
autodoc_member_order = 'bysource'
109+
highlight_language = 'python'

docs/source/interacting.rst

Lines changed: 186 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,212 @@ This tutorial will walk you through our ORM syntax and how to use it to make pro
1010
Creating a Connection
1111
---------------------
1212

13-
In each example I'll be assuming that you have configured and open connection to `syncano`::
13+
In each example I'll be assuming that you have configured connection to `syncano`::
1414

1515
>>> import syncano
1616
>>> connection = syncano.connect(email='YOUR_EMAIL', password='YOUR_PASSWORD')
1717

1818

19-
Getting Existing Objects
20-
------------------------
19+
Accessing models
20+
----------------
2121

22+
All models are defined in :mod:`syncano.models.base` but ``syncano`` simplifies access to them
23+
by attaching all of them directly to ``connection``. Thus::
2224

23-
Creating New Objects
24-
--------------------
25+
from syncano.models.base import Instance
2526

27+
and::
2628

27-
Updating Existing Object
28-
------------------------
29+
Instance = connection.Instance
2930

31+
are equivalent.
3032

31-
Deleting Existing Object
32-
------------------------
33+
Creating objects
34+
----------------
35+
36+
A model class represents a single ``Syncano API`` endpoint,
37+
and an instance of that class represents a particular record in this endpoint.
38+
39+
To create an object, instantiate it using **keyword arguments** to the model class,
40+
then call :meth:`~syncano.models.base.Model.save` to save it to the ``Syncano API``.
41+
42+
Here’s an example::
43+
44+
>>> instance = Instance(name='test-one', description='')
45+
>>> instance.save()
46+
47+
This performs a **POST** request to ``Syncano API`` behind the scenes.
48+
Syncano doesn’t hit the API until you explicitly call :meth:`~syncano.models.base.Model.save`.
3349

50+
.. note::
51+
To create and save **an object** in a single step, use the :meth:`~syncano.models.manager.Manager.create` method.
52+
To create and save **multiple objects** in a single step, use the :meth:`~syncano.models.manager.Manager.bulk_create` method.
3453

35-
Relations
36-
---------
3754

55+
Saving changes to objects
56+
-------------------------
3857

39-
Getting a raw JSON
58+
To save changes to an object that’s already in the ``Syncano API``, use :meth:`~syncano.models.base.Model.save`.
59+
Regarding our **instance** from previous example,
60+
this example changes its description and updates its record in the ``Syncano API``::
61+
62+
>>> instance.description = 'new description'
63+
>>> instance.save()
64+
65+
This performs a **PUT** request to ``Syncano API`` behind the scenes.
66+
Syncano doesn’t hit the API until you explicitly call :meth:`~syncano.models.base.Model.save`.
67+
68+
.. note::
69+
To change and save **an object** in a single step, use the :meth:`~syncano.models.manager.Manager.update` method.
70+
71+
72+
Retrieving objects
4073
------------------
4174

75+
To retrieve objects from ``Syncano API``, construct a query via a :class:`~syncano.models.manager.Manager` on your model class.
76+
77+
Each model has only one :class:`~syncano.models.manager.Manager`, and it’s called **please** by default.
78+
Access it directly via the model class, like so::
79+
80+
>>> Instance.please
81+
[<Instance: test>, <Instance: test-two>, '...(remaining elements truncated)...']
82+
>>> i = Instance(name='Foo', description='Bar')
83+
>>> i.please
84+
Traceback:
85+
...
86+
AttributeError: Manager isn't accessible via Instance instances.
87+
88+
.. note::
89+
**Managers** are accessible only via model classes, rather than from model instances,
90+
to enforce a separation between “table-level” operations and “record-level” operations.
91+
92+
93+
Retrieving all objects
94+
----------------------
95+
96+
The simplest way to retrieve objects from a ``Syncano API`` is to get all of them.
97+
To do this, use the :meth:`~syncano.models.manager.Manager.all` or :meth:`~syncano.models.manager.Manager.list`
98+
method on a :class:`~syncano.models.manager.Manager`::
99+
100+
>>> Instance.please
101+
>>> Instance.please.all()
102+
>>> Instance.please.list()
103+
104+
This performs a **GET** request to ``Syncano API`` list endpoint behind the scenes.
105+
106+
Manager is lazy
107+
---------------
108+
109+
:class:`~syncano.models.manager.Manager` is lazy – the act of creating a **Manager** doesn’t involve any API activity.
110+
You can stack filters Manager methods all day long, and Syncano won’t actually run the API call until the **Manager** is evaluated.
111+
Take a look at this example::
112+
113+
>>> query = Class.please.list('test-instance')
114+
>>> query = query.limit(10)
115+
>>> print(query)
116+
117+
Though this looks like two API calls, in fact it hits API only once, at the last line (``print(query)``).
118+
In general, the results of a :class:`~syncano.models.manager.Manager` aren’t fetched from API until you “ask” for them.
119+
120+
121+
Retrieving a single object
122+
--------------------------
123+
124+
If you know there is only one object that matches your API call,
125+
you can use the :meth:`~syncano.models.manager.Manager.get` method on a :class:`~syncano.models.manager.Manager`
126+
which returns the object directly::
127+
128+
>>> instance = Instance.please.get('instance-name')
129+
130+
This performs a **GET** request to ``Syncano API`` details endpoint behind the scenes.
131+
132+
If there are no results that match the API call, :meth:`~syncano.models.manager.Manager.get`
133+
will raise a :class:`~syncano.exceptions.SyncanoDoesNotExist` exception.
134+
This exception is an attribute of the model class that the API call is being performed on - so in the code above,
135+
if there is no **Instance** object with a name equal "instance-name", Syncano will raise **Instance.DoesNotExist**.
136+
137+
.. note::
138+
To have more RESTful like method names there is :meth:`~syncano.models.manager.Manager.detail`
139+
alias for :meth:`~syncano.models.manager.Manager.get` method.
140+
141+
142+
Removing a single object
143+
------------------------
144+
145+
The delete method, conveniently, is named :meth:`~syncano.models.base.Model.delete`.
146+
This method immediately deletes the object and has no return value.
147+
Example::
148+
149+
>>> instance = Instance.please.get('test-one')
150+
>>> instance.delete()
151+
152+
This performs a **DELETE** request to ``Syncano API`` details endpoint behind the scenes.
153+
154+
155+
Limiting returned objects
156+
-------------------------
157+
158+
Use a subset of Python’s array-slicing syntax to limit your
159+
:class:`~syncano.models.manager.Manager` to a certain number of results.
160+
161+
For example, this returns the first 5 objects::
162+
163+
>>> Instance.please[:5]
164+
165+
This returns the sixth through tenth objects::
166+
167+
>>> Instance.please[5:10]
168+
169+
Negative indexing (i.e. **Instance.please.all()[-1]**) is not supported.
170+
171+
.. note::
172+
If you don't want to use array-slicing syntax there
173+
is a special manager method called :meth:`~syncano.models.manager.Manager.limit`.
174+
175+
176+
.. warning::
177+
Python’s array-slicing syntax is a expensive operation in context of API calls so using
178+
:meth:`~syncano.models.manager.Manager.limit` is a recommended way.
179+
180+
181+
Lookups that span relationships
182+
-------------------------------
183+
184+
185+
Related objects
186+
---------------
187+
188+
189+
Falling back to raw JSON
190+
------------------------
191+
192+
If you find yourself needing to work on raw JSON data instead of Python objects just use
193+
:meth:`~syncano.models.manager.Manager.raw` method::
194+
195+
>>> Instance.please.list()
196+
[<Instance: test>, <Instance: test-two>, '...(remaining elements truncated)...']
197+
198+
>>> Instance.please.list().raw()
199+
[{u'name': u'test-one'...} ...]
200+
201+
>>> Instance.please.raw().get('test-one')
202+
{u'name': u'test-one'...}
42203

43-
Connection juggling
44-
-------------------
45204

46205

47206
Environmental variables
48207
-----------------------
49208

209+
Some settings can be overwritten via environmental variables e.g:
210+
211+
.. code-block:: bash
212+
213+
$ export SYNCANO_LOGLEVEL=DEBUG
214+
$ export SYNCANO_APIROOT='https://127.0.0.1/'
215+
$ export SYNCANO_EMAIL=admin@syncano.com
216+
$ export SYNCANO_PASSWORD=dummy
217+
$ export SYNCANO_APIKEY=dummy123
218+
$ export SYNCANO_INSTANCE=test
219+
220+
.. warning::
221+
**DEBUG** loglevel will **disbale** SSL cert check.

syncano/models/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ class Schedule(Model):
389389
links = fields.HyperlinkedField(links=LINKS)
390390

391391
class Meta:
392-
parent = CodeBox
392+
parent = Instance
393393
endpoints = {
394394
'detail': {
395395
'methods': ['get', 'delete'],

syncano/models/manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def __init__(self, manager):
3232
def __get__(self, instance, owner=None):
3333
if instance is not None:
3434
raise AttributeError("Manager isn't accessible via {0} instances.".format(owner.__name__))
35-
return self.manager
35+
return self.manager.all()
3636

3737

3838
class RelatedManagerDescriptor(object):

0 commit comments

Comments
 (0)