# DocType (ERPNext glossary)

Canonical: https://erpfly.com/glossary/doctype/
Last updated: September 14, 2026

**Definition:** A DocType is the basic data model in the Frappe Framework, which ERPNext is built on. It defines a type of record, its fields, naming rule, permissions and form layout, and Frappe creates a matching database table plus a list view and form view from that definition.

Also called: Doc Type, Frappe DocType, ERPNext DocType

Open anything in the ERPNext desk and you're looking at a document of some DocType. Sales Invoice, Customer, Item, Employee: each one is a DocType, and so is the form you use to define new ones. Once you see ERPNext this way, most of the framework starts to make sense.

### What a DocType decides

When you save a DocType, Frappe creates or alters a table in the site database. The table name is the DocType name with a `tab` prefix, so `Library Member` becomes `tabLibrary Member`. By convention the name is singular: Article, not Articles.

The definition covers:

- **Fields.** Each row is a DocField with a type such as Data, Link, Currency, Date, Select or Table. Layout types like Section Break and Tab Break shape the form but don't add columns.
- **Standard fields** you never add yourself: `name`, `owner`, `creation`, `modified`, `modified_by`, `docstatus` and `idx`.
- **Naming**, from a naming series to a field value or a hash.
- **Permissions** per role: read, write, create, submit, cancel and so on.
- **Behaviour flags.** Is Submittable gives documents the draft, submitted and cancelled states (`docstatus` 0, 1 and 2). Is Child Table makes it a child table that only lives inside a parent. Is Single keeps one record, which suits settings pages. Is Tree and Is Virtual cover hierarchies and data stored outside the database.

### Standard and custom DocTypes

This distinction causes more confusion than any other. A standard DocType belongs to a module inside a Frappe app. With developer mode on, Frappe writes its definition to a JSON file in that app, alongside a Python controller, and `bench migrate` applies it to any other site that installs the app.

A DocType with **Custom** ticked is stored only in the database. Frappe won't let you create a standard one without developer mode; it tells you to switch developer mode on or make the DocType custom. Custom DocTypes are fine for experiments, but they have no controller file and no Git history.

Changing a DocType that ships with ERPNext is a different job again. You don't edit its JSON in the erpnext folder. You add a custom field or a property setter and keep those in your own app. That's the rule erpfly follows too: new DocTypes go into your app as standard ones, and changes to ERPNext's own forms are exported as fixtures.

### What it looks like on disk

A standard DocType named Library Member in a `library_management` app produces a folder at `apps/library_management/library_management/library_management/doctype/library_member/` containing `library_member.json` (the definition), `library_member.py` (the controller), `library_member.js` (the form script) and a test file. Logic goes in the controller:

```python
import frappe
from frappe.model.document import Document
from frappe.utils import getdate


class LibraryMember(Document):
    def validate(self):
        if self.membership_end and getdate(self.membership_end) < getdate(self.membership_start):
            frappe.throw("Membership can't end before it starts")
```

`validate` runs on every insert and save. Other controller methods, such as `on_update`, `on_submit` and `on_cancel`, hook into later points of the document lifecycle.

### Mistakes we keep seeing

Careless fieldnames come first. A label can change any time, but the fieldname is a database column that reports, print formats and code refer to. Next is building DocTypes directly on production, where they end up as database-only records that no other site has. Then there's adding a whole new DocType when two custom fields on Customer would have done the job.

For the full sequence, from developer mode to naming rules and permissions, follow our [step-by-step guide to creating a custom DocType](https://erpfly.com/blog/create-custom-doctype-erpnext/).

See also: https://erpfly.com/glossary/child-table/, https://erpfly.com/glossary/frappe-app/, https://erpfly.com/glossary/custom-field/, https://erpfly.com/glossary/fixtures/

### Sources

- [DocTypes, Frappe Framework documentation](https://docs.frappe.io/framework/user/en/basics/doctypes)
- [Controllers, Frappe Framework documentation](https://docs.frappe.io/framework/user/en/basics/doctypes/controllers)
- [frappe/core/doctype/doctype/doctype.py (version-15), frappe/frappe on GitHub](https://github.com/frappe/frappe/blob/version-15/frappe/core/doctype/doctype/doctype.py)