# Child Table (ERPNext glossary)

Canonical: https://erpfly.com/glossary/child-table/
Last updated: September 14, 2026

**Definition:** A Child Table in Frappe and ERPNext is a DocType marked Is Child Table whose records only exist as rows inside a parent document, such as the items on a Sales Order. The parent holds the rows through a Table field, and each row stores its parent, parenttype, parentfield and idx.

Also called: Child DocType, Table field, istable

Line items are everywhere in ERPNext. The items on a Sales Invoice, the taxes below them, the payment schedule, the components on a BOM. Each of those grids is a child table, and each has its own DocType with **Is Child Table** ticked (the flag is stored as `istable`).

### How a row knows where it belongs

A child DocType gets its own database table like any other, `tabSales Order Item` for example. What makes it different is four columns that tie every row to a document:

- `parent`: the name of the parent document, like `SAL-ORD-2026-00012`
- `parenttype`: the parent's DocType, like `Sales Order`
- `parentfield`: the Table field on the parent that holds the row, like `items`
- `idx`: the row's position, starting at 1

On the parent side, you add a field of type **Table** and set its options to the child DocType. One child DocType can sit under several parents, and `parenttype` keeps their rows apart. The **Table MultiSelect** field type also stores its values in a child DocType.

### Reading and adding rows in Python

When you load a parent, its rows come with it as a list on the Table field's name:

```python
import frappe

so = frappe.get_doc("Sales Order", "SAL-ORD-2026-00012")  # a draft order

for row in so.items:
    print(row.idx, row.item_code, row.qty)

so.append("items", {
    "item_code": "BOLT-M8",
    "qty": 200,
    "delivery_date": so.delivery_date,
})
so.save()
```

Saving the parent saves the rows. It also deletes any row in the database that's no longer in the list, so removing an entry from `so.items` and saving is how you delete a line. When the parent is submitted or cancelled, Frappe copies its `docstatus` onto every row.

### What a child table doesn't get

Frappe strips permission rules from a child DocType. Access is always checked against the parent, which is why a direct `frappe.get_list` on a child DocType needs a `parent_doctype` argument before the permission check will pass. A child DocType can't be imported on its own through Data Import, and when you create one in developer mode Frappe generates the Python controller but skips the JS form script and the test file.

**In List View** means something different here. On a child table field it controls which columns appear in the grid on the parent form.

### Where people trip up

**Editing rows behind the parent's back.** `frappe.db.set_value` on a child row writes straight to the table and skips the parent's `validate`. Totals and taxes don't get recalculated. Load the parent, change the row, save the parent.

**Putting row checks in the wrong place.** We put row validation in the parent controller's `validate`, where every row and the header are available together, such as rejecting duplicate item codes.

**Changing standard child tables in core files.** Need an extra column on Sales Order Item? Add a custom field to the child DocType from your own app rather than editing ERPNext's JSON. That's how erpfly handles it: the field is exported as a fixture and ships in a pull request against your app. The [ERPNext customization](https://erpfly.com/erpnext-customization/) page covers how those changes survive upgrades, and the [custom DocType walkthrough](https://erpfly.com/blog/create-custom-doctype-erpnext/) shows creating a child table from scratch.

See also: https://erpfly.com/glossary/doctype/, https://erpfly.com/glossary/custom-field/, https://erpfly.com/glossary/frappe-app/

### Sources

- [Child DocType, Frappe Framework documentation](https://docs.frappe.io/framework/user/en/basics/doctypes/child-doctype)
- [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)
- [frappe/model/document.py (version-15), frappe/frappe on GitHub](https://github.com/frappe/frappe/blob/version-15/frappe/model/document.py)