web-dev-qa-db-fra.com

Python Sqlite3: INSERT INTO table VALUE (le dictionnaire va ici)

Je voudrais utiliser un dictionnaire pour insérer des valeurs dans une table, comment pourrais-je faire cela? 

import sqlite3

db = sqlite3.connect('local.db')
cur = db.cursor()

cur.execute('DROP TABLE IF EXISTS Media')

cur.execute('''CREATE TABLE IF NOT EXISTS Media(
                id INTEGER PRIMARY KEY, title TEXT, 
                type TEXT,  genre TEXT,
                onchapter INTEGER,  chapters INTEGER,
                status TEXT
                )''')


values = {'title':'jack', 'type':None, 'genre':'Action', 'onchapter':None,'chapters':6,'status':'Ongoing'}

#What would I Replace x with to allow a 
#dictionary to connect to the values? 
cur.execute('INSERT INTO Media VALUES (NULL, x)'), values)
cur.execute('SELECT * FROM Media')

meida = cur.fetchone()

print meida
13
Crispy

Si vous essayez d'utiliser une variable dict pour spécifier les noms de colonne et les valeurs, vous ne pouvez pas le faire, du moins pas directement.

C'est vraiment inhérent à SQL. Si vous ne spécifiez pas la liste des noms de colonnes, vous devez les spécifier dans l'ordre CREATE TABLE, ce que vous ne pouvez pas utiliser avec un dict, car un dict n'a pas d'ordre. Bien sûr, si vous le vouliez, vous pouvez utiliser un collections.OrderedDict, assurez-vous qu'il est dans le bon ordre, puis passez simplement values.values(). Mais à ce stade, pourquoi ne pas simplement avoir une list (ou Tuple) en premier lieu? Si vous êtes absolument sûr que vous avez toutes les valeurs, dans le bon ordre, et que vous souhaitez y faire référence par ordre plutôt que par nom, ce que vous avez est une list, pas une dict.

Et il n'y a aucun moyen de lier des noms de colonnes (ou noms de tables, etc.) en SQL, mais uniquement des valeurs.

Vous pouvez bien sûr générer l’instruction SQL de manière dynamique. Par exemple:

columns = ', '.join(values.keys())
placeholders = ', '.join('?' * len(values))
sql = 'INSERT INTO Media ({}) VALUES ({})'.format(columns, placeholders)
cur.execute(sql, values.values())

Cependant, c'est presque toujours une mauvaise idée. Ce n'est vraiment pas mieux que de générer et de modifier execing du code Python dynamique. Et vous venez tout juste de perdre tous les avantages de l'utilisation de paramètres de substitution, principalement la protection contre les attaques par injection SQL, mais également des éléments moins importants, tels qu'une compilation plus rapide, une meilleure mise en cache, etc. au sein du moteur de base de données.

Il est probablement préférable de prendre du recul et d'examiner ce problème à un niveau supérieur. Par exemple, vous ne vouliez peut-être pas vraiment une liste statique de propriétés, mais plutôt une table nom-valeur MediaProperties? Ou encore, vous souhaitez peut-être un type de stockage documentaire (qu'il s'agisse d'un système nosql de haute puissance ou simplement d'un groupe d'objets JSON ou YAML stockés dans une shelve)?


Une alternative utilisant des espaces réservés nommés :

columns = ', '.join(my_dict.keys())
placeholders = ':'+', :'.join(my_dict.keys())
query = 'INSERT INTO my_table (%s) VALUES (%s)' % (columns, placeholders)
print query
cur.execute(query, my_dict)
con.commit()
23
abarnert

Vous pouvez utiliser paramètres nommés :

cur.execute('INSERT INTO Media VALUES (NULL, :title, :type, :genre, :onchapter, :chapters, :status)', values)

Cela dépend toujours de l'ordre des colonnes dans l'instruction INSERT (ces : ne sont utilisés que comme clés dans le dictionnaire values), mais il évite au moins de devoir commander les valeurs du côté python. Vous pouvez également avoir d'autres éléments dans values sont ignorés ici; Si vous séparez ce qui est dans le dict pour le stocker dans plusieurs tables, cela peut être utile.

Si vous souhaitez toujours éviter de dupliquer les noms, vous pouvez les extraire d'un objet résultat sqlite3.Row ou de cur.description après avoir effectué une requête fictive. il peut être plus sain de les conserver sous forme de python, peu importe où vous faites votre CREATE TABLE.

9
eichin

Il existe une solution pour utiliser des dictionnaires. Tout d'abord, l'instruction sql

INSERT INTO Media VALUES (NULL, 'x');

ne fonctionnerait pas, car cela suppose que vous vous référiez à toutes les colonnes, dans l'ordre dans lequel elles sont définies dans l'instruction CREATE TABLE, comme indiqué par abarnert. (Voir SQLite INSERT .)

Une fois que vous avez résolu le problème en spécifiant les colonnes, vous pouvez utiliser des espaces réservés nommés pour insérer des données. L'avantage est que vous échappez en toute sécurité aux personnages-clés, de sorte que vous n'avez pas à vous inquiéter. De la Python sqlite-documentation :

values = {'title':'jack', 'type':None, 'genre':'Action', 'onchapter':None,'chapters':6,'status':'Ongoing'}
cur.execute('INSERT INTO Media (id, title, type, onchapter, chapters, status) VALUES (:id, :title, :type, :onchapter, :chapters, :status);'), values)
9
MrGumble

J'avais un problème similaire et je me suis retrouvé avec quelque chose d'assez différent de ce qui suit (Remarque: il s'agit du code de l'OP dont les bits ont été modifiés de sorte que cela fonctionne comme ils l'avaient demandé) -

import sqlite3
db = sqlite3.connect('local.db')
cur = db.cursor()

cur.execute('DROP TABLE IF EXISTS Media')

cur.execute('''CREATE TABLE IF NOT EXISTS Media(
                id INTEGER PRIMARY KEY, title TEXT, 
                type TEXT,  genre TEXT,
                onchapter INTEGER,  chapters INTEGER,
                status TEXT
                )''')


values = {'title':'jack', 'type':None, 'genre':'Action',     'onchapter':None,'chapters':6,'status':'Ongoing'}

#What would I Replace x with to allow a 
#dictionary to connect to the values? 
#cur.execute('INSERT INTO Media VALUES (NULL, x)'), values)
# Added code.
cur.execute('SELECT * FROM Media')
colnames = cur.description
list = [row[0] for row in cur.description]
new_list = [values[i] for i in list if i in values.keys()]
sql = "INSERT INTO Media VALUES ( NULL, "
qmarks = ', '.join('?' * len(values))
sql += qmarks + ")"
cur.execute(sql, new_list)
#db.commit() #<-Might be important.
cur.execute('SELECT * FROM Media')
media = cur.fetchone()
print (media)
0
Subgenius
key_lst = ('status', 'title', 'chapters', 'onchapter', 'genre', 'type')
cur.execute('INSERT INTO Media (status,title,chapters,onchapter,genre,type) VALUES ' + 
            '(?,?,?,?,?,?);)',Tuple(values[k] for k in key_lst))

Faites-vous échapper à droite .

Vous avez probablement aussi besoin d’un appel commit quelque part.

0
tacaswell

J'avais le même problème, j'ai donc d'abord créé une chaîne, puis passé cette chaîne pour exécuter la commande. Cela prend plus de temps à exécuter mais la cartographie était parfaite pour moi. Juste un travail autour:

create_string = "INSERT INTO datapath_rtg( Sr_no"
for key in record_tab:
    create_string = create_string+ " ," + str(key)
create_string = create_string+ ") VALUES("+ str(Sr_no) 
for key in record_tab:
    create_string = create_string+ " ," + str(record_tab[key])
create_string = create_string + ")"
cursor.execute(create_string)

En faisant ce qui précède, je me suis assuré que si mon dict (record_tab) ne contenait pas de champ particulier, le script ne renverrait pas d'erreur et un mappage correct pourrait être effectué.

0
yash jain