Une rapide enquête auprès de collègues sur mon projet actuel a montré qu'avec les mots "ORM et travailler avec la base de données" dans la grande majorité des cas, les mots "Alchemy" et "Django ORM" sonnent. La connaissance de ces deux mots, en général, suffit pour écrire du code propre, soigné et fonctionnel. Mais l'élargissement des horizons d'ingénierie n'a fait de mal à personne jusqu'à présent, donc aujourd'hui, nous ajouterons à notre image du monde quelques pièces intéressantes (peut-être jusqu'à ce jour inconnues) pour travailler avec la base de données.

Yoyo
Aujourd'hui, tout ORM est livré avec un système de migration de base de données interne. Une approche simple et cool, quand ORM surveille la structure des tables, en général, convient à tout le monde. Mais cette solution a un cas où il n'est pas pratique de l'utiliser:
Il y a des gars qui figuatent les requêtes dans une base de données, contournant ORM. Cela utilise quelque chose comme asyncpg et une petite tache auto-écrite pour simplifier la compilation des demandes.
Pourquoi ces gars abandonnent-ils les équipements ORM? Oui, car tout wrapper pour la base de données consomme une certaine quantité de ressources système, et ces gars-là doivent écrire du code hautes performances. Ils ont jeté l'ORM, et d'une manière ou d'une autre, ils doivent migrer la base.
- Les migrants intégrés peuvent avoir leurs propres opinions spécifiques sur la liaison et l'indexation des enregistrements. Parfois, il n'est pas possible de supprimer ces vues ORM sur la structure de la table; vous devez corriger les corrections de requête vous-même.
Dans les deux cas, il est pratique d'utiliser des migrations manuelles - nous ne nous appuyons pas sur les modèles ORM, mais avec nos mains, nous tapons les instructions SQL nécessaires et les introduisons dans un simple migrateur qui applique séquentiellement les modifications structurelles à la base de données.
La sortie est une structure de table propre, compréhensible et entièrement gérable, compilée judicieusement.
Pour une telle approche manuelle, il existe un migrateur de schéma de base de données yoyo .
pip install yoyo-migrations
Ensuite, tout le travail de gestion de la migration est effectué à l'aide de l'exécutable yoyo
yoyo new ./migrations -m "Add column to foo"
La commande crée un fichier dans lequel vous pouvez entrer une ou plusieurs instructions pour migrer le schéma
from yoyo import step steps = [ step("CREATE TABLE foo (id INT, bar VARCHAR(20), PRIMARY KEY (id))", "DROP TABLE foo"), ]
Après cette migration, vous pouvez passer à la base
yoyo apply --database postgresql://scott:tiger@localhost/db ./migrations
Tout est simple, comme les journaux. Et en même temps, vous avez un contrôle absolu sur l'apparence de la base de données.
Il y a deux inconvénients à cette approche.
- L'ensemble des champs des tables et leurs paramètres devront être suivis de poignées. Chaque changement entraîne la nécessité d'écrire ALTER TABLE vous-même, la possibilité de tout migrer en un seul clic est perdue.
- La fusion de ces migrations devra également se faire toujours avec les mains et la tête. Ceci, bien sûr, est un travail superflu. Mais dans la pratique, les conflits et les fusions complexes de migration sont rares.
Peewee

Un ORM petit et pas le plus populaire (bien qu'il ait été écrit ici plus d'une fois), qui a cependant son propre public.
Peewee est conçu pour être le wrapper le plus stupide et le plus simple de la base de données, avec le mécanisme d'exécution de requête le plus compréhensible et le code le plus facile à lire.
Users.select().where( Users.user_id == user_id ).get()
Malgré sa simplicité, son minimalisme et une petite quantité de code, peewee a tout ce qui est nécessaire pour un travail sain.
- Performances adéquates (mais pas la vitesse d'exécution des requêtes la plus rapide)
- Tous les goodies nécessaires - divers ensembles de champs, relations entre entités, pools de connexions, ensembles de plugins et d'extensions.
- Il y a même un asynchronisme plus ou moins sain d'esprit ajouté par le module tiers peewee_async .
Orme de poney
Pony est un emballage légendaire. La couche de base de données écrite à l'aide de cet ORM peut parfois accélérer le dos à d'autres solutions. Il n'y a pas de magie dans sa vitesse, il existe une politique très compétente de mise en cache des requêtes vers les bases de données, des tas d'optimisations et des astuces avec le code. Au total, cela conduit au fait que le poney frit à la vitesse du cheval.
Le moins peut être appelé la syntaxe de requête dans cet ORM - elle est très spécifique, avec l'utilisation de générateurs, de lambdas et d'autres chignons en langage Python.
query = select(c for c in Customer if sum(o.total_price for o in c.orders) > 1000) Product.select().order_by(lambda p: desc(sum(p.order_items.quantity))).first()
Cette approche nécessite une certaine décomposition du cerveau.
Orme de tortue

En fouillant dans divers dépôts acceptant les animaux de compagnie, j'ai trouvé une collection de tests de différents ORM avec des mesures de vitesse. En plus du Pony ORM déjà mentionné, un certain Tortoise ORM est apparu dans la liste des plus rapides. Il est clair que les résultats des tests dépendent de la personne qui écrit les tests et de la façon dont ils s'exécutent, mais vous devez y regarder de plus près.
La tortue est un projet relativement jeune, qui est toujours en développement actif. Les bonnes performances de cette bibliothèque s'expliquent par le fait que l'ORM ne contient rien de superflu et est prêt à l'emploi emprisonné pour un fonctionnement asynchrone. Et il suppose également l'utilisation d' uvloop , qui fonctionne plus rapidement que les cycles de piton natifs des événements.
Cet ORM est encore trop brut pour être utilisé au combat (par exemple, jusqu'à ce que les pools de connexions soient même implémentés), mais vous devriez regarder le développement de cette lib. Si tout va bien avec les développeurs, dans l'année à venir, nous aurons un wrapper très rapide pour la base de données avec une bonne vitesse et sans l'étrange syntaxe dans le style Pony.