Core Concepts

The linode_api4 package, and the API V4, have a few ideas that will help you more quickly become proficient with their usage. This page assumes you’ve read the Getting Started guide, and know the basics of authentication already.

Pagination

The Linode API V4 loosely follows a RESTful design, and paginates results to responses for GETs to collections. This library handles pagination transparently, and does not load pages of data until they are required. This is handled by the PaginatedList class, which behaves similarly to a python list. For example:

linodes = client.linode.instances() # returns a PaginatedList of linodes

first_linode = linodes[0] # the first page is loaded automatically, this does
                          # not emit an API call

# you can also use the `first()` convenience function for this
first_linode = linodes.first()

last_linode = linodes[-1] # loads only the last page, if it hasn't been loaded yet
                          # this _will_ emit an API call if there were two or
                          # more pages of results.  If there was only one page,
                          # this does not emit an additional call

for current_linode in linodes: # iterate over all results, loading pages as necessary
    print(current_linode.label)

If you’re not concerned about performance, using a PaginatedList as a normal list should be fine. If your application is sensitive to performance concerns, be aware that iterating over a PaginatedList can cause the thread to wait as a synchronous request for additional data is made mid-iteration.

Filtering

Collections of objects in the API can be filtered to make their results more useful. For example, instead of having to do this filtering yourself on the full list, you can ask the API for all Linode Instances you own belonging to a certain group. This library implements filtering with a SQLAlchemy-like syntax, where a model’s attributes may be used in comparisons to generate filters. For example:

from linode_api4 import Instance

prod_linodes = client.linode.instances(Instance.group == "production")

Filters may be combined using boolean operators similar to SQLAlchemy:

# and_ and or_ can be imported from the linode package to combine filters
prod_or_staging = client.linode.instances(or_(Instance.group == "production",
                                                  Instance.group == "staging"))

# and_ isn't strictly necessary, as it's the default when passing multiple
# filters to a collection
prod_and_green = client.linode.instances(Instance.group == "production",
                                             Instance.label.contains("green"))

Filters are generally only applicable for the type of model you are querying, but can be combined to your heart’s content. For numeric fields, the standard numeric comparisons are accepted, and work as you’d expect. See Filtering Collections for full details.

Models

This library represents objects the API returns as “models.” Most methods of LinodeClient return models or lists of models, and all models behave in a similar manner.

Creating Models

In addition to looking up models from collections, you can simply import the model class and create it by ID.:

from linode_api4 import Instance

my_linode = Instance(client, 123)

All models take a LinodeClient as their first parameter, and their ID as the second. For derived models (models that belong to another model), the parent model’s ID is taken as a third argument to the constructor (i.e. to construct a Disk you pass a LinodeClient, the disk’s ID, then the parent Linode Instance’s ID).

Be aware that when creating a model this way, it is _not_ loaded from the API immediately. Models in this library are lazy-loaded, and will not be looked up until one of their attributes that is currently unknown is accessed.

In order to automatically populate a model for an existing Linode resource, consider using the LinodeClient.load method:

from linode_api4 import Instance, Disk

instance = client.load(Instance, 12345)
instance_disk = client.load(Disk, 123, instance.id)

Lazy Loading

If a model is created, but not yet retrieved from the API, its attributes will be unpopulated. As soon as an unpopulated attribute is accessed, an API call is emitted to retrieve that value (and the rest of the attributes in the model) from the API. For example:

my_linode.id # no API call emitted - this was set on creation
my_linode.label # API call emitted - entire object is loaded from response
my_linode.group # no API call emitted - this was loaded above

Note

When loading a model in this fashion, if the model does not exist in the API or you do not have access to it, an ApiError is raised. If you want to load a model in a more predictable manner, see LinodeClient.load

Volatile Attributes

Some attributes of models are marked volatile. A volatile attribute will become stale after a short time, and if accessed when its value is stale, will refresh itself (and the entire object) from the API to ensure the value is current.:

my_linode.boot()
my_linode.status # booting
time.sleep(20) # wait for my_linode.status to become stale
my_linode.status # running

Note

While it is often safe to loop on a volatile attribute, be aware that there is no guarantee that their value will ever change - be sure that any such loops have another exit condition to prevent your application from hanging if something you didn’t expect happens.

Updating and Deleting Models

Most models have some number of mutable attributes. Updating a model is as simple as assigning a new value to these attributes and then saving the model. Many models can also be deleted in a similar fashion.:

my_linode.label = "new-label"
my_linode.group = "new-group"
my_linode.save() # emits an API call to update label and group

my_linode.delete() # emits an API call to delete my_linode

Note

Saving a model may fail if the values you are attempting to save are invalid. If the values you are attempting to save are coming from an untrusted source, be sure to handle a potential ApiError raised by the API returning an unsuccessful response code.

When updating an attribute on a model, ensure that the model has been populated before any local changes have been made. Attempting to update an attribute and save a model before the model has been populated will result in no changes being applied.

Relationships

Many models are related to other models (for example a Linode Instance has disks, configs, volumes, backups, a region, etc). Related attributes are accessed like any other attribute on the model, and will emit an API call to retrieve the related models if necessary.:

len(my_linode.disks) # emits an API call to retrieve related disks
my_linode.disks[0] # no API call emitted - this is already loaded

my_linode.region.id # no API call emitted - IDs are already populated
my_linode.region.country # API call emitted - retrieves region object