Frappe Server ScriptingPython

How to Create a Custom Server-Side Method for Calendar Views in Frappe

Calendar View Method

Learn to implement a whitelisted Python method in Frappe to fetch dynamic data for Calendar and Gantt views using frappe.db.sql and JSON filters.

In the Frappe framework, Calendar and Gantt views are powerful tools for visualizing time-based data. These views are not magic; they are powered by a dedicated server-side Python method that fetches and formats the necessary event data. This method is typically named `get_events`.

This snippet demonstrates the standard structure of a `get_events` function. It's a whitelisted server script that accepts start and end date parameters from the calendar UI, executes a custom SQL query to retrieve relevant records, and returns them in a format that the calendar component can render.

python
1import frappe
2import json
3
4@frappe.whitelist()
5def get_events(start, end, filters=None):
6 """Returns events for Gantt / Calendar view rendering.
7 :param start: Start date-time.
8 :param end: End date-time.
9 :param filters: Filters (JSON).
10 """
11 filters = json.loads(filters) if filters else {}
12
13 # Note: get_conditions is a hypothetical helper function to build WHERE clauses
14 # You would implement this based on the filters your calendar needs.
15 # from frappe.desk.calendar import get_event_conditions
16 # conditions = get_event_conditions("Timesheet", filters)
17
18 conditions = ""
19 # Example of building conditions from filters
20 # if filters.get('project'):
21 # conditions += f" AND project = '{filters.get('project')}'"
22
23 return frappe.db.sql("""select
24 `tabTimesheet Detail`.name as name,
25 `tabTimesheet Detail`.docstatus as status,
26 `tabTimesheet Detail`.parent as parent,
27 from_time as start_date,
28 hours,
29 activity_type,
30 project,
31 to_time as end_date,
32 CONCAT(`tabTimesheet Detail`.parent, ' (', ROUND(hours,2),' hrs)') as title
33 from `tabTimesheet Detail`, `tabTimesheet`
34 where `tabTimesheet Detail`.parent = `tabTimesheet`.name
35 and `tabTimesheet`.docstatus < 2
36 and (from_time <= %(end)s and to_time >= %(start)s) {conditions}
37 """.format(conditions=conditions),
38 {
39 "start": start,
40 "end": end
41 }, as_dict=True, update={"allDay": 0})

Understanding This Code

What It Does

This Python method fetches records from the Timesheet DocType, formatted as events, for rendering in Frappe's standard Calendar or Gantt views.

When To Use

Use this pattern when creating a custom calendar view for any DocType. This server-side script is required and must be paired with a corresponding client-side JavaScript file that configures the calendar view.

Prerequisites

  • A DocType with date or datetime fields (e.g., start_date, end_date).
  • Basic understanding of Frappe's server-side scripting.
  • Familiarity with SQL queries.

Key Concepts

Important ideas to understand in this code

@frappe.whitelist() Decorator

This decorator is a security feature that exposes a Python function to be called from the client-side (JavaScript) via an AJAX request. Any method intended to be called from the UI must be 'whitelisted'.

Learn more

frappe.db.sql

A Frappe API method used to execute raw SQL queries against the database. It's highly flexible and often necessary for calendar views which require specific field aliases (like 'title', 'start_date') and complex joins.

Learn more

Calendar View Client Script

This Python script is only the backend part. To enable the view, you must also create a `[doctype_name]_calendar.js` file that tells the framework which DocType to use and which `get_events` method to call.

Learn more

Step-by-Step Tutorial

Follow along to understand how this code works

1

Create the Python Server Script File

In your custom app, navigate to the directory of the DocType you want a calendar for. Create or open the main Python file for that DocType (e.g., `my_app/my_app/doctype/meeting/meeting.py`).

python
# In my_app/my_app/doctype/meeting/meeting.py

import frappe
import json
Next Step
2

Define and Whitelist the get_events Function

Add the get_events function and decorate it with `@frappe.whitelist()`. The function must accept `start`, `end`, and optional `filters` arguments.

python
@frappe.whitelist()
def get_events(start, end, filters=None):
    pass # We will add the logic next
Next Step
3

Implement the SQL Query

Use `frappe.db.sql` to fetch your data. Important: You must alias your date fields to start_date and end_date, and a descriptive field to title for the calendar to display them correctly.

python
return frappe.db.sql("""SELECT
              name,
        subject as title,
        scheduled_from as start_date,
        scheduled_to as end_date
    FROM `tabMeeting`
    WHERE docstatus = 1 AND (scheduled_from <= %(end)s AND scheduled_to >= %(start)s)
    """, {"start": start, "end": end}, as_dict=True)
Next Step
4

Create the Calendar View JavaScript File

In the same DocType directory, create a new file named `[doctype_name]_calendar.js` (e.g., `meeting_calendar.js`). This file configures the frontend calendar.

javascript
// In my_app/my_app/doctype/meeting/meeting_calendar.js

// This file should intentionally be left blank if you only need a basic calendar. 
// Frappe will automatically use the get_events method if it exists.
Next Step
5

(Optional) Configure Advanced Calendar Settings

For more control, you can define the calendar view settings explicitly in your JS file, including the path to your `get_events` method.

javascript
frappe.views.calendar["Meeting"] = {
    get_events: "my_app.my_app.doctype.meeting.meeting.get_events",
    field_map: {
        "start": "start_date",
        "end": "end_date",
        "id": "name",
        "title": "title",
    },
};

Common Issues & Solutions

Troubleshoot problems you might encounter