Frappe Fixture

migrate마다 데이터를 덮어쓰는 동기화 시스템

Frappe fixture는 bench migrate 시 JSON 파일로 DB 레코드를 자동 동기화한다. UI에서 수정한 내용이 migrate마다 원본으로 롤백되는 문제가 생길 수 있고, fixture에서 제외하거나 hooks.py 설정으로 제어해야 한다.

Database

개념

Frappe fixture는 JSON 파일로 DB 레코드를 버전 관리하는 시스템이다. 일반적인 DB 마이그레이션(Alembic)이 스키마 변경을 관리한다면, fixture는 데이터(레코드) 자체를 git에 올려서 bench migrate 때마다 JSON → DB로 동기화한다.

hooks.py에 fixture 선언 → bench export-fixtures로 JSON 생성
→ bench migrate 시 JSON → DB로 삽입/업데이트

왜 필요한가

Frappe 앱에는 "코드처럼 관리하고 싶은 데이터"가 있다. 예: Email Template, Custom Field, Print Format. 이런 건 스키마가 아니라 레코드지만 앱의 일부로 배포돼야 한다. fixture는 이 요구를 "JSON 파일을 git에 올리고 migrate가 동기화"하는 방식으로 푼다.

설정 방법

hooks.py에서 선언한다.

# 전체 문서 export
fixtures = ["Email Template"]

# 필터로 특정 문서만
fixtures = [
    {
        "doctype": "Email Template",
        "filters": ["name", "in", ["invite", "welcome"](../../note/%22name%22%2C%20%22in%22%2C%20%5B%22invite%22%2C%20%22welcome%22)]
    }
]
bench export-fixtures    # DB → JSON 파일로 추출
bench migrate            # JSON → DB로 동기화

파일 위치

앱이름/
  fixtures/
    email_template.json    # DocType 이름의 snake_case

JSON 안에 해당 DocType의 레코드들이 배열로 들어 있다.

함정: UI 수정이 migrate에서 날아감

  1. UI에서 Email Template "invite"의 HTML을 수정
  2. bench migrate 실행
  3. fixture JSON의 원본 버전으로 덮어쓰기됨
  4. UI에서 수정한 내용 사라짐

이건 의도된 동작이다. fixture는 "이 데이터는 코드가 관리한다"는 선언이니까. 문제는 이 사실을 모르면 데이터 분실처럼 느껴지는 것.

해결 패턴

1. fixture에서 제외

사용자가 UI에서 커스텀할 문서는 fixture에서 빼고, 초기 세팅은 after_install 훅으로 처리.

# hooks.py
fixtures = [
    {
        "doctype": "Email Template",
        "filters": ["name", "not in", ["invite"](../../note/%22name%22%2C%20%22not%20in%22%2C%20%5B%22invite%22)]  # invite는 제외
    }
]

2. fixture JSON을 직접 수정

UI가 아니라 JSON 파일을 직접 수정하고 커밋. 이러면 migrate 후에도 최신 버전이 반영됨.

3. after_migrate 훅으로 조건부 생성

# hooks.py
after_migrate = ["앱이름.setup.create_default_templates"]
def create_default_templates():
    if not frappe.db.exists("Email Template", "invite"):
        # 없을 때만 생성, 있으면 건드리지 않음
        frappe.get_doc({...}).insert()

"초기값만 필요하고 이후 수정은 UI에서"라는 시나리오에 맞다.

use_html 필드 주의

Email Template에는 responseresponse_html 두 필드가 있다.

  • UI에서 HTML 편집기로 저장하면 use_html=1 + response_html에 저장
  • fixture JSON은 보통 response 필드에 저장

코드에서 템플릿 본문을 읽을 때 둘 다 체크해야 한다.

template = frappe.get_doc("Email Template", "invite")
body = template.response_html or template.response

response만 읽으면 UI에서 HTML로 작성한 내용이 무시된다.

언제 쓰나

상황 방법
코드가 관리하는 데이터 fixture로 관리, UI 수정 금지
사용자가 커스텀하는 데이터 fixture에서 제외
초기값만 필요한 데이터 after_install/after_migrate 훅으로 조건부 생성

더 보기

  • Alembic — 스키마 변경 관리 (fixture와 역할이 다름)

Related

sunshinemoon · 2026