Nodes and Fields
Nodes and Fields are the fundamental building blocks of Capela applications. Understanding how to use them effectively is crucial for building robust applications.
Nodes
A Node is the basic unit of data in Capela. It represents an entity in your application and serves as the building block for your application's data model.
Creating a Node
To create a Node, you need to define a class that inherits from the Node class:
from builtins import Node, Field
class User(Node):
name: str = Field(default="")
age: int = Field(default=0)
Node Methods
Nodes can have methods that operate on their data:
class Counter(Node):
count: int = Field(default=0)
def increment(self):
self.count += 1
return self.count
def reset(self):
self.count = 0
return self.count
Node Lifecycle
Nodes have a lifecycle that includes:
- Creation: A Node is created when it is instantiated or when a partition is created.
- Initialization: Fields are initialized with their default values.
- Persistence: Nodes are automatically persisted to the database.
- Retrieval: Nodes can be retrieved from the database using their reference.
Fields
Fields define the properties of a Node. They are strongly typed and can have default values.
Field Types
Capela supports various field types:
Basic Types
int: Integer valuesbool: Boolean values (True/False)float: Floating point numbersstr: String valuesbytes: Binary data
Collection Types
list[T]: Ordered list of elements of type T (e.g.,list[int])dict[K, V]: Key-value mapping where K is the key type and V is the value type (e.g.,dict[str, int])set[T]: Unordered collection of unique elements of type T (e.g.,set[int])
Collection types can be accessed using array notation or quoted keys for dictionaries:
# Access dictionary values
http GET 'http://localhost:22440/g/!partition_id/users/"Alice"'
http POST 'http://localhost:22440/g/!partition_id/users/"Alice"' --raw '{"name": "Alice", "age": 30}'
# Access array values
http GET 'http://localhost:22440/g/!partition_id/users/0'
http POST 'http://localhost:22440/g/!partition_id/users/0' --raw '{"name": "Alice", "age": 30}'
Custom Types
You can create custom types by defining a class that inherits from Model. These types can be used as fields in your Nodes:
class Address(Model):
street: str = Field(default="")
city: str = Field(default="")
state: str = Field(default="")
zip_code: str = Field(default="")
class User(Node):
name: str = Field(default="")
address: Address = Field(default_factory=Address)
Field Definition
Fields are defined using the Field class with the following syntax:
variable_name: type = Field(default=default_value)
For collection types, you can use default_factory to provide a function that creates a new instance of the collection:
class User(Node):
name: str = Field(default="")
preferences: dict[str, bool] = Field(default_factory=dict)
tags: list[str] = Field(default_factory=list)
Field Validation
Fields can have validation rules to ensure that the data is valid:
class User(Node):
name: str = Field(default="")
age: int = Field(default=0, ge=0, le=120) # age must be between 0 and 120
email: str = Field(default="", regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") # email must be valid
Example: A Complete Node
Here's a complete example of a Node with various field types:
from builtins import Node, Field, List, Dict
from datetime import datetime
class Address(Model):
street: str = Field(default="")
city: str = Field(default="")
state: str = Field(default="")
zip_code: str = Field(default="")
class User(Node):
# Basic types
name: str = Field(default="")
age: int = Field(default=0)
is_active: bool = Field(default=True)
# Collection types
preferences: Dict[str, bool] = Field(default_factory=dict)
tags: List[str] = Field(default_factory=list)
# Custom types
address: Address = Field(default_factory=Address)
# Methods
def activate(self):
self.is_active = True
return self.is_active
def deactivate(self):
self.is_active = False
return self.is_active
def add_tag(self, tag: str):
if tag not in self.tags:
self.tags.append(tag)
return self.tags
def remove_tag(self, tag: str):
if tag in self.tags:
self.tags.remove(tag)
return self.tags