Frappe Framework
Fonctions utilitaires
Les fonctions utilitaires que Frappe fournit dans frappe.utils pour manipuler dates et durées, formater, valider, générer des PDF, utiliser le cache Redis, envoyer des e-mails et plus.
Présentation
Frappe Framework fournit de nombreuses fonctions utilitaires pour les opérations courantes : gestion des dates et heures propres au site, formatage des dates et des devises, génération de PDF, et bien d’autres.
Ces fonctions s’importent depuis le module frappe.utils (et ses sous-modules comme frappe.utils.logger et frappe.utils.data) dans n’importe quel fichier Python de votre app Frappe. La liste ci-dessous n’est pas exhaustive : parcourez le code source du framework pour découvrir tout ce qui existe.
Dates et heures
now
now()
Renvoie la date et l’heure courantes au format yyyy-mm-dd hh:mm:ss.
from frappe.utils import now
now() # '2021-05-25 06:38:52.242515'getdate
getdate(string_date=None)
Convertit string_date (au format yyyy-mm-dd) en objet datetime.date. Sans argument, renvoie la date du jour. Lève une exception si string_date n’est pas une date valide.
from frappe.utils import getdate
getdate() # datetime.date(2021, 5, 25)
getdate('2000-03-18') # datetime.date(2000, 3, 18)today
today()
Renvoie la date du jour au format 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)
| Paramètre | Rôle |
|---|---|
date | Chaîne ou objet datetime ; si None, la fonction utilise le datetime courant. |
as_string | Renvoie le résultat sous forme de chaîne. |
as_datetime | Si as_string et as_datetime valent tous deux True, renvoie une chaîne datetime ; sinon seulement la chaîne date. |
Cette fonction est pratique pour calculer des écarts de date ou d’heure, par exemple ajouter ou retrancher un certain nombre de jours à une date donnée.
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)
Renvoie la différence entre les deux dates, en jours.
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)) #10days_diff
days_diff(date_2, date_1)
Renvoie la différence entre les deux dates, en jours.
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)) #10month_diff
month_diff(date_2, date_1)
Renvoie la différence entre les deux dates, en mois.
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)) #2pretty_date
pretty_date(iso_datetime)
Prend une date-heure ISO et renvoie une chaîne qui indique depuis combien de temps elle est passée. Très courant dans les applications de communication, comme les messageries instantanées.
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 agoformat_duration
format_duration(seconds, hide_days=False)
Convertit une durée en secondes (flottant) en un format de durée lisible.
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'Formatage de texte et de montants
comma_and
comma_and(some_list, add_quotes=True)
À partir d’une liste ou d’un tuple some_list, renvoie une chaîne au format 1er élément, 2e élément, … and dernier élément. La fonction utilise frappe._, vous n’avez donc pas à vous soucier de la traduction du mot and. Si add_quotes vaut False, les éléments sont renvoyés sans guillemets ; avec guillemets sinon. Si some_list n’est ni une liste ni un tuple, il est renvoyé tel quel.
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'> [!NOTE]
> Il existe aussi une fonction comma_or, identique à comma_and à l’exception du séparateur, qui est or.
money_in_words
money_in_words(number, main_currency=None, fraction_currency=None)
| Paramètre | Rôle |
|---|---|
number | Montant monétaire (flottant). |
main_currency | Devise principale. Si elle n’est pas fournie, la fonction la cherche dans les paramètres par défaut, puis utilise INR à défaut. |
fraction_currency | Nom de la sous-unité monétaire. |
Renvoie le montant en toutes lettres, avec la devise et la sous-unité.
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.'get_abbr
get_abbr(string, max_len=2)
Renvoie une abréviation (initiales seulement) de string, limitée à max_len lettres. Très utilisée dans Frappe Framework et ERPNext pour générer des vignettes ou des images de substitution.
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'mask_string
mask_string(input_string, mask_char="*", show_first=4, show_last=3)
Masque une chaîne pour protéger la confidentialité : les caractères du milieu sont cachés, tandis qu’un nombre configurable de caractères au début et à la fin reste visible pour l’identification.
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"random_string
random_string(length)
Génère une chaîne aléatoire de length caractères. Utile dans certains cas pour la cryptographie ou la génération de secrets.
from frappe.utils import random_string
random_string(40) # 'mcrLCrlvkUdkaOe8m5xMI8IwDB8lszwJsWtZFveQ'
random_string(6) # 'htrB4L'
random_string(6) #'HNRirG'unique
unique(seq)
seq : un itérable ou une séquence.
Renvoie la liste des éléments de la séquence sans les doublons, en conservant l’ordre, contrairement à 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']Validation
validate_json_string
validate_json_string(string)
Lève frappe.ValidationError si string n’est pas une chaîne JSON (JavaScript Object Notation) valide, comme le montre l’exemple ci-dessous. Vous pouvez entourer l’appel d’un bloc try-except pour gérer l’erreur.
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')validate_url
validate_url(txt, throw=False, valid_schemes=None)
| Paramètre | Rôle |
|---|---|
txt | Chaîne dont on vérifie la validité. |
throw | Lever une exception si txt n’est pas une URL valide ; False par défaut. |
valid_schemes | Chaîne ou itérable (liste, tuple ou ensemble). Si fourni, le schéma de l’URL est vérifié par rapport à cette valeur. |
Vérifie qu’une chaîne représente une adresse URL valide.
from frappe.utils import validate_url
validate_url('google') # False
validate_url('https://google.com') # True
validate_url('https://google.com', throw=True) # throws ValidationErrorvalidate_email_address
validate_email_address(email_str, throw=False)
Renvoie une chaîne contenant l’adresse e-mail, ou la liste séparée par des virgules des adresses e-mail valides, présentes dans email_str. Si throw vaut True, frappe.InvalidEmailAddressError est levée quand la chaîne ne contient aucune adresse valide ; sinon une chaîne vide est renvoyée.
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)
Renvoie True si phone_number (chaîne) est un numéro de téléphone valide. Si le numéro est invalide et que throw vaut True, frappe.InvalidPhoneNumberError est levée.
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) # InvalidPhoneNumberErrorget_pdf
get_pdf(html, options=None, output=None)
| Paramètre | Rôle |
|---|---|
html | Chaîne HTML à rendre. |
options | dict de configuration, facultatif. |
output | Objet PdfFileWriter, facultatif. |
Cette fonction s’appuie sur les modules pdfkit et pyPDF2 pour générer un PDF à partir de HTML. Si output est fourni, les pages générées y sont ajoutées et l’objet est renvoyé ; sinon la fonction renvoie le flux d’octets (byte) du PDF.
Exemple : générer et renvoyer un PDF en réponse à une requête 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'Cache et e-mail
frappe.cache()
cache()
Renvoie la connexion Redis, instance de la classe RedisWrapper qui hérite de redis.Redis. Vous pouvez l’utiliser pour stocker et lire des paires clé-valeur dans le cache 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)
| Paramètre | Rôle |
|---|---|
recipients | Liste des destinataires. |
sender | Expéditeur. Par défaut, l’utilisateur courant ou le compte sortant par défaut. |
subject | Objet de l’e-mail. |
message | (ou content) Contenu de l’e-mail. |
as_markdown | Convertit le contenu Markdown en HTML. |
template | Nom d’un template HTML (Jinja) du dossier templates/emails. |
args | Arguments pour le rendu du template. |
Dans la plupart des cas, ces arguments suffisent, mais la fonction accepte beaucoup d’autres arguments nommés. Pour les connaître tous, consultez son implémentation dans frappe/__init__.py.
Cette fonction envoie un e-mail via le compte e-mail (Email Account) par défaut de l’utilisateur ou le compte e-mail par défaut global.
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 🎂')
)Exemple de fichier de template Jinja :
<!-- templates/emails/birthday_reminder.html -->
<div>
<div class="gray-container text-center">
<div>
{% for person in birthday_persons %}
{% if person.image %}
<img src="{{ person.image }}" title="{{ person.name }}">
{% endif %}
{% endfor %}
</div>
<div style="margin-top: 15px;">
<span>{{ reminder_text }}</span>
<p class="text-muted">{{ message }}</p>
</div>
</div>Joindre des fichiers
Vous pouvez joindre des fichiers à l’e-mail en passant une liste de pièces jointes à sendmail :
frappe.sendmail(
["[email protected]", "[email protected]"],
message="## hello, *bro*"
attachments=[{"file_url": "/files/hello.png"}],
as_markdown=True
)Les pièces jointes sont une liste de dictionnaires avec une clé file_url. Cette valeur se trouve dans le champ file_url d’un document File.
Synchronisation et liens
filelock
Un verrou de fichier (file lock) permet de synchroniser des processus et d’éviter les situations de concurrence (race conditions).
Exemple : l’écriture dans un fichier peut provoquer une situation de concurrence si plusieurs processus écrivent en même temps. On crée donc un verrou nommé, visible par les processus, qui attendent qu’il soit libéré avant d’écrire.
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)
Utile quand plusieurs documents viennent d’être créés et que vous voulez tous les montrer ensemble à l’utilisateur dans la vue liste.
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"]]'get_filtered_list_link
get_filtered_list_link(doctype, docnames=None, label=None)
Utile quand plusieurs documents viennent d’être créés : au lieu d’afficher une longue liste de liens dans une boîte de message, vous affichez un seul lien propre.
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=[...]">Work Order</a>'