mcpField
F
frappe-core-database

frappe-core-database

github.com/impertio-studioデータ・データベース198129 回インストール30 週間前に更新
リポジトリ

frappe-core-database について

Frappe 開発者向けのデータベースおよびドキュメント操作リファレンスです。ORM、データベース API、クエリビルダー、キャッシュ、トランザクションを扱い、権限の違いや SQL インジェクション対策も説明します。

主な機能

  1. 01ドキュメントの CRUD 操作
  2. 02権限適用・非適用の一覧クエリ
  3. 03クエリビルダーとパラメーター化 SQL
  4. 04ドキュメントおよび Redis キャッシュ
  5. 05トランザクション管理とバージョン差分

活用シーン

  1. 01Frappe ドキュメントの CRUD 実装
  2. 02ページ分割・条件付きクエリの作成
  3. 03結合クエリと集計レポートの構築
  4. 04SQL インジェクションやトランザクションのリスク確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-core-database

インストール後、Claude Code で/frappe-core-databaseを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
caching-patterns.md
examples.md
methods-reference.md
query-patterns.md

詳細

Frappe データベース操作

クイックリファレンス

操作メソッド権限
ドキュメントを取得frappe.get_doc(doctype, name)あり
キャッシュされたドキュメントfrappe.get_cached_doc(doctype, name)なし
新しいドキュメントfrappe.new_doc(doctype)—
挿入doc.insert()あり
保存doc.save()あり
ドキュメントを削除frappe.delete_doc(doctype, name)あり
リスト(権限あり)frappe.db.get_list(doctype, ...)あり
リスト(権限なし)frappe.get_all(doctype, ...)なし
単一フィールドfrappe.db.get_value(doctype, name, field)なし
単一 DocTypefrappe.db.get_single_value(doctype, field)なし
キャッシュされた値frappe.db.get_value(..., cache=True)なし
直接更新frappe.db.set_value(doctype, name, field, val)なし
直接更新doc.db_set(field, value)なし
存在確認frappe.db.exists(doctype, name)なし
件数frappe.db.count(doctype, filters)なし
行を削除frappe.db.delete(doctype, filters)なし
生 SQLfrappe.db.sql(query, values, as_dict)なし
クエリビルダーfrappe.qb.from_(doctype).select(...)なし

「権限」=ありは、ユーザー権限フィルターが自動的に適用されることを意味します。


判断ツリー

What do you need?
│
├─ Create / Update / Delete a document?
│  ├─ With validations + hooks → frappe.get_doc() + .insert()/.save()/.delete()
│  └─ Direct DB (no hooks) → frappe.db.set_value() or doc.db_set()
│
├─ Read a single document?
│  ├─ Need full object with methods → frappe.get_doc()
│  ├─ Read-only, rarely changes → frappe.get_cached_doc()
│  └─ Only need 1-2 fields → frappe.db.get_value()
│
├─ List of documents?
│  ├─ Respect user permissions → frappe.db.get_list()
│  └─ System/admin context → frappe.get_all()
│
├─ Single DocType value?
│  └─ frappe.db.get_single_value('Settings', 'field')
│
├─ Check existence?
│  └─ frappe.db.exists() — NEVER use get_doc in try/except
│
├─ Complex query (JOINs, aggregates)?
│  ├─ Cross-DB compatible → frappe.qb (Query Builder)
│  └─ DB-specific SQL → frappe.db.sql() with parameters
│
└─ DB-specific logic?
   └─ frappe.db.multisql({'mariadb': q1, 'postgres': q2})

ルール:常に最も抽象度の高いレベルを使用してください:ORM > データベース API > クエリビルダー > 生 SQL。


ORM:ドキュメント操作

ドキュメントを取得
doc = frappe.get_doc('Sales Invoice', 'SINV-00001')

# Single DocType (no name needed)
settings = frappe.get_doc('System Settings')

# Cached (read-only, for rarely-changing docs)
company = frappe.get_cached_doc('Company', 'My Company')

# Last created
last_task = frappe.get_last_doc('Task', filters={'status': 'Open'})
ドキュメントを作成
doc = frappe.get_doc({
    'doctype': 'Task',
    'subject': 'Review report',
    'status': 'Open'
})
doc.insert()

# Alternative
doc = frappe.new_doc('Task')
doc.subject = 'Review report'
doc.insert()
ドキュメントを更新
# Via ORM — triggers validate, on_update, etc.
doc = frappe.get_doc('Task', 'TASK-001')
doc.status = 'Completed'
doc.save()

# Direct DB — SKIPS all validations and hooks
frappe.db.set_value('Task', 'TASK-001', 'status', 'Completed')

# Direct DB on loaded doc
doc.db_set('status', 'Completed')
doc.db_set('status', 'Completed', update_modified=False)
doc.db_set({'status': 'Completed', 'priority': 'High'})
ドキュメントを削除
frappe.delete_doc('Task', 'TASK-001')
# Also removes linked Communications, Comments, etc.
挿入フラグ
doc.insert(
    ignore_permissions=True,     # Bypass permission check
    ignore_links=True,           # Skip link validation
    ignore_if_duplicate=True,    # No error on duplicate
    ignore_mandatory=True        # Skip required field check
)

ルール:文書化された理由がない限り、複数の無視フラグを同時に使用しないでください。追加するフラグは、いずれもデータの整合性を弱めます。


データベース API:読み取り

get_value
# Single field → scalar
status = frappe.db.get_value('Task', 'TASK-001', 'status')

# Multiple fields → tuple
subject, status = frappe.db.get_value('Task', 'TASK-001', ['subject', 'status'])

# As dict
data = frappe.db.get_value('Task', 'TASK-001', ['subject', 'status'], as_dict=True)

# With filters instead of name
status = frappe.db.get_value('Task', {'project': 'PROJ-001'}, 'status')

# Cached (for values that rarely change)
country = frappe.db.get_value('Company', 'MyCompany', 'country', cache=True)
get_single_value
timezone = frappe.db.get_single_value('System Settings', 'time_zone')
get_list / get_all
# get_list — applies user permissions
tasks = frappe.db.get_list('Task',
    filters={'status': 'Open'},
    fields=['name', 'subject', 'assigned_to'],
    order_by='creation desc',
    start=0,
    page_length=50
)

# get_all — NO permission check (same API, different default)
all_tasks = frappe.get_all('Task', filters={'status': 'Open'})

# pluck — returns flat list of single field
names = frappe.get_all('Task', filters={'status': 'Open'}, pluck='name')
# Returns: ['TASK-001', 'TASK-002', ...]
exists / count
exists = frappe.db.exists('User', '[email protected]')
exists = frappe.db.exists('User', {'email': '[email protected]'})

total = frappe.db.count('Task')
open_count = frappe.db.count('Task', {'status': 'Open'})

フィルター演算子

{'status': 'Open'}                                    # =
{'status': ['!=', 'Cancelled']}                       # !=
{'amount': ['>', 1000]}                               # >
{'amount': ['>=', 1000]}                              # >=
{'status': ['in', ['Open', 'Working']]}               # IN
{'status': ['not in', ['Cancelled', 'Closed']]}       # NOT 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
フィルターの組み合わせ
# AND — all conditions in one dict
filters = {'status': 'Open', 'priority': 'High'}

# AND — list format (allows duplicate fields)
filters = [['status', '=', 'Open'], ['priority', '=', 'High']]

# OR — separate parameter
or_filters = {'priority': 'Urgent', 'status': 'Overdue'}

データベース API:書き込み

set_value
# Single field
frappe.db.set_value('Task', 'TASK-001', 'status', 'Closed')

# Multiple fields
frappe.db.set_value('Task', 'TASK-001', {'status': 'Closed', 'priority': 'Low'})

# Without updating modified timestamp
frappe.db.set_value('Task', 'TASK-001', 'status', 'Closed', update_modified=False)
delete / truncate
# Delete with filters (DML — can be rolled back)
frappe.db.delete('Error Log', {'creation': ['<', '2024-01-01']})

# Truncate (DDL — CANNOT be rolled back)
frappe.db.truncate('Error Log')
bulk_update [v15+]
frappe.db.bulk_update('Task', {
    'TASK-001': {'status': 'Closed'},
    'TASK-002': {'status': 'Closed'}
}, chunk_size=100)

生 SQL:必ずパラメーター化する

# ✅ CORRECT — parameterized query
results = frappe.db.sql("""
    SELECT name, subject FROM `tabTask`
    WHERE status = %(status)s AND owner = %(owner)s
""", {'status': 'Open', 'owner': frappe.session.user}, as_dict=True)

重大:SQL 内では、f 文字列、% 書式指定、文字列連結を絶対に使用しないでください。SQL インジェクションの防止を参照してください。

戻り値の型
frappe.db.sql(query)                    # Tuple of tuples (default)
frappe.db.sql(query, as_dict=True)      # List of dicts
frappe.db.sql(query, as_list=True)      # List of lists
テーブル名

バッククォートで囲んだ tab プレフィックスを必ず使用してください:`tabSales Invoice`, `tabTask`

データベース固有の SQL
frappe.db.multisql({
    'mariadb': "SELECT IFNULL(field, 0) FROM `tabDoc`",
    'postgres': "SELECT COALESCE(field, 0) FROM `tabDoc`"
})

クエリビルダー(frappe.qb)[v14+]

クエリビルダーは内部で PyPika を使用します。パラメーター化された SQL を自動的に生成します。

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

results = (
    frappe.qb.from_(Task)
    .select(Task.name, Task.subject, Task.status)
    .where(Task.status == 'Open')
    .orderby(Task.creation, order='desc')
    .limit(10)
).run(as_dict=True)
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, SI.grand_total, Customer.customer_name)
    .where(SI.docstatus == 1)
).run(as_dict=True)
集計
from frappe.query_builder.functions import Count, Sum, Avg

stats = (
    frappe.qb.from_(Task)
    .select(Task.status, Count(Task.name).as_('count'))
    .groupby(Task.status)
).run(as_dict=True)
OR 条件
customers = frappe.qb.DocType('Customer')
results = (
    frappe.qb.from_(customers)
    .select(customers.name)
    .where(
        (customers.territory == 'US') | (customers.territory == 'UK')
    )
).run(as_dict=True)
生成された SQL を確認
query = frappe.qb.from_(Task).select('*').where(Task.name == 'X')
sql, params = query.walk()   # Returns (sql_string, param_dict)
sql_str = query.get_sql()    # Returns SQL string

サブクエリ、ImportMapper、ConstantColumn、カスタム関数については、references/query-patterns.md を参照してください。


キャッシュ

ドキュメントキャッシュ
doc = frappe.get_cached_doc('Company', 'My Company')   # Full document
val = frappe.db.get_value('Company', 'X', 'country', cache=True)  # Single value
Redis キャッシュ
frappe.cache.set_value('key', data, expires_in_sec=3600)
data = frappe.cache.get_value('key')
frappe.cache.delete_value('key')
@redis_cache デコレーター
from frappe.utils.caching import redis_cache

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

# Invalidate
get_dashboard_data.clear_cache()

ハッシュ操作、無効化戦略、ベストプラクティスについては、references/caching-patterns.md を参照してください。


トランザクション管理

フレームワークがトランザクションを自動的に管理します:

コンテキストコミットロールバック
POST/PUT リクエスト成功後捕捉されない例外が発生した場合
GET リクエストなし—
バックグラウンドジョブ成功後例外発生時
パッチ成功後例外発生時
手動トランザクション(ほとんどの場合不要)
frappe.db.savepoint('before_payment')
try:
    # operations...
    frappe.db.commit()
except Exception:
    frappe.db.rollback(save_point='before_payment')
トランザクションフック [v15+]
frappe.db.after_commit.add(sync_to_external_system)
frappe.db.after_rollback.add(cleanup_external_state)

SQL インジェクションの防止

重大なセキュリティルール:ユーザー入力を SQL 文字列に絶対に埋め込まないでください。

# ❌ VULNERABLE — SQL injection risk
frappe.db.sql(f"SELECT * FROM `tabUser` WHERE name = '{user_input}'")
frappe.db.sql("SELECT * FROM `tabUser` WHERE name = '%s'" % user_input)
frappe.db.sql("SELECT * FROM `tabUser` WHERE name = " + user_input)

# ✅ SAFE — parameterized query
frappe.db.sql("SELECT * FROM `tabUser` WHERE name = %(name)s", {'name': user_input})

# ✅ SAFE — ORM / Query Builder (always parameterized)
frappe.get_all('User', filters={'name': user_input})

User = frappe.qb.DocType('User')
frappe.qb.from_(User).select('*').where(User.name == user_input).run()

ルール:frappe.db.sql() を使用しなければならない場合は、辞書と %(param)s プレースホルダーを必ず使用してください。新しいコードでは、生 SQL よりクエリビルダー(frappe.qb)を必ず優先してください。


バージョン間の違い

機能v14v15v16
クエリビルダー(frappe.qb)はいはいはい
トランザクションフックいいえはいはい
bulk_updateいいえはいはい
run=False の戻り値SQL 文字列SQL 文字列クエリビルダーオブジェクト
集計フィールドの構文文字列文字列辞書
v16 の破壊的変更
# v14/v15 — string aggregates
fields=['count(name) as count']

# v16 — dict aggregates
fields=[{'COUNT': 'name', 'as': 'count'}]

# v14/v15 — run=False returns SQL string
sql = frappe.db.get_list('Task', run=False)

# v16 — run=False returns Query Builder object
qb_obj = frappe.db.get_list('Task', run=False)
sql = qb_obj.get_sql()

重要なルールのまとめ

  1. 絶対に SQL で文字列フォーマットを使わないでください — 必ずパラメーター化クエリを使用してください
  2. 絶対にコントローラーフック(validate、on_update など)の内部で frappe.db.commit() を呼び出さないでください
  3. 必ずリストクエリをページネーションしてください — page_length パラメーターを使用してください
  4. 必ずフィールドを指定してください — 本番環境では絶対に fields=['*'] を使用しないでください
  5. 存在確認には必ず frappe.db.exists() を使用してください — get_doc と try/except を絶対に使用しないでください
  6. 生の SQL ではテーブル名の先頭に tab を必ず付けてください:`tabSales Invoice`
  7. ドキュメント化された正当な理由がない限り、複数の ignore フラグを絶対に使用しないでください
  8. N+1 クエリを回避するため、必ず一括取得を使用してください
  9. 新しいコードでは、frappe.db.sql() よりも frappe.qb を必ず優先してください
  10. ロールバックできないことを理解せずに frappe.db.truncate() を絶対に使用しないでください

クエリビルダー:専用スキル

複雑なクエリ(結合、集計、サブクエリ、データベース間の互換性)については、frappe-syntax-query-builder を参照してください。

  • frappe.db メソッド(このスキル) — シンプルな CRUD、get_value、get_list、存在確認
  • frappe.qb(query-builder スキル) — 結合、GROUP BY、HAVING、サブクエリ、データベース間の関数
  • frappe.db.sql — qb では表現できない非常に複雑な SQL(必ずパラメーター化してください)

参照ファイル

  • methods-reference.md — すべてのデータベースメソッドとドキュメントメソッドの API シグネチャー
  • query-patterns.md — クエリビルダーのパターン、サブクエリ、ImportMapper、カスタム関数
  • caching-patterns.md — Redis キャッシュ、@redis_cache、ハッシュ操作、無効化
  • examples.md — 実際のパターン:CRUD、レポート、一括処理、トランザクション
  • anti-patterns.md — SQL インジェクション、N+1、コミットの誤り、その他 10 個のアンチパターン

コメント