Skip to content

5. Monday Models and Types

Andrew edited this page Nov 14, 2021 · 9 revisions

Introducing Monday Models

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.

Creating a Model

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)

Validating Models

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'

Exporting Models

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'}

Saving Models

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)

Using Monday Types and Glossary

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.

CheckboxType

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'}}

CountryType

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'}}

CreationLogType

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.

DateType

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'}

DependencyType

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]}}

DropdownType

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]}}

EmailType

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'}}

HourType

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}}

ItemLinkType

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]}}

LastUpdatedType

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.

LinkType

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'}}

LocationType

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'}}

LongTextType

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'}}

NumberType

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'}

PeopleType

|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'}]}}

PhoneType

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'}}

RatingType

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}}

StatusType

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}}

SubItemType

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.

TextType

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'}

TagsType

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]}}

TimelineType

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'}}

TimeZoneType

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

WeekType

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'}}}