-
Notifications
You must be signed in to change notification settings - Fork 18
5. Monday Models and Types
The MondayModel a Python base class that enables developers to define their monday.com boards as a Python class. This powerful new addition allows developers to retrieve individual items from a board as the defined MondayModel subclass, make simple value edits to configured fields, and both export data for and save updated data to monday.com via the API.
Both models and types leverage the Schematics python package.
A MondayModel can be simply created using the available MondayType classes as listed below. Simply declare a class and inherit the MondayModel class to create a new Monday model. Each model comes with the id and name fields corresponding to the ID and name of the linked monday.com item respectively.
from moncli.models import MondayModel
from moncli.types import *
class WorkTask(MondayModel):
assignees = PeopleType(title='Assignees')
status = StatusType(title='Status')
due_date = DateType(title='Due Date')
total_hours = NumberType(title='Total Hours')
A MondayModel instance is initialized in a variety of ways depending on desired use. When creating a new monday.com item from a MondayModel instance, pass the name of the new model to create and the Board containing the necessary column information.
from moncli import client
board = client.get_board(name='Work Tasks')
new_task = WorkTask(name='Task 1', board=board)
NOTE: A MondayModel initialized in this way will contain no id value. The id value is set to the item ID after saving the model data as a new item in monday.com.
MondayModel instances are most frequently initialized from the monday.com item itself. Simply retrieve the monday.com item and pass it into the constructor.
item = board.get_items(ids=[12345678])[0]
existing_task = WorkTask(item)
Entity methods in Moncli that return an Item or a list of Item objects can also return its corresponding model using the as_model parameter. An example using the MondayClient.get_items method below.
model = client.get_items(ids=[12345678], as_model=WorkTask)[0]
print(model.__class__.__name__)
WorkTask
In the case of testing, A MondayModel instance may be created from scratch using the id, name key-word arguments. In the case that the test model is to be populated with data, the raw_data argument may be used. This argument accepts the desired values of the MondayModel fields using the str name of the field as a key.
from datetime import datetime
from moncli.column_value import Person
raw_data = {'assignees': [Person(123456)], 'status': 'In Progress', 'due_date': datetime(2021, 11, 7), 'total_hours': 999}
test_task = WorkTask(id=12345679, name='Test Task 2', raw_data=raw_data)
Monday types contain built-in validation but allow for the use of existing Schematics validation configurations and custom validators.
from moncli.column_value import
from schematics.exceptions import ValidationError
def not_negative(value):
if float(value) < 0:
raise ValidationError('Value must be a non-negative number')
return value
class WorkTask(MondayModel)
assignees = PeopleType(title='Assignees', max_allowed=2) # Built-in type validation configuration
status = StatusType(title='Status', default='Ready to Work')
due_date = DateType(title='Date', required=True) # Extended schematics validation configuration
hours = NumberType(title='Hours', validators=[not_negative]) # Extending schematics custom validators
raw_data = {'assignees': [Person(123456), Person(123457), Person(123458)], 'due_date': None, 'total_hours': -1}
test_task = WorkTask(id=12345680, name='Test Task 3', raw_data=raw_data)
test_task.validate() # This will result in three validation errors, one each for 'assignees', 'due_date', and 'total_hours'
Models export data for serialization using the to_primitive method extended from Schematics. When formatting data for export, the model will use the ID of the column corresponding to the type field.
# Data from board/item
task = WorkTask(item)
print(task.to_primitive())
{'people': [{'id': 123456, 'type': 'person'}], 'status': {'index': 1}, 'date_1': '2021-11-07', 'numbers_1': '999'}
In the case of test data when no column ID or column value ID is provided when declaring the type field, the str name of the field is used.
# Data from test raw data
from datetime import datetime
raw_data = {'assignees': [Person(123456)], 'due_date': datetime(2021, 11, 7), 'total_hours': 999}
test_task = WorkTask(id=12345681, name='Test Task 4', raw_data=raw_data)
print(test_task.to_primitive())
{'assignees': [{'id': 123456, 'type': 'person'}], 'status': {'index': 0}, 'due_date': '2021-11-07', 'total_hours': '999'}
The to_primitive method extends its Schematics predecessor with the optional diff_only key-word argument. Using this argument will format only model field values that have changed.
NOTE: The diff_only key-word argument is used when saving model data to monday.com via the API to reduce latency and to maintain a clean activity log record.
task = WorkTask(item)
task.total_hours = 9999
print(task.to_primitive(diff_only=True))
{'numbers_1': '9999'}
Models may be saved to the monday.com API using the save method. This method validates the existing model before saving only updated fields.
If a Board was used to create the model instance, the method will create a new item in monday.com on the board.
from datetime import datetime
from moncli import client
from moncli.column_value import Person
board = client.get_board(title='Work Tasks')
task = WorkTask(name='Test Task 6', board=board)
task.assignees.append(Person(123456))
task.status = 'In Progress'
task.due_date = datetime(2021, 11, 15)
task.total_hours = 9999
task.save()
Saving a new item to monday.com will add it to the default board group. To save the item to a particular group, simply use the group key-word argument with the Group instance to contain the new item.
group = board.get_group(title='The Actual Group')
task.save(group=group)
The save method can archive items in addition to updating them. Simply set the archived key-word argument to True when saving the model.
task.save(archive=True)
The MondayType base class defines the fields for the declared MondayModel class implementation. This base class extends the Schematics BaseType, enabling built-in validation and conversion configurations in addition to custom configurations and validation provided by Moncli.
A MondayType class implementation requires either the corresponding column ID or title upon initialization. This is what allows Moncli to map the column value data from monday.com to the corresponding MondayModel field.
This section contains all available MondayType classes for both reading and writing data from and to monday.com respectively. Please note that for the remaining examples, the exported results use the name of the configured MondayType field instead of the given column ID for the board. It is assumed that the model values for the examples below are not derived from monday.com boards or items.
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
CheckboxValue | bool | int, str | False |
Examples
## Configuring the type
class ExampleModel(MondayModel):
checkbox = CheckboxType(title='Checkbox')
model = ExampleModel(id=12345, name='Checkbox Model')
## Setting value
model.checkbox = True
print(model.to_primitive())
{'checkbox': {'checked': 'true'}}
## Setting value with allowed cast
# int
model.checkbox = 0
print(model.to_primitive())
{'checkbox': {}}
# str
model.checkbox = "true"
print(model.to_primitive())
{'checkbox': {'checked': 'true'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
CountryValue | moncli.column_value.Country | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
country = CountryType(title='Country')
model = ExampleModel(id=12345, name='Country Model')
## Setting value
from moncli.column_value import Country
model.country = Country('United States', 'US')
model.to_primitive()
{'country':{'name': 'United States', 'code': 'US'}}
## Setting value with allowed cast
# dict
model.country = {'name':'United Kingdom', 'code':'GB'}
print(model.to_primitive())
{'country':{'countryName':'United Kingdom', 'countryCode':'GB'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
CreationLogValue | datetime | N/A | None |
Examples
## Configuring the type
from datetime import datetime
class ExampleModel(MondayModel):
creation_log = CreationLogTytpe(title='Created At')
raw_data = {'creation_log': datetime(2021, 11, 1)
model = ExampleModel(id=12345, name='Creation Log Model', raw_data=raw_data)
print(model.creation_log)
datetime.datetime(2021, 11, 1, 0, 0)
# No primitive value will be returned for read-only columns
print(model.to_primitive())
{}
Notes:
- CreationLogType is a read-only type, and any changes will not be kept when saving the model.
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
DateValue | datetime | int, str | None |
Additional Parameters
Name | Type | Description | Default |
---|---|---|---|
has_time | bool | True if the date value also contains a time | False |
Examples
## Configuring the type
class ExampleModel(MondayModel):
date = DateType(title='Date')
date_with_time = Datetime(title='Date with Time', has_time=True)
model = ExampleModel(id=12345, name='Date Model')
## Setting value
from datetime import datetime
model.date_with_time = datetime(2021, 10, 31, 3, 32)
print(model.to_primitive(diff_only=True))
{'date_with_time': '2021-10-31 08:32:00'} # Assuming that the local timezone is GMT-5:00, to_primitive converts the local time to UTC
## Setting value with allowed cast
# str
model.date = '2021-10-31'
print(model.to_primitive(diff_only=True))
{'date': '2021-10-31'}
# int
model.date = 1635566400
print(model.to_primitive(diff_only=True))
{'date': '2021-10-30'}
Column Value Type | Python Type/Class | Element Type | Default Value |
---|---|---|---|
DependencyValue | list | str | [] |
Examples
## Configuring the type
class ExampleModel(MondayModel):
dependency = DependencyType(title='Depends On')
model = ExampleModel(id=12345, name='Dependency Model')
## Setting value
# As new list
model.dependency = [12345678]
print(model.to_primitive())
{'dependency': {'item_ids': [12345678]}}
# To existing list
model.dependency.append(23456789)
print(model.to_primitive())
{'dependency': {'item_ids': [12345678, 23456789]}}
Column Value Type | Python Type/Class | Element Type | Default Value |
---|---|---|---|
DropdownValue | list | str | Enum |
Examples
Additional Parameters
Name | Type | Description | Default |
---|---|---|---|
as_enum | type | The Enum class type to be used for field values | [] |
## Configuring the type
# Without enum
# Assume the following labels: [{'id': 0, 'name': 'Python'}, {'id': 1, 'name': 'Javascript'}, {'id': 2, 'name': 'C#'}]
class ExampleModel(MondayModel):
dropdown = DropdownType(title='Dropdown')
model = ExampleModel(id=12345, name='Dropdown Model')
# With enum
from enum import Enum
class Language(Enum): # When using an Enum for dropdown values, the value must be the dropdown label name
python = 'Python'
javascript = 'Javascript'
c_sharp = 'C#'
class ExampleEnumModel(MondayModel):
dropdown = DropdownType(title='Dropdown', as_enum=Language)
model_with_enum = ExampleEnumModel(id=12345, name='Dropdown Model')
## Setting value
# As str
model.dropdown.append('Python')
print(model.to_primitive())
{'dropdown': {'ids': [0]}}
# As enum
model.dropdown.append(Language.javascript)
print(model.to_primitive())
{'dropdown': {'ids': [0, 1]}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
EmailValue | moncli.column_value.Email | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
email = EmailType(title='Email')
model = ExampleModel(id=12345, name='Email Model')
## Setting value
from moncli.column_value import Email
model.email = Email('andrew.shatz@trix.solutions', 'Andrew Shatz')
print(model.to_primitive())
{'email': {'email': 'andrew.shatz@trix.solutions', 'text': 'Andrew Shatz'}}
## Setting value with allowed cast
model.email = {'email': 'patrick@trix.solutions', 'text': 'The Buck Stops Here'}
print(model.to_primitive())
{'email': {'email': 'patrick@trix.solutions', 'text': 'The Buck Stops Here'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
HourValue | moncli.column_value.Hour | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
hour = EmailType(title='Hour')
model = ExampleModel(id=12345, name='Hour Model')
## Setting value
from moncli.column_value import Hour
model.hour = Hour(7, 6)
print(model.to_primitive())
{'hour': {'hour': 7, 'minute': 6}}
## Setting value with allowed cast
model.hour = {'hour': 3, 'minute': 33}
print(model.to_primitive())
{'hour': {'hour': 3, 'minute': 33}}
Column Value Type | Python Type/Class | Element Type | Default Value |
---|---|---|---|
ItemLinkType | list (int when multiple_values is False) | int (None when multiple_values is False) | [] |
Examples
Additional Parameters
Name | Type | Description | Default |
---|---|---|---|
multiple_values | bool | True if value is list of multiple items | True |
## Configuring the type
class ExampleModel(MondayModel):
item_link = ItemLinkType(title='Linked Board')
model = ExampleModel(id=12345, name='Item Link Model')
# With single value
class ExampleSingleModel(MondayModel):
item_link = ItemLinkType(title='Other Linked Board', multiple_values=False)
model_single = ExampleModel(id=12345, name='Single Item Link Model')
## Setting value
# for multiple values
model.item_link.append(12345678)
print(model.to_primitive())
{'item_link': {'item_ids': [12345678]}}
# for single item
model_single.item_link = 23456789
print(model_single.to_primitive())
{'item_link': {'item_ids': [23456789]}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
LastUpdateValue | datetime | N/A | None |
Examples
## Configuring the type
from datetime import datetime
class ExampleModel(MondayModel):
last_updated = LastUpdatedType(title='Created At')
raw_data = {'last_updated': datetime(2021, 11, 1)
model = ExampleModel(id=12345, name='Last Updated Model', raw_data=raw_data)
print(model.last_updated)
datetime.datetime(2021, 11, 1, 0, 0)
# No primitive value will be returned for read-only columns
print(model.to_primitive())
{}
Notes:
- LastUpdatedType is a read-only type, and any changes will not be kept when saving the model.
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
LinkValue | moncli.column_value.Link | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
link = LinkType(title='Link')
model = ExampleModel(id=12345, name='Link Model')
## Setting value
from moncli.column_value import Link
model.link = Link('https://google.com', 'Google')
print(model.to_primitive())
{'link': {'url': 'https://google.com', 'text', 'Google'}}
## Setting value with allowed cast
model.link = {'url': 'https://github.com', 'text', 'Github'}}
print(model.to_primitive())
{'link': {'url': 'https://github.com', 'text', 'Github'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
LocationValue | moncli.column_value.Location | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
location = LocationTypew(title='Location')
model = ExampleModel(id=12345, name='Link Model')
## Setting value
from moncli.column_value import Location
model.location = Location(30.550557, 79.5637746, 'Jyotir Math')
print(model.to_primitive())
{'location': {'lat': 30.550557, 'lng': 79.5637746, 'address': 'Jyotir Math'}}
## Setting value with allowed cast
model.location = {'lat': 34.8890572, 'lng': 135.8076569, 'address': 'Byodoin'}}
print(model.to_primitive())
{'location': {'lat': 34.8890572, 'lng': 135.8076569, 'address': 'Byodoin'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
LongTextValue | str | int, float | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
long_text = LongTextType(title='Long Text')
model = ExampleModel(id=12345, name='Long Text Model')
## Setting value
model.long_text = 'This is really a long string......................'
print(model.to_primitive())
{'long_text': {'text':'This is really a long string......................'}}
## Setting value with allowed cast
# int
model.long_text = 1234567890987654321
print(model.to_primitive())
{'long_text': {'text':'1234567890987654321'}}
# float
model.long_text = 1234567890987654321.0
print(model.to_primitive())
{'long_text': {'text':'1234567890987654321.0'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
NumberValue | int, float | str | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
number = NumberType(title='Numbers')
model = ExampleModel(id=12345, name='Number Model')
## Setting value
model.number = 12345
print(model.to_primitive())
{'number': '12345'}
## Setting value with allowed cast
model.number = '123456'
print(model.to_primitive())
{'number': '123456'}
|Column Value Type|Python Type/Class|Allowed Casts|Element Type|Default Value| |---|---|---|---| |PeopleValue|list, moncli.column_value.PersonOrTeam|int, str, dict (when max_allowed == 1)|moncli.column_value.PersonOrTeam (when max_allowed != 1)|[]|
Additional Parameters
Parameter | Type | Default Value | Additional Info |
---|---|---|---|
max_allowed | int | The total number of people or teams allowed. Value of 0 indicates not limit. | 0 |
## Configuring the type
class ExampleModel(MondayModel):
manager = PeopleType(title='Manager', max_allowed=1)
employees = PeopleType(title='Employees')
model = ExampleModel(id=12345, name='People Model')
## Setting value
# to single person or team.
from moncli.column_value import Person
model.manager = Person(123456)
print(model.to_primitive(diff_only=True))
{'manager': {'personsAndTeams': [{'id': 123456, 'kind': 'person'}]}}
# to multiple persons and teams
model.employees = [Person(234567)]
model.employees.append(Person(345678))
print(model.to_primitive(diff_only=True))
{'manager': {'personsAndTeams': [{'id': 234567, 'kind': 'person'}, {'id': 234567, 'kind': 'person'}]}}
## Setting value to single person or team with allowed cast
# int or str for (person) user id
model.manager = 456789
print(model.to_primitive())
{'manager': {'personsAndTeams': [{'id': 456789, 'kind': 'person'}]}}
# dict
model.manager = {'id': 567890, 'kind': 'person'}
print(model.to_primitive())
{'manager': {'personsAndTeams': [{'id': 567890, 'kind': 'person'}]}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
PhoneValue | moncli.column_value.Phone | str, dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
phone = PhoneType(title='Phone')
model = ExampleModel(id=12345, name='Phone Model')
## Setting value
from moncli.column_value import Phone
model.phone = Phone('+15083658469', 'US')
print(model.to_primitive())
{'phone': {'phone': '+15083658469', 'countryShortName': 'US'}}
## Setting value with allowed cast
model.phone = {'phone': '+15088675309', 'countryShortName': 'US'}
print(model.to_primitive())
{'phone': {'phone': '+15088675309', 'countryShortName': 'US'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
RatingValue | int | str | None |
Examples
## Configuring the type
class ExampleModel(MondayModel)
rating = RatingType(title='Rating')
model = ExampleModel(id=12345, name='Rating Model')
## Setting value
model.rating = 5
print(model.to_primitive())
{'rating': {'rating': 5}}
## Setting value with allowed cast
model.rating = '1'
print(model.to_primitive())
{'rating': {'rating': 1}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
StatusValue | str | int | None |
Additional Parameters
Name | Type | Description | Default |
---|---|---|---|
as_enum | type | The Enum class type to be used for field values | [] |
Examples
## Configuring the type
# Assume labels: {'0': 'Ready to Work', '1': 'In Progress', '2': 'Done'}
# Using string label values
class ExampleModel(MondayModel):
status = StatusType(title='Status')
model = ExampleModel(id=12345, name='Status Model')
# Using Enum subclass
from enum import Enum
class Status(Enum): # Enum values need to match with configured label text from the monday.com column.
ready_to_work = 'Ready to Work'
in_progress = 'In Progress'
done = 'Done'
class ExampleEnumModel(MondayModel):
status = StatusType(title='Status', as_enum=Status)
model_with_enums = ExampleModel(id=12345, name='Status Model')
## Setting value
# With str label
model.status = 'Ready to Work'
print(model.to_primitive())
{'status': {'index': 0}}
# With Enum label
model_with_enums = Status.in_progress
print(model_with_enums.to_primitive())
{'status': {'index': 1}}
## Setting value with allowed cast
model.status = 2
print(model.to_primitive())
{'status': {'index': 2}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
SubItemValue | list | N/A | [] |
Examples
## Configuring the type
class ExampleModel(MondayModel):
subitems = SubItemType(title='Subitems')
model = ExampleModel(id=12345, name='Subitems Model')
print(model.subitems) # Stored value is a list of item IDs
[12345678, 23456789]
print(model.to_primitive()) # No value is returned as field is read-only
{}
Notes:
- SubItemsType is read-only and cannot be updated.
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
TextValue | str | int, float | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
text = TextType(title='Text')
model = ExampleModel(id=12345, name='Text Model')
## Setting value
model.text = 'This is text'
print(model.to_primitive())
{'text': 'This is text'}
## Setting value with allowed cast
# int or float
model.text = 123.456
print(model.to_primitive())
{'text': '123.456'}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
TagsValue | list | str | [] |
Examples
## Configuring the type
class ExampleModel(MondayModel):
tags = TagsType(title='Tags')
model = ExampleModel(id=12345, name='Tags Model')
## Setting value
model.tags = [123456]
model.tags.append(234567)
print(model.to_primitive())
{'tags': {'tag_ids': [123456,234567]}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
TimelineValue | moncli.column_value.Timeline | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
timeline = TimelineType(title='Timeline')
model = ExampleModel(id=12345, name='Timeline Model')
## Setting value
from datetime import datetime
from moncli.column_value import Timeline
model.timeline = Timeline(datetime(2021, 10, 1), datetime(2021, 11, 1))
print(model.to_primitive())
{'timeline': {'from': '2021-10-01', 'to': '2021-11-01'}}
## Setting value with allowed cast
model.timeline = {'from': datetime(2021, 10, 1), 'to': datetime(2021, 11, 1)}
print(model.to_primitive())
{'timeline': {'from': '2021-10-01', 'to': '2021-11-01'}}
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
TimeZoneValue | str | N/A | None |
Examples
## Configuring the type
class ExampleModel(MondayModel):
timezone = TimeZoneType(title='World Clock')
model = ExampleModel(id=12345, name='Time Zone Type')
## Setting value
model.timezone = 'America/New_York'
print(model.to_primitive())
{'timezone': {'timezone': 'America/New_York'}}
Notes:
- Valid time zone values are available here
Column Value Type | Python Type/Class | Allowed Casts | Default Value |
---|---|---|---|
WeekValue | moncli.column_value.Week | dict | None |
Examples
## Configuring the type
class ExampleModel(MondayModel)
week = WeekType(title='Week')
model = ExampleModel(id=12345, name='Week Model')
## Setting value
from datetime import datetime
from moncli.column_value import Week
model.week = Week(datetime(2021, 11, 1)) # All you need is the start date, the data type handles the rest.
print(model.to_primitive())
{'week': {'week': {'startDate': '2021-11-01', 'endDate': '2021-11-07'}}}
## Setting value with allowed cast
model.week = {'start': datetime(2021, 11, 1)} # All you need is the start date, the data type handles the rest.
print(model.to_primitive())
{'week': {'week': {'startDate': '2021-11-01', 'endDate': '2021-11-07'}}}