工具函数

Frappe 框架附带各种实用函数,用于处理常见的操作,例如站点特定的日期时间管理、日期和货币格式化、PDF 生成等等。

这些实用方法可以从 frappe.utils 模块(及其嵌套模块,如 frappe.utils.loggerfrappe.utils.data)中导入到您的 Frappe 应用的任何 Python 文件中。此列表并非详尽无遗,您可以查看框架代码库以了解可用的内容。

now(当前时间)

now()

返回当前日期时间,格式为 yyyy-mm-dd hh:mm:ss

from frappe.utils import now

now() # '2021-05-25 06:38:52.242515'

getdate(获取日期)

getdate(string_date=None)

string_date(yyyy-mm-dd)转换为 datetime.date 对象。如果未提供输入,则返回当前日期。如果 string_date 是无效的日期字符串,则会抛出异常。

from frappe.utils import getdate

getdate() # datetime.date(2021, 5, 25)
getdate('2000-03-18') # datetime.date(2000, 3, 18)

today(今天)

today()

返回当前日期,格式为 yyyy-mm-dd

from frappe.utils import today

today() # '2021-05-25'

add_to_date(日期加减)

add_to_date(date, years=0, months=0, weeks=0, days=0, hours=0, minutes=0, seconds=0, as_string=False, as_datetime=False)

`date`: A string representation or `datetime` object, uses the current `datetime` if `None` is passed
`as_string`: Return as string
`as_datetime`: If `as_string` is True and `as_datetime` is also True, returns a `datetime` string otherwise just the `date` string.

此函数在进行日期/日期时间增减时非常方便,例如,从特定日期/日期时间加上或减去一定数量的天数。

from datetime import datetime # from python std library
from frappe.utils import add_to_date

today = datetime.now().strftime('%Y-%m-%d')
print(today) # '2021-05-21'

after_10_days = add_to_date(datetime.now(), days=10, as_string=True)
print(after_10_days) # '2021-05-31'

add_to_date(datetime.now(), months=2) # datetime.datetime(2021, 7, 21, 15, 31, 18, 119999)
add_to_date(datetime.now(), days=10, as_string=True, as_datetime=True) # '2021-05-31 15:30:23.757661'
add_to_date(None, years=6) # datetime.datetime(2027, 5, 21, 15, 32, 31, 652089)

date_diff(日期差)

date_diff(date_2, date_1)

返回给定两个日期之间的天数差。

from frappe.utils import add_to_date, today, date_diff
date_1 = today()
date_2 = add_to_date(date_1, days=10)

print(date_diff(date_2, date_1)) #10

days_diff(天数差)

days_diff(date_2, date_1)

返回给定两个日期之间的天数差。

from frappe.utils import add_to_date, today, days_diff
date_1 = today()
date_2 = add_to_date(date_1, days=10)

print(days_diff(date_2, date_1)) #10

month_diff(月份差)

month_diff(date_2, date_1)

返回给定两个日期之间的月份差。

from frappe.utils import add_to_date, month_diff
date_1 = "2024-07-01"
date_2 = add_to_date(date_1, days=60)

print(month_diff(date_2, date_1)) #2

pretty_date(友好日期)

pretty_date(iso_datetime)

接受一个 ISO 时间,并返回一个字符串,表示该日期距今有多长时间。这在即时通讯等通信应用中非常常见。

from frappe.utils import pretty_date, now, add_to_date

pretty_date(now()) # 'just now'

# Some example outputs:

# 1 hour ago
# 20 minutes ago
# 1 week ago
# 5 years ago

format_duration(格式化时长)

format_duration(seconds, hide_days=False)

将给定的以秒(浮点数)为单位的时长值转换为时长格式。

from frappe.utils import format_duration

format_duration(50) # '50s'
format_duration(10000) # '2h 46m 40s'
format_duration(1000000) # '11d 13h 46m 40s'

# Convert days to hours
format_duration(1000000, hide_days=True) # '277h 46m 40s'

comma_and(逗号和)

comma_and(some_list, add_quotes=True)

给定一个列表或元组 some_list,返回格式为 1st item, 2nd item, .... and last item 的字符串。此函数使用 frappe._,因此您不必担心单词 and 的翻译问题。如果 add_quotesFalse,则返回不带引号的项目,否则返回带引号的项目。如果作为参数传递的 some_list 的类型不是列表或元组,则原样返回它(some_list)。

from frappe.utils import comma_and

comma_and([1, 2, 3]) # "'1', '2' and '3'"
comma_and(['Apple', 'Ball', 'Cat'], add_quotes=False) # 'Apple, Ball and Cat'
comma_and('abcd') # 'abcd'

还有一个 comma_or 函数,它与 comma_and 类似,只是分隔符不同,在 comma_or 的情况下,分隔符是 or

money_in_words(金额转大写)

money_in_words(number, main_currency=None, fraction_currency=None)

`number`: A floating point money amount
`main_currency`: Uses this as the main currency. If not given, tries to fetch from default settings or uses `INR` if not found there.

此函数返回带有货币和辅币单位的字符串(金额大写形式)。

from frappe.utils import money_in_words

money_in_words(900) # 'INR Nine Hundred and Fifty Paisa only.'
money_in_words(900.50) # 'INR Nine Hundred and Fifty Paisa only.'
money_in_words(900.50, 'USD') # 'USD Nine Hundred and Fifty Centavo only.'
money_in_words(900.50, 'USD', 'Cents') # 'USD Nine Hundred and Fifty Cents only.'

validate_json_string(验证 JSON 字符串)

validate_json_string(string)

如果给定的 string 是有效的 JSON(JavaScript 对象表示法)字符串,则抛出 frappe.ValidationError。您可以使用 try-except 块来处理对此函数的调用,如下面的代码片段所示。

import frappe
from frappe.utils import validate_json_string

# No Exception thrown
validate_json_string('[]')
validate_json_string('[{}]')
validate_json_string('[{"player": "one", "score": 199}]')

try:
    # Throws frappe.ValidationError
    validate_json_string('invalid json')
except frappe.ValidationError:
    print('Not a valid JSON string')

random_string(随机字符串)

random_string(length)

此函数生成一个包含 length 个字符的随机字符串。这在某些情况下对于生成加密或秘密信息非常有用。

from frappe.utils import random_string

random_string(40) # 'mcrLCrlvkUdkaOe8m5xMI8IwDB8lszwJsWtZFveQ'
random_string(6) # 'htrB4L'
random_string(6) #'HNRirG'

mask_string(掩码字符串)

mask_string(input_string, mask_char="*", show_first=4, show_last=3)

出于隐私保护目的,通过隐藏中间字符来掩码字符串,同时显示可配置的首尾字符以用于识别。

from frappe.utils import mask_string

mask_string("1234567890")  # "1234***890"
mask_string("12345")       # "******"
mask_string("1234567890", mask_char="#", show_first=2, show_last=2)  # "12####90"

unique(去重)

unique(seq)

seq:一个可迭代对象 / 序列

此函数在移除重复项后返回给定序列的元素列表。此外,与 list(set(seq)) 不同,它会保留顺序。

from frappe.utils import unique

unique([1, 2, 3, 1, 1, 1]) # [1, 2, 3]
unique('abcda') # ['a', 'b', 'c', 'd']
unique(('Apple', 'Apple', 'Banana', 'Apple')) # ['Apple', 'Banana']

get_pdf(生成 PDF)

get_pdf(html, options=None, output=None)

`html`: HTML string to render
`options`: An optional `dict` for configuration
`output`: A optional `PdfFileWriter` object.

此函数使用 pdfkitpyPDF2 模块从 HTML 生成 PDF 文件。如果提供了 output,则将生成的页面追加到此对象并返回它,否则返回 PDF 的 byte 流。

例如,生成 PDF 并将其作为对 Web 请求的响应返回:

import frappe
from frappe.utils.pdf import get_pdf

@frappe.whitelist(allow_guest=True)
def generate_invoice():
    cart = [{
        'Samsung Galaxy S20': 10,
        'iPhone 13': 80
    }]

    html = '
<h1>Invoice from Star Electronics e-Store!</h1>'

    # Add items to PDF HTML
    html += '
<ol>'
    for item, qty in cart.items():
        html += f'
<li>{item} - {qty}</li>'
    html += '</ol>'

    # Attaching PDF to response
    frappe.local.response.filename = 'invoice.pdf'
    frappe.local.response.filecontent = get_pdf(html)
    frappe.local.response.type = 'pdf'

get_abbr(获取缩写)

get_abbr(string, max_len=2)

返回给定 string 的缩写(仅首字母)版本,最多包含 max_len 个字母。它广泛用于 Frappe 框架和 ERPNext 中,以生成缩略图或占位图像。

from frappe.utils import get_abbr

get_abbr('Gavin') # 'G'
get_abbr('Coca Cola Company') # 'CC'
get_abbr('Mohammad Hussain Nagaria', max_len=3) # 'MHN'

validate_url(验证 URL)

validate_url(txt, throw=False, valid_schemes=None)

`txt`: A string to check validity
`throw`: Weather to throw an exception if `txt` does not represent a valid URL, `False` by default
`valid_schemes`: A string or an iterable (list, tuple or set). If provided, checks the given URL's scheme against this.

此实用函数可用于检查字符串是否表示有效的 URL 地址。

from frappe.utils import validate_url

validate_url('google') # False
validate_url('https://google.com') # True
validate_url('https://google.com', throw=True) # throws ValidationError

validate_email_address(验证电子邮件地址)

validate_email_address(email_str, throw=False)

返回一个字符串,其中包含给定 email_str 中存在的电子邮件地址或以逗号分隔的有效电子邮件地址列表。如果 throwTrue,则在给定字符串中不存在有效电子邮件地址时抛出 frappe.InvalidEmailAddressError,否则返回空字符串。

from frappe.utils import validate_email_address

# Single valid email address
validate_email_address('[email protected]') # '[email protected]'
validate_email_address('other text, [email protected], some other text') # '[email protected]'

# Multiple valid email address
validate_email_address(
    'some text, [email protected], some other text, [email protected], yet another no-emailic phrase.'
) # '[email protected], [email protected]'

# Invalid email address
validate_email_address('some other text') # ''

validate_phone_number(验证电话号码)

validate_phone_number(phone_number, throw=False)

如果 phone_number(字符串)是有效的电话号码,则返回 True。如果 phone_number 无效且 throwTrue,则会抛出 frappe.InvalidPhoneNumberError

from frappe.utils import validate_phone_number

# Valid phone numbers
validate_phone_number('753858375') # True
validate_phone_number('+91-75385837') # True

# Invalid phone numbers
validate_phone_number('invalid') # False
validate_phone_number('87345%%', throw=True) # InvalidPhoneNumberError

frappe.cache()

cache()

返回 Redis 连接,该连接是 RedisWrapper 类的实例,继承自 redis.Redis 类。您可以使用此连接来利用 Redis 缓存存储/检索键值对。

import frappe

cache = frappe.cache()

cache.set('name', 'frappe') # True
cache.get('name') # b'frappe'

frappe.sendmail()

sendmail(recipients=[], sender="", subject="No Subject", message="No Message", as_markdown=False, template=None, args=None, **kwargs)

`recipients`: List of recipients
`sender`: Email sender. Default is current user or default outgoing account
`subject`: Email Subject
`message`: (or `content`) Email Content
`as_markdown`: Convert content markdown to HTML
`template`: Name of html template (jinja) from templates/emails folder
`args`: Arguments for rendering the template

在大多数情况下,上述参数已经足够,但此函数还可以接受许多其他关键字参数。要查看所有关键字参数,请查看此函数的实现(frappe/__init__.py)。

此函数可以使用用户的默认电子邮件账户或全局默认电子邮件账户发送邮件。

import frappe

recipients = [
    '[email protected]',
    '[email protected]'
]

frappe.sendmail(
    recipients=recipients,
    subject=frappe._('Birthday Reminder'),
    template='birthday_reminder',
    args=dict(
        reminder_text=reminder_text,
        birthday_persons=birthday_persons,
        message=message,
    ),
    header=_('Birthday Reminder 🎂')
)

示例 Jinja 模板文件:

<!-- templates/emails/birthday_reminder.html -->
<div>
<div class="gray-container text-center">
<div>
 {% for person in birthday_persons %}
 {% if person.image %}
 <img src="%7B%7B%20person.image%20%7D%7D" title="{{ person.name }}">
 {% endif %}
 {% endfor %}
 </div>
<div style="margin-top: 15px;"><span>{{ reminder_text }}</span>
<p class="text-muted">{{ message }}</p>
</div>
</div>

<h3 id="attaching-files">附加文件</h3>
<p>您可以通过向 <code>sendmail 函数传递附件列表来轻松地将文件附加到邮件中:

frappe.sendmail(
    ["[email protected]", "[email protected]"],
    message="## hello, *bro*"
    attachments=[{"file_url": "/files/hello.png"}],
    as_markdown=True
)

请注意,附件是一个字典列表,其中包含键 file_url。您可以在 File 文档的 file_url 字段中找到此 file_url

文件锁

文件锁可用于同步进程,以避免竞争条件。

示例:如果多个写入者尝试写入同一个文件,可能会导致竞争条件。因此,我们创建一个命名锁,以便进程可以看到该锁并等待其可用于写入。

from frappe.utils.synchronization import filelock

def update_important_config(config, file):
    with filelock("config_name"):
        json.dumps(config, file)

get_filtered_list_url

get_filtered_list_url(doctype, docnames=None)

当创建了多个文档,并且您希望在列表视图中向用户展示所有这些文档时,此功能非常有用。

from frappe.utils import get_filtered_list_url

get_filtered_list_url("Work Order", [
    "MFG-WO-2025-00027",
    "MFG-WO-2025-00028",
    "MFG-WO-2025-00029"
])
# → 'http://
<site>/app/work-order?name=["in",["MFG-WO-2025-00027","MFG-WO-2025-00028","MFG-WO-2025-00029"]]'
</site>

get_filtered_list_link(doctype, docnames=None, label=None)

当创建了多个文档,并且您希望显示一个简洁的链接而不是冗长的消息对话框链接时,此功能非常有用。

from frappe.utils import get_filtered_list_link

get_filtered_list_link("Work Order", [
    "MFG-WO-2025-00027",
    "MFG-WO-2025-00028",
])
# → '<a href="http://<site>/app/work-order?name=%5B...%5D">Work Order</a>'