Storage

Introduction

Browsers have various mechanisms to store data. localStorage and IndexedDB are two such mechanisms. These are particularly useful for storing data offline.

The anvil_extras storage module provides wrappers around both these storage mechanisms in a convenient dictionary like API.

In order to store data you’ll need a store object. You can import the default store objects local_storage or indexed_db. Alternatively create your own store object using the classmethod create_store(store_name).

NB: when working in the IDE the app is running in an IFrame and the storage objects may not be available. This can be fixed by changing your browser settings. Turning the shields down in Brave or making sure not to block third party cookies in Chrome should fix this.

Which to chose?

If you have small amounts of data which can be converted to JSON - use local_storage.
If you have more data which can be converted to JSON (also bytes) - use indexed_db.

datetime and date objects are also supported. If you want to store anything else you’ll need to convert it to something JSONable first.

Usage Examples

Store user preference

from anvil_extras.storage import local_storage

class UserPreferences(UserPreferencesTemplate):
    def __init__(self, **properties):
        self.init_components(**properties)

    def dark_mode_checkbox_change(self, **event_args):
        local_storage['dark_mode'] = self.dark_mode_checkbox.checked

Change the theme at startup

## inside a startup module
from anvil_extras.storage import local_storage

if local_storage.get('dark_mode') is not None:
    # set the app theme to dark
    ...

Create an offline todo app

from anvil_extras.storage import indexed_db
from anvil_extras.uuid import uuid4

todo_store = indexed_db.create_store('todos')
# create_store() is a classmethod that takes a store_name
# it will create another store object inside the browsers IndexedDB
# or return the store object if it already exists
# the todo_store acts as dictionary like object

class TodoPage(TodoPageTemplate):
    def __init__(self, **properties):
        self.init_components(**properties)
        self.todo_panel.items = list(todo_store.values())

    def save_todo_btn_click(self, **event_args):
        if not self.todo_input.text:
            return
        id = str(uuid4())
        todo = {"id": id, "todo": self.todo_input.text, "completed": False}
        todo_store[id] = todo
        self.todo_panel.items = self.todo_panel.items + [todo]
        self.todo_input.text = ""

API

class StorageWrapper
class IndexedDBWrapper
class LocalStorageWrapper

both indexed_db and local_storage are instances of the dictionary like classes IndexedDBWrapper and LocalStorageWrapper respectively.

classmethod create_store(name)

Create a store object. e.g. todo_store = indexed_db.create_store('todos'). This will create a new store inside the browser’s IndexedDB and return an IndexedDBWrapper instance. The indexed_db object is equivalent to indexed_db.create_store('default'). To explore this further, open up devtools and find IndexedDB in the Application tab. Since create_store is a classmethod you can also do todo_store = IndexedDBWrapper.create_store('todos').

is_available()

Check if the storage object is supported. Returns a boolean.

list(store)

Return a list of all the keys used in the store.

len(store)

Return the number of items in store.

store[key]

Return the value of store with key key. Raises a KeyError if key is not in store.

store[key] = value

Set store[key] to value. If the value is not a JSONable data type it may be stored incorrectly. If storing bytes objects it is best to use the indexed_db store. datetime and date objects are also supported.

del store[key]

Remove store[key] from store.

key in store

Return True if store has a key key, else False.

iter(store)

Return an iterator over the keys of the store. This is a shortcut for iter(store.keys()).

clear()

Remove all items from the store.

get(key[, default])

Return the value for key if key is in store, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

items()

Return an iterator of the store’s (key, value) pairs.

keys()

Return an iterator of the store’s keys.

pop(key[, default])

If key is in store, remove it and return its value, else return default. If default is not given, it defaults to None, so that this method never raises a KeyError.

store(key, value)

Equivalent to store[key] = value.

update([other])

Update the store with the key/value pairs from other, overwriting existing keys. Return None.

update() accepts either a dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, store is then updated with those key/value pairs: store.update(red=1, blue=2).

values()

Return an iterator of the store’s values.