Custom Data
Overview #
The Canvas SDK provides two techniques for storing custom data in your plugins, allowing you to define fully structured data models with relationships among entities, or create flexible key-value stores:
- CustomModels - Build your own data model or expand the Canvas data model by adding fully structured tables with typed fields, relationships, and indexes
- AttributeHubs - Store arbitrary key-value pairs and JSON data independently of the Canvas data model
Each technique serves different use cases and provides different levels of structure and type safety. Both techniques may be used together.
When to Use Each Technique #
CustomModels #
Use this when you need structured, typed data with relationships and normalized data. CustomModels can also extend existing SDK models (like Patient or Staff) with custom fields via OneToOneField, ForeignKey, and ManyToManyField.
Best for:
- Structured data with a stable, known schema
- Custom fields on existing SDK models (e.g., provider preferences, patient flags)
- Relationships between entities (foreign keys, join tables)
- Data requiring compound filtering, sorting, or aggregation
- Data consumed by reports or analytics
Example use cases:
- Provider specialties and certifications
- Adding practice-specific fields to patients or staff
- Linking
StafftoNotecreating asupervising_providerassociation - Custom workflows and forms
- Integration-specific data structures
- Practice-specific business operation concepts and logic
Learn more about CustomModels →
AttributeHubs #
AttributeHubs provide a key/value and document store free from the burden of defining any schema or linking to Canvas models. They are for storing irregular or unstructured information that doesn’t have a natural home. Whereas CustomModels build upon the Canvas data model, AttributeHubs allow easy, standalone persistence of information. Use this when you need to store data that doesn’t naturally belong to any existing or imagined model.
Best for:
- Cross-cutting state that spans multiple models (sync cursors, external IDs)
- One-off or small-collection configuration and state
- Data with no natural schema (varying fields per record)
- External system state tracking
Example use cases:
- API synchronization state
- External system identifiers
- Plugin configuration and feature flags
Learn more about AttributeHubs →
For help choosing between these techniques, see Design Considerations. For details on how multiple plugins can share a namespace using these keys, see the Sharing Data guide. For managing API tokens and other sensitive configuration, see Managing Secrets.
Caching #
If your use case represents transient data that should expire via TTL, use the Caching API instead of the Custom Data features.
Data Privacy and Plugin Isolation #
All custom data created by a plugin — whether using CustomModels or AttributeHubs — is scoped to a namespace. This isolation ensures that plugins cannot directly access or modify another plugin’s data, maintaining security and data integrity across the system.
Plugins may share data in two ways:
- By explicit co-location within a namespace, allowing direct database access
- By publishing Simple API endpoints
Data Isolation #
CustomModels created by a plugin exist within namespaces. Tables and data are completely isolated from other namespaces.
# In a plugin named "my_plugin": Creates a table "specialty" in the "my_plugin" namespace
from canvas_sdk.v1.data.base import CustomModel
from django.db.models import TextField
class Specialty(CustomModel):
name = TextField()
# In a plugin named "your_plugin": Creates a table "specialty" in the "your_plugin" namespace
from canvas_sdk.v1.data.base import CustomModel
from django.db.models import TextField
class Specialty(CustomModel):
name = TextField()
# In "your_plugin": Cannot access the "my_plugin" Specialty model or data
AttributeHubs similarly store data within the plugin’s namespace and are not accessible to plugins in other namespaces.
Testing Custom Data #
The Canvas SDK provides comprehensive testing utilities for all custom data approaches. See the Testing Custom Data guide for detailed examples and best practices.
Sharing Data #
Use APIs to make data available and accessible to and from other plugins and external services. See the Sharing Data guide for detailed examples and best practices.
Read Replica Databases #
All the data managed by plugin is available via the database read replica. To access it, alter the PostgreSQL search_path to include the namespaces that you intend to query.
Limitations (for Safety) #
- Values stored in
textandjsonfields may not exceed 1mb as measured by character count. - Bulk operations (e.g.,
bulk_create) are limited to 10,000 records at a time.
See Also #
- CustomModels - Structured models with relationships among entities
- Extending SDK Models - Proxy models,
related_namenamespacing, and referencing SDK models - AttributeHubs - Standalone key-value storage
- Design Considerations - Choosing the right technique and avoiding anti-patterns
- Transactions - All-or-nothing writes with
transaction.atomic() - Testing Custom Data - Testing utilities and examples
- Sharing Data - Sharing data with other plugins and external services
- Data Models - Core SDK data models
- Caching API - Auto-expiring transient data
- Canvas CLI - Simple API for sharing data between plugins
- Secrets - Managing API keys and sensitive configuration