frappe-web-forms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Frappe Web Forms

Frappe Web Forms

Build public-facing web forms for data collection, submissions, and customer self-service.
构建用于数据收集、信息提交和客户自助服务的面向公众的Web表单。

When to use

适用场景

  • Creating forms for external users (no Desk access)
  • Building support/ticket submission forms
  • Collecting customer feedback or registrations
  • Enabling self-service data entry portals
  • Replacing simple portal pages with form-based workflows
  • 为外部用户(无Desk权限)创建表单
  • 构建支持/工单提交表单
  • 收集客户反馈或注册信息
  • 搭建自助数据录入门户
  • 用基于表单的工作流替代简单的门户页面

Inputs required

所需输入

  • Target DocType for form submissions
  • Which fields to expose on the web form
  • Authentication requirements (login required vs guest)
  • Whether users can edit/resubmit entries
  • File upload requirements
  • 表单提交对应的目标DocType
  • 要在Web表单上展示的字段
  • 认证要求(是否需要登录 vs 访客访问)
  • 用户是否可以编辑/重新提交条目
  • 文件上传要求

Procedure

操作步骤

0) Prerequisites

0) 前提条件

Ensure the target DocType exists and has the fields you want to expose.
确保目标DocType已存在,且包含你想要展示的字段。

1) Create the Web Form

1) 创建Web表单

  1. Type "new web form" in the awesomebar
  2. Enter a Title (becomes the URL slug)
  3. Select the DocType for record creation
  4. Add introduction text (optional, shown above the form)
  5. Click "Get Fields" to import all fields, or add fields manually
  6. Set field order and which are required
  7. Publish the form
  1. 在awesomebar中输入“new web form”
  2. 输入标题(将作为URL路径别名)
  3. 选择用于创建记录的DocType
  4. 添加介绍文本(可选,显示在表单上方)
  5. 点击“Get Fields”导入所有字段,或手动添加字段
  6. 设置字段顺序和必填项
  7. 发布表单

2) Configure settings

2) 配置设置

SettingPurpose
Login RequiredRequire authentication before form access
Allow EditLet users edit their submitted entries
Allow MultipleLet users submit more than one entry
Show as CardDisplay in card layout style
Max Attachment SizeLimit file upload sizes
Success URLRedirect after successful submission
Success MessageCustom message after submission
设置项用途
Login Required要求用户先认证才能访问表单
Allow Edit允许用户编辑已提交的条目
Allow Multiple允许用户提交多条条目
Show as Card以卡片布局样式显示
Max Attachment Size限制文件上传大小
Success URL提交成功后的跳转地址
Success Message提交成功后的自定义提示信息

3) Make it a Standard Web Form (app-bundled)

3) 设为标准Web表单(应用捆绑)

Check "Is Standard" (visible in Developer Mode) to export the form as files:
my_app/
└── my_module/
    └── web_form/
        └── contact_us/
            ├── contact_us.json    # Web form metadata
            ├── contact_us.py      # Server-side customization
            └── contact_us.js      # Client-side customization
勾选“Is Standard”(仅在开发者模式下可见),将表单导出为文件:
my_app/
└── my_module/
    └── web_form/
        └── contact_us/
            ├── contact_us.json    # Web form metadata
            ├── contact_us.py      # Server-side customization
            └── contact_us.js      # Client-side customization

4) Add server-side customization

4) 添加后端自定义

python
undefined
python
undefined

contact_us.py

contact_us.py

import frappe
def get_context(context): """Add custom context variables to the web form.""" context.categories = frappe.get_all("Support Category", filters={"enabled": 1}, fields=["name", "label"], order_by="label asc" )
def validate(doc): """Custom validation before the document is saved.""" if not doc.email: frappe.throw("Email address is required")
# Prevent duplicate submissions
existing = frappe.db.exists("Support Ticket", {"email": doc.email, "status": "Open"})
if existing:
    frappe.throw("You already have an open ticket. Please wait for a response.")
undefined
import frappe
def get_context(context): """Add custom context variables to the web form.""" context.categories = frappe.get_all("Support Category", filters={"enabled": 1}, fields=["name", "label"], order_by="label asc" )
def validate(doc): """Custom validation before the document is saved.""" if not doc.email: frappe.throw("Email address is required")
# Prevent duplicate submissions
existing = frappe.db.exists("Support Ticket", {"email": doc.email, "status": "Open"})
if existing:
    frappe.throw("You already have an open ticket. Please wait for a response.")
undefined

5) Add client-side customization

5) 添加前端自定义

javascript
// contact_us.js
frappe.ready(function() {
    // Handle field changes
    frappe.web_form.on("field_change", function(field, value) {
        if (field === "category" && value === "Urgent") {
            frappe.web_form.set_df_property("description", "reqd", 1);
        }
    });

    // Custom validation
    frappe.web_form.validate = function() {
        let data = frappe.web_form.get_values();
        if (data.phone && !data.phone.match(/^\+?[0-9\-\s]+$/)) {
            frappe.msgprint("Please enter a valid phone number");
            return false;
        }
        return true;
    };

    // Custom after-save behavior
    frappe.web_form.after_save = function() {
        frappe.msgprint("Thank you for your submission!");
    };
});
javascript
// contact_us.js
frappe.ready(function() {
    // Handle field changes
    frappe.web_form.on("field_change", function(field, value) {
        if (field === "category" && value === "Urgent") {
            frappe.web_form.set_df_property("description", "reqd", 1);
        }
    });

    // Custom validation
    frappe.web_form.validate = function() {
        let data = frappe.web_form.get_values();
        if (data.phone && !data.phone.match(/^\+?[0-9\-\s]+$/)) {
            frappe.msgprint("Please enter a valid phone number");
            return false;
        }
        return true;
    };

    // Custom after-save behavior
    frappe.web_form.after_save = function() {
        frappe.msgprint("Thank you for your submission!");
    };
});

6) Control permissions

6) 权限控制

  • Guest access: Uncheck "Login Required" for fully public forms
  • Portal roles: Assign portal roles to control which logged-in users see the form
  • User permissions: Set explicit document-level permissions on the target DocType
  • Row-level access: Use User Permission rules to restrict which records users can edit
  • 访客访问:取消勾选“Login Required”以设置完全公开的表单
  • 门户角色:分配门户角色来控制哪些已登录用户可以看到该表单
  • 用户权限:在目标DocType上设置明确的文档级权限
  • 行级访问:使用用户权限规则限制用户可编辑的记录

7) Style the web form

7) 自定义Web表单样式

Web forms use the website theme by default. For custom styling:
html
<!-- Add custom CSS via Web Form → Custom CSS field -->
<style>
    .web-form-container { max-width: 600px; margin: 0 auto; }
    .web-form-container .form-group { margin-bottom: 1.5rem; }
    .web-form-container .btn-primary { background-color: #2490EF; }
</style>
Web表单默认使用网站主题。如需自定义样式:
html
<!-- Add custom CSS via Web Form → Custom CSS field -->
<style>
    .web-form-container { max-width: 600px; margin: 0 auto; }
    .web-form-container .form-group { margin-bottom: 1.5rem; }
    .web-form-container .btn-primary { background-color: #2490EF; }
</style>

Verification

验证步骤

  • Web form accessible at the correct URL (
    /contact-us
    )
  • All fields render correctly
  • Required field validation works
  • Submission creates the correct DocType record
  • Login requirement enforced (if configured)
  • Edit and resubmit work (if configured)
  • File uploads work within size limits
  • Success message/redirect works after submission
  • Custom Python validation runs on submit
  • Web表单可通过正确的URL访问(如
    /contact-us
  • 所有字段正确渲染
  • 必填字段验证功能正常
  • 提交操作可创建正确的DocType记录
  • 登录要求已生效(若已配置)
  • 编辑和重新提交功能正常(若已配置)
  • 文件上传在大小限制内可正常工作
  • 提交成功后的提示信息/跳转功能正常
  • 自定义Python验证在提交时运行

Failure modes / debugging

故障模式与调试

  • Form not accessible: Check if published; verify URL slug
  • Permission denied on submit: Check DocType permissions for Website User or Guest
  • Fields not showing: Ensure fields are added to the Web Form (not just on the DocType)
  • Custom JS not loading: Check browser console; ensure file path is correct
  • Validation not firing: Verify
    validate
    function in Python file returns/throws correctly
  • Duplicate entries: Check "Allow Multiple" setting; add custom duplicate detection
  • 表单无法访问:检查是否已发布;验证URL路径别名
  • 提交时权限被拒绝:检查Website User或Guest角色的DocType权限
  • 字段未显示:确保字段已添加到Web表单(而不仅仅是在DocType上)
  • 自定义JS未加载:检查浏览器控制台;确保文件路径正确
  • 验证未触发:验证Python文件中的
    validate
    函数是否正确返回/抛出异常
  • 重复条目:检查“Allow Multiple”设置;添加自定义重复检测逻辑

Escalation

问题升级渠道

  • For DocType schema →
    frappe-doctype-development
  • For Frappe UI portal apps →
    frappe-frontend-development
  • For API endpoint access →
    frappe-api-development
  • 关于DocType架构 →
    frappe-doctype-development
  • 关于Frappe UI门户应用 →
    frappe-frontend-development
  • 关于API端点访问 →
    frappe-api-development

References

参考资料

  • references/web-forms.md — Web Form creation and customization
  • references/web-forms.md — Web表单创建与自定义

Guardrails

安全防护措施

  • Validate input server-side: Never trust client validation; check in
    validate()
    Python method
  • Use captcha for public forms: Enable reCAPTCHA for guest-accessible forms to prevent spam
  • Sanitize output: Escape user-submitted data when displaying; use
    frappe.utils.escape_html()
  • Limit file uploads: Set max file size and allowed types for attachment fields
  • Check rate limits: Consider throttling form submissions from same IP
  • 后端验证输入:永远不要信任客户端验证;在Python的
    validate()
    方法中进行检查
  • 公共表单使用验证码:为访客可访问的表单启用reCAPTCHA以防止垃圾信息
  • 清理输出内容:显示用户提交的数据时进行转义;使用
    frappe.utils.escape_html()
  • 限制文件上传:为附件字段设置最大文件大小和允许的类型
  • 检查速率限制:考虑对同一IP的表单提交进行限流

Common Mistakes

常见错误

MistakeWhy It FailsFix
Missing DocType permissions"Permission denied" on submitGrant Create permission to Website User or Guest role
Not handling file uploadsFiles don't attach to recordConfigure Attach field properly; check upload limits
XSS vulnerabilitiesSecurity riskEscape user input in display; use `
Forgetting to publish form404 errorCheck "Published" checkbox in Web Form
Client-only validationInvalid data in databaseAdd
validate()
method in web form Python file
Not testing as guest userWorks for admin, fails for usersTest in incognito/logged out mode
错误失败原因修复方案
缺少DocType权限提交时提示“Permission denied”为Website User或Guest角色授予创建权限
未处理文件上传文件未附加到记录正确配置Attach字段;检查上传限制
XSS漏洞安全风险在显示时转义用户输入;在模板中使用`
忘记发布表单出现404错误检查Web表单中的“Published”复选框
仅客户端验证数据库中存在无效数据在Web表单的Python文件中添加
validate()
方法
未以访客用户身份测试管理员可用,但普通用户无法使用在隐身模式/注销状态下测试