diff --git a/pyairtable/models/schema.py b/pyairtable/models/schema.py index 6b49069c..b71162df 100644 --- a/pyairtable/models/schema.py +++ b/pyairtable/models/schema.py @@ -1,5 +1,6 @@ import importlib from datetime import datetime +from enum import Enum from functools import partial from typing import ( TYPE_CHECKING, @@ -30,6 +31,55 @@ from pyairtable import orm +class FieldType(str, Enum): + """ + Enumeration of all field types supported by Airtable. + + Usage: + >>> from pyairtable.models.schema import FieldType + >>> FieldType.SINGLE_LINE_TEXT + FieldType('singleLineText') + """ + + AI_TEXT = "aiText" + AUTO_NUMBER = "autoNumber" + BARCODE = "barcode" + BUTTON = "button" + CHECKBOX = "checkbox" + COUNT = "count" + CREATED_BY = "createdBy" + CREATED_TIME = "createdTime" + CURRENCY = "currency" + DATE = "date" + DATE_TIME = "dateTime" + DURATION = "duration" + EMAIL = "email" + EXTERNAL_SYNC_SOURCE = "externalSyncSource" + FORMULA = "formula" + LAST_MODIFIED_BY = "lastModifiedBy" + LAST_MODIFIED_TIME = "lastModifiedTime" + MANUAL_SORT = "manualSort" + MULTILINE_TEXT = "multilineText" + MULTIPLE_ATTACHMENTS = "multipleAttachments" + MULTIPLE_COLLABORATORS = "multipleCollaborators" + MULTIPLE_LOOKUP_VALUES = "multipleLookupValues" + MULTIPLE_RECORD_LINKS = "multipleRecordLinks" + MULTIPLE_SELECTS = "multipleSelects" + NUMBER = "number" + PERCENT = "percent" + PHONE_NUMBER = "phoneNumber" + RATING = "rating" + RICH_TEXT = "richText" + ROLLUP = "rollup" + SINGLE_COLLABORATOR = "singleCollaborator" + SINGLE_LINE_TEXT = "singleLineText" + SINGLE_SELECT = "singleSelect" + URL = "url" + + def __repr__(self) -> str: + return f"FieldType({self.value!r})" + + FieldSpecifier: TypeAlias = Union[str, "orm.fields.AnyField"] _T = TypeVar("_T", bound=Any) @@ -755,7 +805,7 @@ class AITextFieldConfig(AirtableModel): Field configuration for `AI text `__. """ - type: Literal["aiText"] + type: Literal[FieldType.AI_TEXT] options: "AITextFieldOptions" @@ -772,7 +822,7 @@ class AutoNumberFieldConfig(AirtableModel): Field configuration for `Auto number `__. """ - type: Literal["autoNumber"] + type: Literal[FieldType.AUTO_NUMBER] class BarcodeFieldConfig(AirtableModel): @@ -780,7 +830,7 @@ class BarcodeFieldConfig(AirtableModel): Field configuration for `Barcode `__. """ - type: Literal["barcode"] + type: Literal[FieldType.BARCODE] class ButtonFieldConfig(AirtableModel): @@ -788,7 +838,7 @@ class ButtonFieldConfig(AirtableModel): Field configuration for `Button `__. """ - type: Literal["button"] + type: Literal[FieldType.BUTTON] class CheckboxFieldConfig(AirtableModel): @@ -796,7 +846,7 @@ class CheckboxFieldConfig(AirtableModel): Field configuration for `Checkbox `__. """ - type: Literal["checkbox"] + type: Literal[FieldType.CHECKBOX] options: "CheckboxFieldOptions" @@ -810,7 +860,7 @@ class CountFieldConfig(AirtableModel): Field configuration for `Count `__. """ - type: Literal["count"] + type: Literal[FieldType.COUNT] options: "CountFieldOptions" @@ -824,7 +874,7 @@ class CreatedByFieldConfig(AirtableModel): Field configuration for `Created by `__. """ - type: Literal["createdBy"] + type: Literal[FieldType.CREATED_BY] class CreatedTimeFieldConfig(AirtableModel): @@ -832,7 +882,7 @@ class CreatedTimeFieldConfig(AirtableModel): Field configuration for `Created time `__. """ - type: Literal["createdTime"] + type: Literal[FieldType.CREATED_TIME] class CurrencyFieldConfig(AirtableModel): @@ -840,7 +890,7 @@ class CurrencyFieldConfig(AirtableModel): Field configuration for `Currency `__. """ - type: Literal["currency"] + type: Literal[FieldType.CURRENCY] options: "CurrencyFieldOptions" @@ -854,7 +904,7 @@ class DateFieldConfig(AirtableModel): Field configuration for `Date `__. """ - type: Literal["date"] + type: Literal[FieldType.DATE] options: "DateFieldOptions" @@ -867,7 +917,7 @@ class DateTimeFieldConfig(AirtableModel): Field configuration for `Date and time `__. """ - type: Literal["dateTime"] + type: Literal[FieldType.DATE_TIME] options: "DateTimeFieldOptions" @@ -890,7 +940,7 @@ class DurationFieldConfig(AirtableModel): Field configuration for `Duration `__. """ - type: Literal["duration"] + type: Literal[FieldType.DURATION] options: "DurationFieldOptions" @@ -903,7 +953,7 @@ class EmailFieldConfig(AirtableModel): Field configuration for `Email `__. """ - type: Literal["email"] + type: Literal[FieldType.EMAIL] class ExternalSyncSourceFieldConfig(AirtableModel): @@ -911,7 +961,7 @@ class ExternalSyncSourceFieldConfig(AirtableModel): Field configuration for `Sync source `__. """ - type: Literal["externalSyncSource"] + type: Literal[FieldType.EXTERNAL_SYNC_SOURCE] options: "SingleSelectFieldOptions" @@ -920,7 +970,7 @@ class FormulaFieldConfig(AirtableModel): Field configuration for `Formula `__. """ - type: Literal["formula"] + type: Literal[FieldType.FORMULA] options: "FormulaFieldOptions" @@ -936,7 +986,7 @@ class LastModifiedByFieldConfig(AirtableModel): Field configuration for `Last modified by `__. """ - type: Literal["lastModifiedBy"] + type: Literal[FieldType.LAST_MODIFIED_BY] class LastModifiedTimeFieldConfig(AirtableModel): @@ -944,7 +994,7 @@ class LastModifiedTimeFieldConfig(AirtableModel): Field configuration for `Last modified time `__. """ - type: Literal["lastModifiedTime"] + type: Literal[FieldType.LAST_MODIFIED_TIME] options: "LastModifiedTimeFieldOptions" @@ -959,7 +1009,7 @@ class ManualSortFieldConfig(AirtableModel): Field configuration for ``manualSort`` field type (not documented). """ - type: Literal["manualSort"] + type: Literal[FieldType.MANUAL_SORT] class MultilineTextFieldConfig(AirtableModel): @@ -967,7 +1017,7 @@ class MultilineTextFieldConfig(AirtableModel): Field configuration for `Long text `__. """ - type: Literal["multilineText"] + type: Literal[FieldType.MULTILINE_TEXT] class MultipleAttachmentsFieldConfig(AirtableModel): @@ -975,7 +1025,7 @@ class MultipleAttachmentsFieldConfig(AirtableModel): Field configuration for `Attachments `__. """ - type: Literal["multipleAttachments"] + type: Literal[FieldType.MULTIPLE_ATTACHMENTS] options: "MultipleAttachmentsFieldOptions" @@ -992,7 +1042,7 @@ class MultipleCollaboratorsFieldConfig(AirtableModel): Field configuration for `Multiple Collaborators `__. """ - type: Literal["multipleCollaborators"] + type: Literal[FieldType.MULTIPLE_COLLABORATORS] class MultipleLookupValuesFieldConfig(AirtableModel): @@ -1000,7 +1050,7 @@ class MultipleLookupValuesFieldConfig(AirtableModel): Field configuration for `Lookup __`. """ - type: Literal["multipleLookupValues"] + type: Literal[FieldType.MULTIPLE_LOOKUP_VALUES] options: "MultipleLookupValuesFieldOptions" @@ -1016,7 +1066,7 @@ class MultipleRecordLinksFieldConfig(AirtableModel): Field configuration for `Link to another record __`. """ - type: Literal["multipleRecordLinks"] + type: Literal[FieldType.MULTIPLE_RECORD_LINKS] options: "MultipleRecordLinksFieldOptions" @@ -1033,7 +1083,7 @@ class MultipleSelectsFieldConfig(AirtableModel): Field configuration for `Multiple select `__. """ - type: Literal["multipleSelects"] + type: Literal[FieldType.MULTIPLE_SELECTS] options: "SingleSelectFieldOptions" @@ -1042,7 +1092,7 @@ class NumberFieldConfig(AirtableModel): Field configuration for `Number `__. """ - type: Literal["number"] + type: Literal[FieldType.NUMBER] options: "NumberFieldOptions" @@ -1055,7 +1105,7 @@ class PercentFieldConfig(AirtableModel): Field configuration for `Percent `__. """ - type: Literal["percent"] + type: Literal[FieldType.PERCENT] options: "NumberFieldOptions" @@ -1064,7 +1114,7 @@ class PhoneNumberFieldConfig(AirtableModel): Field configuration for `Phone `__. """ - type: Literal["phoneNumber"] + type: Literal[FieldType.PHONE_NUMBER] class RatingFieldConfig(AirtableModel): @@ -1072,7 +1122,7 @@ class RatingFieldConfig(AirtableModel): Field configuration for `Rating `__. """ - type: Literal["rating"] + type: Literal[FieldType.RATING] options: "RatingFieldOptions" @@ -1087,7 +1137,7 @@ class RichTextFieldConfig(AirtableModel): Field configuration for `Rich text `__. """ - type: Literal["richText"] + type: Literal[FieldType.RICH_TEXT] class RollupFieldConfig(AirtableModel): @@ -1095,7 +1145,7 @@ class RollupFieldConfig(AirtableModel): Field configuration for `Rollup __`. """ - type: Literal["rollup"] + type: Literal[FieldType.ROLLUP] options: "RollupFieldOptions" @@ -1112,7 +1162,7 @@ class SingleCollaboratorFieldConfig(AirtableModel): Field configuration for `Collaborator `__. """ - type: Literal["singleCollaborator"] + type: Literal[FieldType.SINGLE_COLLABORATOR] class SingleLineTextFieldConfig(AirtableModel): @@ -1120,7 +1170,7 @@ class SingleLineTextFieldConfig(AirtableModel): Field configuration for `Single line text `__. """ - type: Literal["singleLineText"] + type: Literal[FieldType.SINGLE_LINE_TEXT] class SingleSelectFieldConfig(AirtableModel): @@ -1128,7 +1178,7 @@ class SingleSelectFieldConfig(AirtableModel): Field configuration for `Single select `__. """ - type: Literal["singleSelect"] + type: Literal[FieldType.SINGLE_SELECT] options: "SingleSelectFieldOptions" @@ -1146,7 +1196,7 @@ class UrlFieldConfig(AirtableModel): Field configuration for `Url `__. """ - type: Literal["url"] + type: Literal[FieldType.URL] class UnknownFieldConfig(AirtableModel): diff --git a/pyairtable/orm/fields.py b/pyairtable/orm/fields.py index a41def84..2a48e2ca 100644 --- a/pyairtable/orm/fields.py +++ b/pyairtable/orm/fields.py @@ -69,6 +69,7 @@ UnsavedRecordError, ) from pyairtable.models import schema as S +from pyairtable.models.schema import FieldType from pyairtable.orm.lists import AttachmentsList, ChangeTrackingList if TYPE_CHECKING: @@ -1410,43 +1411,46 @@ class RequiredUrlField(UrlField, _Requires[str]): #: for the ORM to know or detect those fields' types. These two #: field type names are mapped to the constant ``NotImplemented``. #: +#: Keys are :class:`~pyairtable.models.schema.FieldType` enum values, +#: which inherit from ``str`` and can be used in string comparisons. +#: #: :meta hide-value: FIELD_TYPES_TO_CLASSES: Dict[str, Type[AnyField]] = { - "aiText": AITextField, - "autoNumber": AutoNumberField, - "barcode": BarcodeField, - "button": ButtonField, - "checkbox": CheckboxField, - "count": CountField, - "createdBy": CreatedByField, - "createdTime": CreatedTimeField, - "currency": CurrencyField, - "date": DateField, - "dateTime": DatetimeField, - "duration": DurationField, - "email": EmailField, - "externalSyncSource": ExternalSyncSourceField, - "formula": NotImplemented, - "lastModifiedBy": LastModifiedByField, - "lastModifiedTime": LastModifiedTimeField, - "lookup": LookupField, - "manualSort": ManualSortField, - "multilineText": TextField, - "multipleAttachments": AttachmentsField, - "multipleCollaborators": MultipleCollaboratorsField, - "multipleLookupValues": LookupField, - "multipleRecordLinks": LinkField, - "multipleSelects": MultipleSelectField, - "number": NumberField, - "percent": PercentField, - "phoneNumber": PhoneNumberField, - "rating": RatingField, - "richText": RichTextField, - "rollup": NotImplemented, - "singleCollaborator": CollaboratorField, - "singleLineText": TextField, - "singleSelect": SelectField, - "url": UrlField, + FieldType.AI_TEXT: AITextField, + FieldType.AUTO_NUMBER: AutoNumberField, + FieldType.BARCODE: BarcodeField, + FieldType.BUTTON: ButtonField, + FieldType.CHECKBOX: CheckboxField, + FieldType.COUNT: CountField, + FieldType.CREATED_BY: CreatedByField, + FieldType.CREATED_TIME: CreatedTimeField, + FieldType.CURRENCY: CurrencyField, + FieldType.DATE: DateField, + FieldType.DATE_TIME: DatetimeField, + FieldType.DURATION: DurationField, + FieldType.EMAIL: EmailField, + FieldType.EXTERNAL_SYNC_SOURCE: ExternalSyncSourceField, + FieldType.FORMULA: NotImplemented, + FieldType.LAST_MODIFIED_BY: LastModifiedByField, + FieldType.LAST_MODIFIED_TIME: LastModifiedTimeField, + "lookup": LookupField, # Deprecated alias for multipleLookupValues + FieldType.MANUAL_SORT: ManualSortField, + FieldType.MULTILINE_TEXT: TextField, + FieldType.MULTIPLE_ATTACHMENTS: AttachmentsField, + FieldType.MULTIPLE_COLLABORATORS: MultipleCollaboratorsField, + FieldType.MULTIPLE_LOOKUP_VALUES: LookupField, + FieldType.MULTIPLE_RECORD_LINKS: LinkField, + FieldType.MULTIPLE_SELECTS: MultipleSelectField, + FieldType.NUMBER: NumberField, + FieldType.PERCENT: PercentField, + FieldType.PHONE_NUMBER: PhoneNumberField, + FieldType.RATING: RatingField, + FieldType.RICH_TEXT: RichTextField, + FieldType.ROLLUP: NotImplemented, + FieldType.SINGLE_COLLABORATOR: CollaboratorField, + FieldType.SINGLE_LINE_TEXT: TextField, + FieldType.SINGLE_SELECT: SelectField, + FieldType.URL: UrlField, } diff --git a/pyairtable/orm/generate.py b/pyairtable/orm/generate.py index a1e89cf9..3d6d0673 100644 --- a/pyairtable/orm/generate.py +++ b/pyairtable/orm/generate.py @@ -19,6 +19,7 @@ from pyairtable.api.base import Base from pyairtable.api.table import Table from pyairtable.models import schema as S +from pyairtable.models.schema import FieldType from pyairtable.orm import fields _ANNOTATION_IMPORTS = { @@ -165,7 +166,7 @@ def __str__(self) -> str: if cls is fields._ListField: generic = "str" - if self.schema.type in ("formula", "rollup"): + if self.schema.type in (FieldType.FORMULA, FieldType.ROLLUP): assert isinstance(self.schema, (S.FormulaFieldSchema, S.RollupFieldSchema)) cls = fields.Field if self.schema.options.result: @@ -209,7 +210,7 @@ def lookup_field_type_annotation(schema: S.MultipleLookupValuesFieldSchema) -> s if not schema.options.result: return "Any" lookup_type = schema.options.result.type - if lookup_type == "multipleRecordLinks": + if lookup_type == FieldType.MULTIPLE_RECORD_LINKS: return "str" # otherwise this will be 'list' cls = fields.FIELD_TYPES_TO_CLASSES[lookup_type] if isinstance(contained_type := getattr(cls, "contains_type", None), type): diff --git a/tests/test_models_schema.py b/tests/test_models_schema.py index b5b0ca82..e909c337 100644 --- a/tests/test_models_schema.py +++ b/tests/test_models_schema.py @@ -542,3 +542,53 @@ def test_save_date_dependency_settings__invalid_field(table_schema): predecessor_field="invalid_field", rescheduling_mode="none", ) + + +def test_field_type_enum(): + """ + Test that FieldType enum contains all expected field types. + """ + # Test that enum inherits from str + assert isinstance(schema.FieldType.SINGLE_LINE_TEXT, str) + + # Test that enum can be used in string comparisons + assert schema.FieldType.SINGLE_LINE_TEXT == "singleLineText" + + # Test that all field config types have corresponding enum values + expected_types = { + "aiText", + "autoNumber", + "barcode", + "button", + "checkbox", + "count", + "createdBy", + "createdTime", + "currency", + "date", + "dateTime", + "duration", + "email", + "externalSyncSource", + "formula", + "lastModifiedBy", + "lastModifiedTime", + "manualSort", + "multilineText", + "multipleAttachments", + "multipleCollaborators", + "multipleLookupValues", + "multipleRecordLinks", + "multipleSelects", + "number", + "percent", + "phoneNumber", + "rating", + "richText", + "rollup", + "singleCollaborator", + "singleLineText", + "singleSelect", + "url", + } + assert expected_types == {member.value for member in schema.FieldType}