This handles the full lifecycle of Frappe DocTypes, from creating new data models to wiring up controller logic and permissions. You'll reach for it when building standard documents, child tables, or submittable workflows. It covers the practical stuff: field definitions across 20+ types, naming series setup, controller hooks like validate and before_save, and the parent-child table patterns that trip people up. The guardrails are solid, reminding you to enable developer mode before schema changes and always run migrate after modifications. Honestly, the structured approach to controller implementation and the comprehensive field type reference make this more useful than the official docs for day-to-day DocType work.
npx -y skills add lubusin/agent-skills --skill frappe-doctype-development --agent claude-codeInstalls into .claude/skills of the current project.
Build and modify DocTypes—the core data model abstraction in Frappe Framework.
# Ensure developer mode is enabled
bench --site <site> console
>>> frappe.conf.developer_mode # Must be True
| Type | Use Case | Key Setting |
|---|---|---|
| Standard | Multiple records | Default |
| Single | Config/settings (one record) | issingle: 1 |
| Child Table | Rows in parent table | istable: 1 |
| Submittable | Draft→Submit→Cancel workflow | is_submittable: 1 |
| Tree | Hierarchical data | is_tree: 1 |
| Virtual | External data source | is_virtual: 1 |
Option A: Via UI (recommended for new DocTypes)
Option B: Via code
# Create DocType JSON in:
# <app>/<module>/doctype/<doctype_name>/<doctype_name>.json
Common field patterns:
{
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer",
"reqd": 1
}
See references/field-types.md for all field types.
Create <doctype_name>.py alongside the JSON:
import frappe
from frappe.model.document import Document
class MyDocType(Document):
def validate(self):
# Lightweight validation
if not self.customer:
frappe.throw("Customer is required")
def before_save(self):
# Pre-save normalization
self.full_name = f"{self.first_name} {self.last_name}"
def after_insert(self):
# Post-create side effects
frappe.publish_realtime("new_doc", {"name": self.name})
{
"autoname": "naming_series:",
"fields": [
{
"fieldname": "naming_series",
"fieldtype": "Select",
"options": "PRJ-.YYYY.-\nPRJ-.YYYY.-.###"
}
]
}
Options: field:fieldname, naming_series:, hash, format:PREFIX-{####}
Set in DocType → Permissions tab:
Create Workflow DocType linking to your DocType with states and transitions.
bench --site <site> migrate succeedsdeveloper_mode = 1 in site configbench --site <site> migrate to apply| Mistake | Why It Fails | Fix |
|---|---|---|
Missing reqd on mandatory fields | Users can save incomplete data | Set reqd: 1 on fields that must have values |
| Wrong fieldtype for data | Data truncation or validation errors | Match fieldtype to data (e.g., Currency for money, not Float) |
Not running bench migrate | Schema changes not applied to database | Always run bench --site <site> migrate after DocType changes |
| Circular Link dependencies | DocType creation fails | Use Dynamic Link or restructure relationships |
| Controller class name mismatch | Controller methods not called | Class name must be PascalCase of DocType name (e.g., SalesOrder for "Sales Order") |
Missing in_list_view on key fields | Fields not visible in list | Set in_list_view: 1 on important fields |
sickn33/antigravity-awesome-skills
moizibnyousaf/ai-agent-skills
github/awesome-copilot