Field Types¶
Complete guide to Airtable fields and field type detection.
Field Type Detection Overview¶
Pydantic Airtable automatically detects the appropriate Airtable field type based on:
- Field name patterns - Keywords in the field name
- Python type - The type annotation
- Explicit override - Using
airtable_field()
Detection Priority¶
When determining field type, the library checks in this order:
- Explicit type -
airtable_field(field_type=...)always wins - Field metadata - Existing type in field's
json_schema_extra - Field name patterns - Field type detection from naming
- Python type - Default mapping from type annotation
Field Name Patterns¶
Email Fields¶
Field names matching these patterns become EMAIL fields:
emaile_mailmailcontact
URL Fields¶
Field names matching these patterns become URL fields:
urllinkwebsitesitehref
class Company(BaseModel):
website: str # → URL
linkedin_url: str # → URL
portfolio_link: str # → URL
Phone Fields¶
Field names matching these patterns become PHONE fields:
phonetelmobilecell
Long Text Fields¶
Field names matching these patterns become LONG_TEXT (multiline) fields:
descriptioncommentnotebiosummarycontentbodymessagedetail
class Article(BaseModel):
description: str # → LONG_TEXT
content: str # → LONG_TEXT
author_bio: str # → LONG_TEXT
notes: str # → LONG_TEXT
Currency Fields¶
Number fields with these name patterns become CURRENCY fields:
pricecostamountfeesalarywagerevenuebudgetpayment
class Product(BaseModel):
price: float # → CURRENCY
shipping_cost: float # → CURRENCY
total_amount: float # → CURRENCY
Percentage Fields¶
Number fields with these name patterns become PERCENT fields:
percentpercentagerateratio
class Metrics(BaseModel):
completion_rate: float # → PERCENT
success_percentage: float # → PERCENT
conversion_ratio: float # → PERCENT
Python Type Mappings¶
Basic Types¶
| Python Type | Airtable Type |
|---|---|
str |
SINGLE_LINE_TEXT |
int |
NUMBER |
float |
NUMBER |
bool |
CHECKBOX |
Date/Time Types¶
| Python Type | Airtable Type |
|---|---|
datetime |
DATETIME |
date |
DATE |
timedelta |
DURATION |
Other Types¶
| Python Type | Airtable Type | Notes |
|---|---|---|
int (with rating pattern) |
RATING | For fields named rating, stars, score, rank |
Special Types (Explicit Only)¶
These field types require explicit specification - they are not auto-detected:
| Airtable Type | Description |
|---|---|
| LINKED_RECORD | Links to records in another table |
| USER | Collaborator/user references |
| BUTTON | Triggers automations (read-only) |
| BARCODE | Stores barcode text |
AUTO_NUMBER Field Limitation
The Airtable public API does not support creating fields with type AUTO_NUMBER. If you need an auto-incrementing number field, you must:
- Create a
NUMBERfield via the API or model - Manually convert it to
Auto numberin the Airtable UI (Field settings → Convert to Auto number)
Once converted, the field becomes read-only and auto-increments for new records.
Complex Types¶
| Python Type | Airtable Type |
|---|---|
Enum |
SELECT |
List[str] |
MULTI_SELECT |
All Airtable Field Types¶
The library supports these Airtable field types:
from pydantic_airtable import AirtableFieldType
class AirtableFieldType(str, Enum):
SINGLE_LINE_TEXT = "singleLineText"
LONG_TEXT = "multilineText"
NUMBER = "number"
CURRENCY = "currency"
PERCENT = "percent"
DATE = "date"
DATETIME = "dateTime"
DURATION = "duration"
RATING = "rating"
CHECKBOX = "checkbox"
# Relational fields
LINKED_RECORD = "multipleRecordLinks"
# Special fields
BARCODE = "barcode"
BUTTON = "button"
USER = "multipleCollaborators"
SELECT = "singleSelect"
MULTI_SELECT = "multipleSelects"
EMAIL = "email"
URL = "url"
PHONE = "phoneNumber"
ATTACHMENT = "multipleAttachments"
FORMULA = "formula"
ROLLUP = "rollup"
COUNT = "count"
LOOKUP = "lookup"
CREATED_TIME = "createdTime"
MODIFIED_TIME = "lastModifiedTime"
CREATED_BY = "createdBy"
MODIFIED_BY = "lastModifiedBy"
AUTO_NUMBER = "autoNumber"
Overriding Detection¶
Use airtable_field() to override automatic detection:
Explicit Field Type¶
from pydantic_airtable import airtable_field, AirtableFieldType
@airtable_model(table_name="Products")
class Product(BaseModel):
name: str
# Override: 'code' would normally be SINGLE_LINE_TEXT
code: str = airtable_field(
field_type=AirtableFieldType.SINGLE_LINE_TEXT
)
# Override: 'notes' detected as LONG_TEXT, but we want single line
notes: str = airtable_field(
field_type=AirtableFieldType.SINGLE_LINE_TEXT
)
Custom Field Name¶
Map Python field name to different Airtable field name:
@airtable_model(table_name="Users")
class User(BaseModel):
# Python: 'full_name' → Airtable: 'Full Name'
full_name: str = airtable_field(
field_name="Full Name"
)
# Python: 'desc' → Airtable: 'Description'
desc: str = airtable_field(
field_name="Description",
field_type=AirtableFieldType.LONG_TEXT
)
Select Field with Choices¶
@airtable_model(table_name="Tasks")
class Task(BaseModel):
title: str
status: str = airtable_field(
field_type=AirtableFieldType.SELECT,
choices=["To Do", "In Progress", "Review", "Done"]
)
tags: List[str] = airtable_field(
field_type=AirtableFieldType.MULTI_SELECT,
choices=["Frontend", "Backend", "Database", "DevOps"]
)
Read-Only Fields¶
Mark computed or system fields as read-only:
@airtable_model(table_name="Records")
class Record(BaseModel):
name: str
# Won't be sent during create/update
record_number: int = airtable_field(
read_only=True
)
# Computed field
formula_result: str = airtable_field(
field_type=AirtableFieldType.FORMULA,
read_only=True
)
Field Options¶
Different field types support different options:
Checkbox Options¶
Currency Options¶
price: float = airtable_field(
field_type=AirtableFieldType.CURRENCY,
json_schema_extra={
"precision": 2, # Decimal places
"symbol": "€" # Currency symbol
}
)
Percent Options¶
rate: float = airtable_field(
field_type=AirtableFieldType.PERCENT,
json_schema_extra={
"precision": 1 # Decimal places
}
)
Number Options¶
count: int = airtable_field(
field_type=AirtableFieldType.NUMBER,
json_schema_extra={
"precision": 0 # Integer (no decimals)
}
)
Working with Enums¶
Basic Enum¶
from enum import Enum
class Priority(str, Enum):
LOW = "Low"
MEDIUM = "Medium"
HIGH = "High"
@airtable_model(table_name="Tasks")
class Task(BaseModel):
title: str
priority: Priority = Priority.MEDIUM
Enum with Values¶
Airtable displays the enum value, not the name:
class Status(str, Enum):
DRAFT = "📝 Draft"
REVIEW = "👀 Under Review"
APPROVED = "✅ Approved"
REJECTED = "❌ Rejected"
# In Airtable, you'll see: "📝 Draft", "👀 Under Review", etc.
Optional Enum¶
class Category(str, Enum):
TECH = "Technology"
BUSINESS = "Business"
PERSONAL = "Personal"
@airtable_model(table_name="Tasks")
class Task(BaseModel):
title: str
category: Optional[Category] = None # Can be unset
Detection Examples¶
Here's a comprehensive example showing various detections:
from pydantic_airtable import airtable_model, airtable_field, AirtableFieldType
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
from enum import Enum
class Priority(str, Enum):
LOW = "Low"
MEDIUM = "Medium"
HIGH = "High"
@airtable_model(table_name="Contacts")
class Contact(BaseModel):
# Auto-detected types
name: str # → SINGLE_LINE_TEXT
email: str # → EMAIL
phone: str # → PHONE
website: Optional[str] = None # → URL
bio: Optional[str] = None # → LONG_TEXT
age: Optional[int] = None # → NUMBER
salary: Optional[float] = None # → CURRENCY
is_active: bool = True # → CHECKBOX
created_at: datetime # → DATETIME
priority: Priority # → SELECT
# Explicit overrides
internal_code: str = airtable_field(
field_name="Internal Code",
field_type=AirtableFieldType.SINGLE_LINE_TEXT
)
tags: List[str] = airtable_field(
field_type=AirtableFieldType.MULTI_SELECT,
choices=["VIP", "Partner", "Lead", "Customer"]
)
Troubleshooting¶
Field Type Not Detected¶
If a field isn't detected as expected:
# Problem: 'status' isn't detected as SELECT
status: str # → SINGLE_LINE_TEXT
# Solution: Use explicit type
status: str = airtable_field(
field_type=AirtableFieldType.SELECT,
choices=["Active", "Inactive"]
)
Wrong Type Detected¶
# Problem: 'rate' detected as PERCENT but you want NUMBER
rate: float # → PERCENT (because of 'rate' in name)
# Solution: Override
rate: float = airtable_field(
field_type=AirtableFieldType.NUMBER
)
Field Name Conflict¶
# Problem: Python name differs from Airtable name
# Airtable has "First Name" but Python can't use spaces
first_name: str = airtable_field(
field_name="First Name"
)
Next Steps¶
- CRUD Operations - Use your models
- Custom Fields - Advanced customization
- Table Management - Create and sync tables