erpnext-database

ERPNext Database Operations

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "erpnext-database" with this command: npx skills add openaec-foundation/erpnext_anthropic_claude_development_skill_package/openaec-foundation-erpnext-anthropic-claude-development-skill-package-erpnext-database

ERPNext Database Operations

Quick Overview

Frappe provides three abstraction levels for database operations:

Level API Usage

High-level ORM frappe.get_doc , frappe.new_doc

Document CRUD with validations

Mid-level Query frappe.db.get_list , frappe.db.get_value

Reading with filters

Low-level SQL frappe.db.sql , frappe.qb

Complex queries, reports

RULE: Always use the highest abstraction level appropriate for your use case.

Decision Tree

What do you want to do? │ ├─ Create/modify/delete document? │ └─ frappe.get_doc() + .insert()/.save()/.delete() │ ├─ Get single document? │ ├─ Changes frequently? → frappe.get_doc() │ └─ Changes rarely? → frappe.get_cached_doc() │ ├─ List of documents? │ ├─ With user permissions? → frappe.db.get_list() │ └─ Without permissions? → frappe.get_all() │ ├─ Single field value? │ ├─ Regular DocType → frappe.db.get_value() │ └─ Single DocType → frappe.db.get_single_value() │ ├─ Direct update without triggers? │ └─ frappe.db.set_value() or doc.db_set() │ └─ Complex query with JOINs? └─ frappe.qb (Query Builder) or frappe.db.sql()

Most Used Patterns

Get Document

With ORM (triggers validations)

doc = frappe.get_doc('Sales Invoice', 'SINV-00001')

Cached (faster for frequently accessed docs)

doc = frappe.get_cached_doc('Company', 'My Company')

List Query

With user permissions

tasks = frappe.db.get_list('Task', filters={'status': 'Open'}, fields=['name', 'subject'], order_by='creation desc', page_length=50 )

Without permissions

all_tasks = frappe.get_all('Task', filters={'status': 'Open'})

Single Value

Single field

status = frappe.db.get_value('Task', 'TASK001', 'status')

Multiple fields

subject, status = frappe.db.get_value('Task', 'TASK001', ['subject', 'status'])

As dict

data = frappe.db.get_value('Task', 'TASK001', ['subject', 'status'], as_dict=True)

Create Document

doc = frappe.get_doc({ 'doctype': 'Task', 'subject': 'New Task', 'status': 'Open' }) doc.insert()

Update Document

Via ORM (with validations)

doc = frappe.get_doc('Task', 'TASK001') doc.status = 'Completed' doc.save()

Direct (without validations) - use carefully!

frappe.db.set_value('Task', 'TASK001', 'status', 'Completed')

Filter Operators

{'status': 'Open'} # = {'status': ['!=', 'Cancelled']} # != {'amount': ['>', 1000]} # > {'amount': ['>=', 1000]} # >= {'status': ['in', ['Open', 'Working']]} # IN {'date': ['between', ['2024-01-01', '2024-12-31']]} # BETWEEN {'subject': ['like', '%urgent%']} # LIKE {'description': ['is', 'set']} # IS NOT NULL {'description': ['is', 'not set']} # IS NULL

Query Builder (frappe.qb)

Task = frappe.qb.DocType('Task')

results = ( frappe.qb.from_(Task) .select(Task.name, Task.subject) .where(Task.status == 'Open') .orderby(Task.creation, order='desc') .limit(10) ).run(as_dict=True)

With JOIN

SI = frappe.qb.DocType('Sales Invoice') Customer = frappe.qb.DocType('Customer')

results = ( frappe.qb.from_(SI) .inner_join(Customer) .on(SI.customer == Customer.name) .select(SI.name, Customer.customer_name) .where(SI.docstatus == 1) ).run(as_dict=True)

Caching

Basics

Set/Get

frappe.cache.set_value('key', 'value') value = frappe.cache.get_value('key')

With expiry

frappe.cache.set_value('key', 'value', expires_in_sec=3600)

Delete

frappe.cache.delete_value('key')

@redis_cache Decorator

from frappe.utils.caching import redis_cache

@redis_cache(ttl=300) # 5 minutes def get_dashboard_data(user): return expensive_calculation(user)

Invalidate cache

get_dashboard_data.clear_cache()

Transactions

Framework manages transactions automatically:

Context Commit Rollback

POST/PUT request After success On exception

Background job After success On exception

Manual (rarely needed)

frappe.db.savepoint('my_savepoint') try: # operations frappe.db.commit() except: frappe.db.rollback(save_point='my_savepoint')

Critical Rules

  1. NEVER Use String Formatting in SQL

❌ SQL Injection risk!

frappe.db.sql(f"SELECT * FROM tabUser WHERE name = '{user_input}'")

✅ Parameterized

frappe.db.sql("SELECT * FROM tabUser WHERE name = %(name)s", {'name': user_input})

  1. NEVER Commit in Controller Hooks

❌ WRONG

def validate(self): frappe.db.commit() # Never do this!

✅ Framework handles commits

  1. ALWAYS Paginate

✅ Always limit

docs = frappe.get_all('Sales Invoice', page_length=100)

  1. Avoid N+1 Queries

❌ N+1 problem

for name in names: doc = frappe.get_doc('Customer', name)

✅ Batch fetch

docs = frappe.get_all('Customer', filters={'name': ['in', names]})

Version Differences

Feature v14 v15 v16

Transaction hooks ❌ ✅ ✅

bulk_update ❌ ✅ ✅

Aggregate syntax String String Dict

v16 Aggregate Syntax

v14/v15

fields=['count(name) as count']

v16

fields=[{'COUNT': 'name', 'as': 'count'}]

Reference Files

See the references/ folder for detailed documentation:

  • methods-reference.md - All Database and Document API methods

  • query-patterns.md - Filter operators and Query Builder syntax

  • caching-patterns.md - Redis cache patterns and @redis_cache

  • examples.md - Complete working examples

  • anti-patterns.md - Common mistakes and how to avoid them

Quick Reference

Action Method

Get document frappe.get_doc(doctype, name)

Cached document frappe.get_cached_doc(doctype, name)

New document frappe.new_doc(doctype) or frappe.get_doc({...})

Save document doc.save()

Insert document doc.insert()

Delete document doc.delete() or frappe.delete_doc()

Get list frappe.db.get_list() / frappe.get_all()

Single value frappe.db.get_value()

Single value frappe.db.get_single_value()

Direct update frappe.db.set_value() / doc.db_set()

Exists check frappe.db.exists()

Count records frappe.db.count()

Raw SQL frappe.db.sql()

Query Builder frappe.qb.from_()

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

erpnext-code-interpreter

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

erpnext-syntax-jinja

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

erpnext-impl-controllers

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

erpnext-syntax-customapp

No summary provided by upstream source.

Repository SourceNeeds Review