Frappe Framework
frappe.qb.get_query
Filtres, jointures de liens et tables enfants, pagination et verrous. Un champ interdit disparaît du select, mais un filtre dessus lève une erreur.
Rôle
frappe.qb.get_query remplace, pour les nouvelles requêtes, frappe.db.get_list et get_all, dont le code est devenu difficile à étendre. Il s’appuie sur PyPika et sur les métadonnées des DocTypes. Le constructeur plus bas est dans Query Builder.
frappe.qb.get_query("User") puis run(as_dict=True). Sans fields, seul name est sélectionné.
Champs
fields est une liste, une chaîne séparée par des virgules, ou "*". Un alias s’écrit name as user_name. Un lien se lit en pointé, customer.customer_name : un LEFT JOIN est ajouté. Une table enfant aussi, items.item_code, et plusieurs lignes reviennent si la commande a plusieurs articles. On peut aussi demander les lignes imbriquées : le résultat d’exemple place items dans chaque commande, avec item_code, qty et rate.
Agrégats : COUNT, COUNT(*), SUM, AVG, MAX, MIN. Scalaires : ABS, IFNULL (deux arguments), CONCAT, EXTRACT (unité et date), NOW.
Filtres
Un dictionnaire est une égalité. Les autres opérateurs sont une liste dans la valeur, ou une liste de triplets. Plusieurs filtres dictionnaire ou liste se combinent par AND. Les opérateurs publiés : =, !=, >, <, >=, <=, like, not like, in, not in, is avec set ou not set (IS NOT NULL et IS NULL), descendants of, ancestors of, et leurs négations pour les arbres lft / rgt.
Le pointé filtre un lien (customer.territory) ou un enfant (items.item_code). Pour l’enfant, distinct=True évite de répéter le parent.
AND et OR se groupent par listes imbriquées. L’exemple : activé ET (prénom Admin OU Guest).
Exécution
run() renvoie des tuples. as_dict=True des dictionnaires, une option liste des listes, un seul champ une liste plate. debug imprime le SQL et le temps. Une méthode donne le SQL avec les valeurs déjà substituées, pour le diagnostic : l’exemple filtre first_name égal à Admin. Un itérateur, avec unbuffered_cursor, parcourt beaucoup de lignes sans tout charger.
order_by un ou plusieurs champs. group_by compte, par exemple les factures par client. limit et un décalage paginent. distinct isole des valeurs, par exemple les clients des factures soumises.
Droits
Par défaut, ignore_permissions=True : aucun contrôle. False applique les droits de l’utilisateur courant. Sinon, frappe.PermissionError.
Avec les droits : lecture ou select selon les rôles, User Permissions, partages, contrainte if_owner, conditions de hooks ou de scripts serveur, et le niveau de champ. Un champ de fields inaccessible est retiré en silence. Un champ inaccessible dans filters, group_by ou order_by lève une erreur. Le pointé vérifie le lien et la cible. L’exemple Blog Post retire published (permlevel 1) pour [email protected], mais filters={"published": 1} échoue. user choisit l’utilisateur. parent_doctype donne le parent d’un DocType enfant.
Avancé et verrous
Un objet PyPika peut être un champ, par exemple un sous-requête corrélé qui compte les ToDo ouverts, alias open_todos_count. Un critère PyPika peut être le filtre : modifié après création ET sujet de plus de 10 caractères, OU statut Cancelled.
for_update=True ajoute FOR UPDATE et attend. skip_locked=True saute les lignes déjà verrouillées. wait=False échoue tout de suite. L’exemple System Settings attrape une exception générique.
Sécurité
Les noms de champs sont validés, les valeurs sont paramétrées, et ignore_permissions=False passe par les droits Frappe. Une valeur dynamique qui construit un nom de champ ou une clé de filtre doit être assainie si elle vient d’une source non fiable. Les saisies utilisateur se passent comme valeurs de filtre. La page indique avoir été écrite par Claude Sonnet 4 puis relue.
Point d’attention
Le défaut ignore les droits. Demander un champ interdit ne prévient pas : il disparaît. Le filtrer, lui, échoue.
