[Python] Psycopg2 e serializzazione delle SELECT in JSON

Daniele Varrazzo piro a develer.com
Gio 16 Ott 2014 16:32:41 CEST


On 2014-10-16 15:08, Alberto Granzotto wrote:
> Ciao Daniele, grazie per la esaustiva risposta, mi sembra tutto chiaro 
> a
> parte una cosa ->
> 
> 2014-10-16 15:47 GMT+02:00 Daniele Varrazzo <piro a develer.com>:
> 
>> On 2014-10-16 13:59, Alberto Granzotto wrote:
>> [...]l as (id integer, email text);
>> 
>> e riscrivi la tua query in maniera da resitiuire un array di questi 
>> tipi:
>> test=> select u.id, u.name, array_agg((e.id, e.email)::t_email) from
>> users u join emails e on u.id = e.user_id group by 1,2;
>> 
> 
> C'è modo di evitare il group by? O devo prima estrarmi tutti gli id 
> delle
> email associate allo specifico user e poi usarle nel group by? Cosa 
> succede
> se invece di estrarre un solo user volessi estrarre N user?

Il group by e' necessario per usare array_agg: se non lo usi hai i dati 
di ogni utente ripetuti se un utente ha piu' di una email (come nella 
tua query originale). Puoi usare una sottoquery che crea una mappa 
id_utente -> array di t_email e fare il join di questa con gli utenti, 
ma un group by (sebbene piu' efficiente) ci vuole sempre, es. nella 
sottoquery.

In linea generale, psycopg ti restituira' un oggetto per ogni record che 
Postgres genera: i gradi di liberta' sono nel record generato da 
postgres (quindi e.g. usare group by, creare array, strutture, 
dizionari, ecc. usando i tipi di postgres in maniera un po' piu' 
avanzata) e nel configurare il mapping tra i record letti e gli oggetti 
python creati da psycopg (giocando con i typecaster). Se esci da questo 
framework (un record -> un oggetto) allora devi fare qualcosa a valle di 
psycopg, quindi ti ritrovi con i tipi di algoritmi ripetitivi per cui 
hai scritto la mail in primo momento: iterare sugli utenti, controllare 
se li avevi gia' letti, metterli in un dict con una lista di email 
vuote, aggiungere una email...

Quindi, detto questo, come mai vuoi evitare il group by? Ti aspetti di 
leggere i dati da postgres in una maniera che non ho capito (nel qual 
caso fammi un esempio) oppure ti aspetti un comportamento di psycopg che 
esula dal restitiure un oggetto per ogni record?

Estrarre N utenti si fa esattamente come sopra: la query restituisce un 
record per utente, ognuno con la lista delle sue email, e puoi iterare 
sul cursore in tutti i modi che psycopg offre (fetchmany(), fetchall(), 
for record in cur:...)

     test=> insert into users (name) values ('daniele');
     INSERT 0 1
     test=> insert into emails (user_id, email) values (2, 
'piro a officine');
     INSERT 0 1
     test=> insert into emails (user_id, email) values (2, 
'piro a develer');
     INSERT 0 1

     In [20]: dcur.execute("select u.id, u.name, array_agg((e.id, 
e.email)::t_email) as emails from users u join emails e on u.id = 
e.user_id group by 1,2")

     In [21]: dcur.fetchall()
     Out[21]:
     [{'emails': [
         {'email': 'alberto a example.org', 'id': 1},
         {'email': 'alberto a lemonparty.org', 'id': 2}],
       'id': 1,
       'name': 'alberto'},
      {'emails': [
         {'email': 'piro a officine', 'id': 4},
         {'email': 'piro a develer', 'id': 5}],
       'id': 2,
       'name': 'daniele'}]


-- Daniele


Maggiori informazioni sulla lista Python