|
| 1 | +Title: La magie de SQLAlchemy |
| 2 | +Date: 2014-02-09 21:00 |
| 3 | +Slug: la-magie-de-sqlalchemy |
| 4 | +Author: Québec-Python |
| 5 | +Summary: Recette pour faire de l'insertion de données à partir d'un fichier CSV avec SQLAlchemy |
| 6 | + |
| 7 | +Voici donc une petite recette bien savoureuse pour extraire des données d'un fichier CSV et l'insérer dans une base de données. |
| 8 | + |
| 9 | +3 modules seront utilisés: |
| 10 | + |
| 11 | +* [SQLAlchemy](http://www.sqlalchemy.org/): L'ORM |
| 12 | +* [Fabric](http://fabric.readthedocs.org/en/1.8/): Pas vraiment nécessaire dans ce cas-ci, mais je l'utilise pour afficher des messages de couleur dans le terminal. |
| 13 | +* [PySQLite](https://pypi.python.org/pypi/pysqlite): Pour la connexion à une base de données SQLite |
| 14 | + |
| 15 | +Roulez les commandes suivantes pour les installer dans votre environnement virtuel: |
| 16 | + |
| 17 | + :::sh |
| 18 | + $ virtualenv venv |
| 19 | + $ source venv/bin/activate |
| 20 | + $ pip install sqlalchemy fabric pysqlite |
| 21 | + |
| 22 | +Nous avons donc les données suivantes: |
| 23 | + |
| 24 | + :::text |
| 25 | + country,nas,fullname,created_at,birthday,email |
| 26 | + Maldives,122,Tre Kunde,Sat Oct 04 1980 19:18:34,23/5/2007,Diamond@buster.co.uk |
| 27 | + Christmas Island,123,Nina Sauer I,Sun Sep 11 2011 07:11:11,6/3/2008,Fae@haskell.biz |
| 28 | + Western Sahara,124,Jerod O'Reilly,Mon May 24 1993 03:34:03,27/5/1982,Eloy@jailyn.tv |
| 29 | + Maldives,125,Aliza Nienow,Fri Nov 29 1996 07:39:35,26/8/1993,Johnson_Marvin@lucius.io |
| 30 | + Sierra Leone,126,Litzy Altenwerth MD,Sun Jun 06 1993 04:05:20,14/1/1981,Nicolas@adolfo.com |
| 31 | + Peru,127,Willow Bahringer,Tue Sep 15 1992 12:20:10,4/12/1988,Jesse@christy.org |
| 32 | + Andorra,128,Ward Bode,Mon Oct 12 1987 17:56:50,10/8/1992,Brandon@helen.co.uk |
| 33 | + Philippines,129,Maureen Boehm,Wed Sep 09 1998 00:22:14,7/6/1996,Deondre.Friesen@elroy.ca |
| 34 | + United Arab Emirates,130,Linda Bins,Sat May 09 1992 06:41:04,15/1/1984,Jessy@cassidy.me |
| 35 | + |
| 36 | +Du code de base pour SQLAlchemy: |
| 37 | + |
| 38 | + :::python |
| 39 | + import os |
| 40 | + import inspect |
| 41 | + |
| 42 | + from fabric.colors import red, green |
| 43 | + |
| 44 | + from sqlite3 import dbapi2 as sqlite |
| 45 | + |
| 46 | + from sqlalchemy import create_engine |
| 47 | + from sqlalchemy.orm import sessionmaker |
| 48 | + from sqlalchemy.ext.declarative import declarative_base |
| 49 | + from sqlalchemy import ( |
| 50 | + Column, Integer, String, DateTime, Date |
| 51 | + ) |
| 52 | + |
| 53 | + CURRENT_DIRECTORY = os.path.dirname( |
| 54 | + inspect.getfile(inspect.currentframe()) |
| 55 | + ) |
| 56 | + |
| 57 | + DATABASE_NAME = "users" |
| 58 | + |
| 59 | + Base = declarative_base() |
| 60 | + |
| 61 | +Une classe qui fera le lien entre notre code et la base de données SQLite: |
| 62 | + |
| 63 | + :::python |
| 64 | + class User(Base): |
| 65 | + __tablename__ = "users" |
| 66 | + id = Column("id", Integer, primary_key = True) |
| 67 | + nas = Column("nas", Integer) |
| 68 | + country = Column("country", String(200)) |
| 69 | + fullname = Column("fullname", String(200)) |
| 70 | + created_at = Column("created_at", DateTime) |
| 71 | + birthday = Column("birthday", Date) |
| 72 | + email = Column("email", String(200)) |
| 73 | + |
| 74 | +Une fonction de création de bd: |
| 75 | + |
| 76 | + :::python |
| 77 | + def init_db(): |
| 78 | + path = os.path.join(CURRENT_DIRECTORY, "{}.db".format(DATABASE_NAME)) |
| 79 | + |
| 80 | + if os.path.isfile(path): |
| 81 | + print(red(u"La base de données existe déjà...")) |
| 82 | + print(red(u"Sa création est annulé")) |
| 83 | + return |
| 84 | + |
| 85 | + # Création d'un fichier sqlite s'il n'existe pas déjà |
| 86 | + sqlite.connect(path) |
| 87 | + |
| 88 | + engine = create_engine('sqlite:///%s' % path, convert_unicode=True) |
| 89 | + Base.metadata.create_all(bind=engine) |
| 90 | + print(green(u"Base de données créé !")) |
| 91 | + |
| 92 | +Et finalement, une fonction de création de session: |
| 93 | + |
| 94 | + :::python |
| 95 | + def connect(debug=False): |
| 96 | + path = os.path.join(CURRENT_DIRECTORY, "{}.db".format(DATABASE_NAME)) |
| 97 | + engine = create_engine( |
| 98 | + 'sqlite+pysqlite:///{}'.format(path), |
| 99 | + module=sqlite, |
| 100 | + echo=debug |
| 101 | + ) |
| 102 | + SessionMakerInstance = sessionmaker(bind=engine) |
| 103 | + return SessionMakerInstance() |
| 104 | + |
| 105 | +Maintenant que nous avons tous les ingrédients, voici la recette elle-même. Prenez le temps de lire les commentaires si vous êtes un débutant: |
| 106 | + |
| 107 | + :::python |
| 108 | + import csv |
| 109 | + from glob import glob |
| 110 | + from datetime import datetime |
| 111 | + |
| 112 | + # obtention de tous les fichiers *.csv dans le dossier courant |
| 113 | + CSV_PATH = os.path.join("*.csv") |
| 114 | + csv_files = glob(CSV_PATH) |
| 115 | + csv_files.reverse() |
| 116 | + |
| 117 | + # création de la base de données |
| 118 | + init_db() |
| 119 | + |
| 120 | + # Création de la connexion à la base de données |
| 121 | + session = connect(debug=True) |
| 122 | + |
| 123 | + # Pour chacun des fichiers csv dans le dossier data |
| 124 | + for csv_file in csv_files: |
| 125 | + |
| 126 | + # ouverture du fichier. à noté: fermeture implicite à la sortie du "with" |
| 127 | + with open(csv_file) as f: |
| 128 | + # utilisation de DictReader pour transformer chaque ligne |
| 129 | + # du fichier texte en dictionnaire |
| 130 | + # ex: |
| 131 | + # Cette ligne du fichier csv: |
| 132 | + # Maldives,122,Tre Kunde,Sat Oct 04 1980 19:18:34,23/5/2007,Diamond@buster.co.uk |
| 133 | + # |
| 134 | + # donne ceci: |
| 135 | + # { |
| 136 | + # "country": Maldives, |
| 137 | + # "nas": 122, |
| 138 | + # "fullname": "Tre kunde", |
| 139 | + # "created_at": "Sat Oct 04 1980 19:18:34", |
| 140 | + # "birthday": "23/5/2007", |
| 141 | + # "email": "Diamond@buster.co.uk" |
| 142 | + # } |
| 143 | + csv_content = csv.DictReader(f) |
| 144 | + |
| 145 | + # à l'aide d'un generator, instanciation d'un user pour chaque ligne du DictReader |
| 146 | + session.add_all(( |
| 147 | + User( |
| 148 | + nas=row["nas"], |
| 149 | + country=row["country"], |
| 150 | + fullname=row["fullname"], |
| 151 | + # Tue Dec 31 1996 11:35:43 |
| 152 | + created_at=datetime.strptime( |
| 153 | + row["created_at"], "%a %b %d %Y %H:%M:%S" |
| 154 | + ).date(), |
| 155 | + birthday=datetime.strptime( |
| 156 | + row["birthday"], "%d/%m/%Y" |
| 157 | + ).date(), |
| 158 | + email=row["email"] |
| 159 | + ) for row in csv_content |
| 160 | + )) |
| 161 | + |
| 162 | + session.commit() |
| 163 | + |
| 164 | +Vous pouvez prendre le temps de copier-coller tous ces fragments dans un seul et même fichier ou bien [télécharger un fichier zip avec le même contenu...](https://gist.github.com/bchhun/8909903/download) Si vous le téléchargez, vous n'avez qu'à rouler le fichier *magie.py* par la suite. |
| 165 | + |
| 166 | +N'hésitez pas à nous poser des questions ou a partager votre expérience avec cette recette dans la liste de diffusions ou sur les réseaux sociaux. |
| 167 | + |
| 168 | +L'équipe de Québec-Python |
0 commit comments