2017-05-23 1 views
0

J'écris du code en utilisant psycopg2 pour me connecter à une base de données PostGreSQL.Python - créer une chaîne à partir d'éléments dict (pour écrire dans postgresql db)

J'ai beaucoup de différents types de données que je veux écrire sur différentes tables dans ma base de données PostGreSQL. J'essaie d'écrire une fonction qui peut écrire dans chacune des tables en fonction d'une seule variable passée dans la fonction et je veux écrire plus d'une ligne à la fois pour optimiser ma requête. Heureusement, PostGreSQL me permet de le faire: PostGreSQL Insert:

INSERT INTO films (code, title, did, date_prod, kind) VALUES 
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'), 
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy'); 

J'ai couru un problème que j'espérais que quelqu'un pourrait me aider.

Je dois créer une chaîne:

string1 = (value11, value21, value31), (value12, value22, value32) 

La variable string1 sera créé en utilisant un dictionnaire avec des valeurs. Jusqu'à présent, j'ai été capable de créer un tuple proche de la structure que je veux. J'ai une liste de dictionnaires. La liste est appelée rows:

string1 = tuple([tuple([value for value in row.values()]) for row in rows]) 

Pour le tester, je l'ai créé la variable petite rows suivante:

rows = [{'id': 1, 'test1': 'something', 'test2': 123}, 
     {'id': 2, 'test1': 'somethingelse', 'test2': 321}] 

Lorsque des lignes est passé à travers la pièce au-dessus du code de string1 devient comme suit:

((1, 'something', 123), (2, 'somethingelse', 321)) 

Comme vu avec string1 J'ai juste besoin d'enlever la parenthèse extrême et en faire une chaîne pour que ce soit comme je le besoin t. Jusqu'à présent, je ne sais pas comment cela est fait. Donc, ma question à vous est: "Comment puis-je formater string1 pour avoir mon format requis?"

Répondre

1

execute_values rend beaucoup plus facile.Passez la séquence dict au lieu d'une séquence de valeurs:

import psycopg2, psycopg2.extras 

rows = [ 
    {'id': 1, 'test1': 'something', 'test2': 123}, 
    {'id': 2, 'test1': 'somethingelse', 'test2': 321} 
] 

conn = psycopg2.connect(database='cpn') 
cursor = conn.cursor() 

insert_query = 'insert into t (id, test1, test2) values %s' 
psycopg2.extras.execute_values (
    cursor, insert_query, rows, 
    template='(%(id)s, %(test1)s, %(test2)s)', 
    page_size=100 
) 

Et les valeurs sont insérées:

table t; 
id |  test1  | test2 
----+---------------+------- 
    1 | something  | 123 
    2 | somethingelse | 321 

Pour que le nombre de lignes affectées utilisent un CTE:

insert_query = ''' 
    with i as (
     insert into t (id, test1, test2) values %s 
     returning * 
    ) 
    select count(*) from i 
''' 
psycopg2.extras.execute_values (
    cursor, insert_query, rows, 
    template='(%(id)s, %(test1)s, %(test2)s)', 
    page_size=100 
) 
row_count = cursor.fetchone()[0] 
+0

Est-ce que 'execute_values' supporte Python3? – Zeliax

+0

@Zeliax Je l'ai testé en Python3. 'execute_values' est nouveau dans Psycopg 2.7. Vérifiez votre version: 'psycopg2 .__ version__' –

+0

Merci. Je viens de le mettre à jour. J'étais sur 2.6.2. Va vérifier si cette méthode fait l'affaire. Dois-je exécuter 'execute' et faire un' conn.commit() 'après la dernière ligne de votre extrait de code? – Zeliax

2

Avec peu de modifications, vous pouvez y parvenir. changer votre morceau de morue comme suit

','.join([tuple([value for value in row.values()]).__repr__() for row in rows]) 

sortie courant est

tuple tuple

(('something', 123, 1), ('somethingelse', 321, 2))

Après la sortie de changements sera

au format de chaîne que vous voulez

"('something', 123, 1),('somethingelse', 321, 2)"

+0

Merci. C'est exactement ce dont j'avais besoin. Je ne pouvais juste pas envelopper ma tête autour d'elle. – Zeliax

+0

désolé ma première langue n'est pas l'anglais! deviner la réponse est utile :) – DexJ

1

La solution que vous avez décrite n'est pas très bonne car potentiellement elle pourrait endommager votre base de données - cette solution ne se soucie pas de la chaîne d'échappement, etc. L'injection SQL est donc possible. Heureusement, psycopg (et psycopg2) a curseur de methodsexecute et mogrify qui bien faire tout ce travail pour vous:

import contextlib 

with contextlib.closing(db_connection.cursor()) as cursor: 
    values = [cursor.mogrify('(%(id)s, %(test1)s, %(test2)s)', row) for row in rows] 
    query = 'INSERT INTO films (id, test1, test2) VALUES {0};'.format(', '.join(values)) 

Pour python 3:

import contextlib 

with contextlib.closing(db_connection.cursor()) as cursor: 
    values = [cursor.mogrify('(%(id)s, %(test1)s, %(test2)s)', row) for row in rows] 
    query_bytes = b'INSERT INTO films (id, test1, test2) VALUES ' + b', '.join(values) + b';' 
+0

Ouais. Mon plan était d'utiliser la méthode 'cursor.execute (query, inputs)' pour faire cela, mais j'ai fini par avoir quelques problèmes avec ça. Cependant, je ne peux pas me souvenir précisément de ce que ces problèmes impliquaient en ce moment – Zeliax

+0

Désolé pour les commentaires de Spam. Mais la méthode que j'ai doit être capable de s'adapter aux types de données que je transmets. Je transmettrai beaucoup de données différentes de différents types de données pour un grand nombre de tables différentes, qui devraient toutes passer par cette fonction. – Zeliax

+1

@Zeliax 'mogrify' peut formater tous les types de données simplement avec '% s' –