This document describes the mechanisms that can be used to extend CommCare’s functionality. There are a number of legacy mechanisms that are used which are not described in this document. This document will focus on the use of pre-defined extension points to add functionality to CommCare.
Where to put custom code¶
The custom code for extending CommCare may be part of the main commcare-hq repository or it may have its own repository. In the case where it is in a separate repository the code may be ‘added’ to CommCare by cloning the custom repository into the extensions folder in the root of the CommCare source:
/commcare-hq /corehq /custom ... /extensions /custom_repo /custom_app1/models.py /custom_app2/models.py
The corehq/extensions package provides the utilities to register extension points and their implementations and to retrieve the results from all the registered implementations.
Create an extension point¶
from corehq import extensions @extensions.extension_point def get_things(arg1: int, domain: str, keyword: bool = False) -> List[str]: '''Docs for the extension point''' pass @extensions.extension_point def get_other_things(): '''Default implementation of ``get_other_things``. May be overridden by an extension''' return ["default1", "default2"]
- The extension point function is called if there are no registered extensions or none that match
- the call args.
Registering an extension point implementation¶
from xyz import get_things @get_things.extend() def some_things(arg1, domain, keyword=False): return ["thing2", "thing1"]
Extensions may also be limited to specific domains by passing the list of domains as a keyword argument (it must be a keyword argument). This is only supported if the extension point defines a domain argument.
from xyz import get_things @get_things.extend(domains=["cat", "hat"]) def custom_domain_things(arg1, domain, keyword=False): return ["thing3", "thing4"]
Calling an extension point¶
An extension point is called as a normal function. Results are returned as a list with any None values removed.
from xyz import get_things results = get_things(10, "seuss", True)
By default the results from calling an extension point are returned as a list where each element is the result from each implementation:
> get_things(10, "seuss", True) [["thing2", "thing1"], ["thing3", "thing4"]]
Results can also be converted to a flattened list or a single value by passing a ResultFormat enum when defining the extension point.
@extensions.extension_point(result_format=ResultFormat.FLATTEN) def get_things(...): pass > get_things(...) ["thing2", "thing1", "thing3", "thing4"]
This will return the first result that is not None. This will only call the extension point implementations until a value is found.
@extensions.extension_point(result_format=ResultFormat.FIRST) def get_things(...): pass > get_things(...) ["thing2", "thing1"]
List Extension Points¶
You can list existing extension points and their implementations by running the following management command:
python manage.py list_extension_points