วิธี Sync Workspace ใน Frappe ด้วย Delete+Reinsert Pattern
เรียนรู้วิธีแก้ปัญหา Frappe Workspace ไม่ Update เมื่อรัน bench migrate ด้วย Delete+Reinsert Pattern พร้อมตัวอย่าง Workspace Manager

หนึ่งในปัญหาที่นักพัฒนา Frappe/ERPNext พบบ่อยคือ Workspace ไม่ Update เมื่อรัน bench migrate บทความนี้จะอธิบายปัญหาและวิธีแก้ไข
ปัญหา
เมื่อเราแก้ไขไฟล์ JSON ของ Workspace และรัน bench migrate:
- Workspace ใหม่ถูกสร้าง ✓
- Workspace ที่มีอยู่แล้ว ไม่ถูก Update ✗
เหตุผลคือ Frappe ถือว่า Workspace เป็น User-customizable Data ไม่ใช่ Configuration ดังนั้น:
bench migrateไม่ Overwrite Workspace Records ที่มีอยู่bench updateไม่ Apply JSON Changesfrappe.reload_doc()ไม่ Work สำหรับ Workspace
วิธีแก้ไข
วิธีที่ 1: Manual ผ่าน Desk UI
- ไปที่ Workspace List
- ลบ Workspace ที่ต้องการ Update
- รัน
bench migrate - Workspace จะถูกสร้างใหม่จาก JSON
วิธีที่ 2: Programmatic ด้วย workspace_manager.py
สร้างไฟล์ your_app/setup/workspace_manager.py:
import frappe
def sync_workspaces():
"""ลบและสร้าง Workspace ใหม่เพื่อ Apply JSON Changes"""
workspaces_to_sync = [
"Your Workspace 1",
"Your Workspace 2",
]
for ws_name in workspaces_to_sync:
if frappe.db.exists("Workspace", ws_name):
frappe.delete_doc("Workspace", ws_name, force=True)
frappe.db.commit()
print(f"Deleted workspace: {ws_name}")
# Workspace จะถูกสร้างใหม่เมื่อรัน migrate
print("Run 'bench migrate' to recreate workspaces")
การใช้งาน
# รัน Sync
bench --site your-site.local execute your_app.setup.workspace_manager.sync_workspaces
# จากนั้น Migrate เพื่อสร้างใหม่
bench --site your-site.local migrate
วิธีที่ 3: Hook เข้า after_migrate
ใน hooks.py:
after_migrate = [
"your_app.setup.workspace_manager.sync_workspaces"
]
ข้อควรระวัง: วิธีนี้จะ Sync ทุกครั้งที่ Migrate อาจไม่เหมาะสำหรับทุกกรณี
Delete + Reinsert Strategy แบบเต็ม
สำหรับกรณีที่ต้องการควบคุมละเอียด:
import frappe
import json
def sync_workspace(workspace_name: str):
"""Delete และ Reinsert Workspace พร้อม Child Tables"""
# 1. ลบ Workspace และ Child Tables ที่เกี่ยวข้อง
if frappe.db.exists("Workspace", workspace_name):
frappe.db.delete("Workspace", {"name": workspace_name})
frappe.db.delete("Workspace Link", {"parent": workspace_name})
frappe.db.delete("Workspace Shortcut", {"parent": workspace_name})
frappe.db.delete("Workspace Chart", {"parent": workspace_name})
frappe.db.delete("Workspace Number Card", {"parent": workspace_name})
frappe.db.commit()
print(f"Deleted: {workspace_name}")
# 2. โหลด JSON File
workspace_json_path = frappe.get_app_path(
"your_app",
"your_module",
"workspace",
workspace_name.lower().replace(" ", "_"),
f"{workspace_name.lower().replace(' ', '_')}.json"
)
with open(workspace_json_path, 'r', encoding='utf-8') as f:
workspace_data = json.load(f)
# 3. Insert ใหม่
doc = frappe.get_doc(workspace_data)
doc.flags.ignore_permissions = True
doc.flags.ignore_links = True
doc.insert(ignore_permissions=True)
# 4. Commit และ Clear Cache
frappe.db.commit()
frappe.cache().flushall()
print(f"Recreated: {workspace_name}")
def sync_all_workspaces():
"""Sync ทุก Workspaces ของ App"""
workspaces = [
"Dashboard",
"Settings",
"Reports",
]
for ws in workspaces:
sync_workspace(ws)
ข้อควรระวัง
- Backup ก่อน Delete - User Customizations จะหายไป
- ระบุเฉพาะ Workspaces ของ App - อย่าลบ Workspaces ของ Frappe/ERPNext Core
- Clear Cache - ต้อง Clear Redis Cache หลัง Sync
โครงสร้าง Workspace JSON
{
"doctype": "Workspace",
"name": "Your Workspace",
"label": "Your Workspace",
"module": "Your Module",
"icon": "organization",
"public": 1,
"content": "[...]",
"links": [...],
"shortcuts": [...],
"charts": [...],
"number_cards": [...]
}
สรุป
Frappe Workspace ไม่ Update อัตโนมัติเมื่อ Migrate เพราะถูกออกแบบให้ User สามารถ Customize ได้ วิธีแก้คือใช้ Delete + Reinsert Pattern:
- ลบ Workspace ที่มีอยู่
- รัน
bench migrateเพื่อสร้างใหม่จาก JSON - Clear Cache
หรือใช้ Workspace Manager Script เพื่อ Automate Process นี้