Documentation Frappe Framework

Frappe Framework

API base de données

Toutes les fonctions de frappe.db pour lire, écrire et administrer la base de données d’un site Frappe, ainsi que le modèle de transaction appliqué par le framework.

8 min de lectureMis à jour le Source : docs.frappe.io

Présentation

L’API base de données (Database API) regroupe les fonctions de frappe.db qui lisent, écrivent et administrent les tables d’un site.

Attention

À partir de la version 16, get_list et get_all comportent des changements incompatibles.

Lecture des enregistrements

frappe.db.get_list

frappe.db.get_list(doctype, filters, or_filters, fields, order_by, group_by, start, page_length, run)

  • Également disponible sous l’alias frappe.get_list.

Renvoie une liste d’enregistrements de la table du doctype. C’est une surcouche ORM d’une requête SELECT. Les permissions de l’utilisateur de la session sont appliquées aux enregistrements. Si l’argument nommé fields n’est pas fourni, seuls les noms de documents sont renvoyés. Par défaut, la méthode renvoie une liste de dict, mais vous pouvez extraire un seul champ avec l’argument nommé pluck :

python
frappe.db.get_list('Employee')

# output
[{'name': 'HR-EMP-00008'},
 {'name': 'HR-EMP-00006'},
 {'name': 'HR-EMP-00010'},
 {'name': 'HR-EMP-00005'}
]

# with pluck
frappe.db.get_list('Employee', pluck='name')

# output
['HR-EMP-00008',
 'HR-EMP-00006',
 'HR-EMP-00010',
 'HR-EMP-00005'
]

Combinaison de filtres et d’autres arguments :

python
frappe.db.get_list('Task',
    filters={
        'status': 'Open'
    },
    fields=['subject', 'date'],
    order_by='date desc',
    start=10,
    page_length=20,
    as_list=True
)

# output
(('Update Branding and Design', '2019-09-04'),
('Missing Documentation', '2019-09-02'),
('Fundraiser for Foundation', '2019-09-03'))

# Tasks with date after 2019-09-08
frappe.db.get_list('Task', filters={
    'date': ['>', '2019-09-08']
})

# Tasks with date between 2020-04-01 and 2021-03-31 (both inclusive)
frappe.db.get_list('Task', filters=[[
    'date', 'between', ['2020-04-01', '2021-03-31']
]])

# Tasks with subject that contains "test"
frappe.db.get_list('Task', filters={
    'subject': ['like', '%test%']
})

# Count number of tasks grouped by status
frappe.db.get_list('Task',
    fields=['count(name) as count', 'status'],
    group_by='status'
)

# For version 16 +
frappe.db.get_list('Task',
    fields=[{'COUNT': 'name', 'as': 'count'}, 'status'],
    group_by='status'
)

# output
[{'count': 1, 'status': 'Working'},
 {'count': 2, 'status': 'Overdue'},
 {'count': 2, 'status': 'Open'},
 {'count': 1, 'status': 'Filed'},
 {'count': 20, 'status': 'Completed'},
 {'count': 1, 'status': 'Cancelled'}]

Avant la version 16, run=False renvoyait la requête SQL au lieu de l’exécuter. À partir de la version 16, vous obtenez à la place un objet du constructeur de requêtes (Query Builder). Appelez .get_sql() pour obtenir la requête SQL comme auparavant, ou modifiez la requête à votre guise avant de l’exécuter avec query.run().

frappe.db.get_all

frappe.db.get_all(doctype, filters, or_filters, fields, order_by, group_by, start, page_length, run)

  • Également disponible sous l’alias frappe.get_all.

Identique à frappe.db.get_list, mais récupère tous les enregistrements sans appliquer les permissions.

frappe.db.get_value

frappe.db.get_value(doctype, name, fieldname) ou frappe.db.get_value(doctype, filters, fieldname)

  • Également disponible sous les alias frappe.get_value et frappe.db.get_values.

Renvoie la valeur d’un champ d’un document, ou une liste de valeurs.

python
# single value
subject = frappe.db.get_value('Task', 'TASK00002', 'subject')

# multiple values
subject, description = frappe.db.get_value('Task', 'TASK00002', ['subject', 'description'])

# as dict
task_dict = frappe.db.get_value('Task', 'TASK00002', ['subject', 'description'], as_dict=1)
task_dict.subject
task_dict.description

# with filters, will return the first record that matches filters
subject, description = frappe.db.get_value('Task', {'status': 'Open'}, ['subject', 'description'])

frappe.db.get_single_value

frappe.db.get_single_value(doctype, fieldname)

Renvoie la valeur d’un champ d’un DocType Single.

python
timezone = frappe.db.get_single_value('System Settings', 'timezone')

frappe.db.exists

frappe.db.exists(doctype, name)

Renvoie vrai si l’enregistrement existe.

Avec le doctype et le nom du document :

python
frappe.db.exists("User", "[email protected]", cache=True)

Avec un dictionnaire de filtres contenant la clé "doctype" :

python
frappe.db.exists({"doctype": "User", "full_name": "Jane Doe"})

Avec le doctype et un dictionnaire de filtres :

python
frappe.db.exists("User", {"full_name": "Jane Doe"})

frappe.db.count

frappe.db.count(doctype, filters)

Renvoie le nombre d’enregistrements pour un doctype et des filters donnés.

python
# total number of Task records
frappe.db.count('Task')

# total number of Open tasks
frappe.db.count('Task', {'status': 'Open'})

Écriture des enregistrements

frappe.db.set_value

frappe.db.set_value(doctype, name, fieldname, value)

  • Également disponible sous l’alias frappe.db.update.

Définit la valeur d’un champ dans la base de données, sans appeler les déclencheurs de l’ORM, mais en mettant à jour l’horodatage modified (sauf indication contraire).

python
# update a field value
frappe.db.set_value('Task', 'TASK00002', 'subject', 'New Subject')

# update multiple values
frappe.db.set_value('Task', 'TASK00002', {
    'subject': 'New Subject',
    'description': 'New Description'
})

# update without updating the `modified` timestamp
frappe.db.set_value('Task', 'TASK00002', 'subject', 'New Subject', update_modified=False)

Attention

Cette méthode n’appelle pas les déclencheurs de l’ORM comme validate et on_update. Utilisez-la pour mettre à jour des champs cachés, ou si vous savez exactement ce que vous faites.

frappe.db.delete

frappe.db.delete(doctype, filters)

Supprime les enregistrements du doctype qui correspondent aux filters. C’est une commande DML : elle peut être annulée par un rollback. Sans filtres, tous les enregistrements du doctype sont supprimés.

python
frappe.db.delete("Route History", {
    "modified": ("<=", last_record_to_keep[0].modified),
    "user": user
})

frappe.db.delete("Error Log")
frappe.db.delete("__Test Table")

Vous pouvez passer le nom d’un DocType ou le nom d’une table interne. Par convention, les tables internes de Frappe sont préfixées par __, et l’API suit cette convention. Les deux dernières commandes ci-dessus exécutent un DELETE inconditionnel sur les tables tabError Log et __Test Table.

frappe.db.truncate

frappe.db.truncate(doctype)

Vide une table de la base de données. C’est une commande DDL TRUNCATE TABLE : un commit est déclenché avant son exécution, et l’opération ne peut pas être annulée. Elle convient par exemple au nettoyage périodique des tables de journaux.

python
frappe.db.truncate("Error Log")
frappe.db.truncate("__Test Table")

Ces commandes exécutent un TRUNCATE sur les tables tabError Log et __Test Table.

frappe.db.bulk_update

frappe.db.bulk_update(doctype, doc_updates, *, chunk_size=100, modified=None, modified_by=None, update_modified=True, debug=False)

Met à jour plusieurs documents en masse, par lots, avec une seule requête SQL UPDATE utilisant des expressions CASE.

ParamètreValeur par défautRôle
doctype—DocType concerné.
doc_updates—Dictionnaire {nom du document: {champ: valeur}} (format ci-dessous).
chunk_size100Nombre de documents par lot.
modifiedNoneHorodatage modified à écrire.
modified_byNoneUtilisateur à inscrire dans modified_by.
update_modifiedTrueMet à jour les colonnes de modification.
debugFalseActive le mode débogage.

Exemple :

python
frappe.db.bulk_update(
    "Task",
    {
        "TASK-0001": {"status": "Closed", "description": "Completed by QA"},
        "TASK-0002": {"status": "Open", "description": "Pending assignment"},
    },
    chunk_size=200,
    modified_by="[email protected]",
    update_modified=True,
    debug=True,
)

Format de doc_updates :

python
{
    "DOC-0001": {"field1": "value1", "field2": "value2"},
    "DOC-0002": {"field1": "valueA", "field2": "valueB"},
}

À retenir

Il s’agit d’une mise à jour directe en base : les événements et validations du Document ne sont pas déclenchés. Les champs non précisés restent inchangés.

Transactions

frappe.db.commit

frappe.db.commit()

Valide la transaction en cours. Exécute le COMMIT SQL.

À retenir

Dans la plupart des cas, vous n’avez pas besoin de valider manuellement. Reportez-vous au modèle de transaction de Frappe décrit plus bas.

frappe.db.savepoint

frappe.db.savepoint(save_point)

Crée un point de sauvegarde (savepoint) nommé, vers lequel vous pourrez revenir plus tard.

frappe.db.rollback

frappe.db.rollback()

Annule la transaction en cours. Exécute le ROLLBACK SQL.

À retenir

Frappe exécute automatiquement frappe.db.rollback() si une exception est levée pendant une requête web de type POST ou PUT. Utilisez cette méthode si vous devez annuler plus tôt dans la transaction.

frappe.db.rollback(save_point="save_point_name")

Revient à un point de sauvegarde précis au lieu d’annuler toute la transaction. Ce rollback n’annule pas les modifications faites sur le système de fichiers ni les autres observateurs de rollback.

Requêtes SQL

frappe.db.sql

frappe.db.sql(query, values, as_dict)

Exécute une requête SQL arbitraire. Utile pour des rapports côté serveur complexes avec des jointures, pour adapter la base de données à de nouvelles fonctionnalités, etc.

Exemple :

python
values = {'company': 'Frappe Technologies Inc'}
data = frappe.db.sql("""
 SELECT
 acc.account_number
 gl.debit
 gl.credit
 FROM `tabGL Entry` gl
 LEFT JOIN `tabAccount` acc
 ON gl.account = acc.name
 WHERE gl.company = %(company)s
""", values=values, as_dict=0)

Attention

Évitez cette méthode : elle contourne les validations et les contrôles d’intégrité. Il vaut toujours mieux utiliser frappe.get_doc, frappe.db.get_list, etc. quand c’est possible.

frappe.db.multisql

frappe.db.multisql({'mariadb': mariadb_query, 'postgres': postgres_query})

Exécute l’instruction SQL adaptée au moteur de base de données utilisé, parmi ceux pris en charge.

Administration du schéma

frappe.db.rename_table

frappe.db.rename_table(old_name, new_name)

Exécute une requête de renommage de table. Indiquez directement le DocType ou le nom de la table interne.

Exemple :

python
frappe.db.rename_table("__internal_cache", "__temporary_cache")
frappe.db.rename_table("todo", "ToDo")

Le second exemple ne doit être utilisé que si vous en comprenez les conséquences.

Attention

N’utilisez pas cette méthode pour renommer les tables de DocType ; utilisez frappe.rename_doc à la place.

frappe.db.describe

frappe.db.describe(doctype)

Renvoie un tuple décrivant la table du DocType donné.

frappe.db.change_column_type

frappe.db.change_column_type(doctype, column, new_type)

Change le type d’une colonne pour le DocType indiqué.

frappe.db.add_index

frappe.db.add_index(doctype, fields, index_name)

Crée des index sur les champs indiqués d’un doctype.

À retenir

Pour indexer un champ TEXT ou BLOB, vous devez préciser une longueur fixe.

Exemple :

python
frappe.db.add_index("Notes", ["id(10)", "content(500)"], index_name)

frappe.db.add_unique

frappe.db.add_unique(doctype, fields, constraint_name=None)

Crée une contrainte d’unicité sur les champs indiqués d’un doctype.

Exemple :

python
frappe.db.add_unique("DoctypeName",["field1","field2"])

Hooks de transaction

À retenir

Cette API a été introduite en v15.

Frappe fournit des hooks pour exécuter des fonctions de rappel avant ou après les commandes de transaction (commit, rollback). Ils servent notamment à :

  • annuler des changements faits en dehors de la base de données quand la transaction est annulée ;
  • propager des changements en dehors de la base de données uniquement quand la transaction est validée.

Les hooks disponibles :

HookMoment d’exécution
frappe.db.before_commit.add(func: Callable)Avant le commit.
frappe.db.after_commit.add(func: Callable)Après le commit.
frappe.db.before_rollback.add(func: Callable)Avant le rollback.
frappe.db.after_rollback.add(func: Callable)Après le rollback.

Exemple d’utilisation :

python
def create_file(self):
    self.write_file()
    # This ensures rollback if DB transaction is rolledback
    frappe.db.after_rollback.add(self.rollback_file)

def rollback_file(self):
    self.delete_file()

Modèle de transaction

Les abstractions de base de données de Frappe appliquent par défaut un modèle de transaction raisonnable. Dans la plupart des cas, vous n’avez donc pas à gérer les transactions SQL vous-même. Voici ses grandes lignes.

Requêtes web

  • Lors d’un POST ou d’un PUT, les écritures en base sont validées à la fin de la requête si celle-ci réussit.
  • Les appels AJAX faits avec frappe.call sont des POST par défaut, sauf modification.
  • Les requêtes GET ne provoquent pas de commit implicite.
  • Toute exception non interceptée pendant le traitement de la requête annule la transaction.

Tâches de fond et tâches planifiées

  • Une fonction exécutée comme tâche de fond (Background Job) ou tâche planifiée valide la transaction à la fin de son exécution si elle réussit.
  • Toute exception non interceptée annule la transaction.

Patches

  • La fin réussie de la fonction execute d’un patch valide automatiquement la transaction.
  • Toute exception non interceptée annule la transaction.

Tests unitaires

  • La transaction est validée après l’exécution d’un module de test, c’est-à-dire d’un fichier de test Python comme test_core.py.
  • La transaction est aussi validée à la fin de l’ensemble des tests.
  • Toute exception non interceptée arrête le lanceur de tests, donc sans commit.

Attention

Si vous interceptez des exceptions quelque part, la couche base de données ne sait pas qu’un problème est survenu : c’est alors à vous d’assurer le rollback correct de la transaction.

Pages liées