2013-05-02 13 views
9

Ich spiele ein wenig mit der Python API für sqlite3, ich habe eine kleine Tabelle für Shop-Sprachen mit einem ID, Name und creation_date Felder. Ich versuche, die rohen Abfrageergebnisse in eine abzubilden, wie die Dokumente empfehlen, es auf diese Weise kann ich Zeilen in einer lesbareren Weise verwalten, also hier ist meine namedtuple.Mapping Ergebnis Zeilen zu namedtuple in Python sqlite

LanguageRecord = namedtuple('LanguageRecord', 'id, name, creation_date') 

Der Code, der die Dokumentation für die Abbildung vorschlägt, ist wie folgt:

for language in map(LanguageRecord._make, c.fetchall()): 
    # do something with languages 

Das ist in Ordnung, wenn ich eine Sammlung von Sprachen zurückkehren will, aber in diesem Fall mag ich nur ein abrufen Sprache:

c.execute('SELECT * FROM language WHERE name=?', (name,)) 

So war mein erster Versuch es so etwas wie dieses:

language = map(LanguageRecord._make, c.fetchone()) 

Dieser Code nicht funktioniert nicht, weil fetchone() ein Tupel zurück, anstatt eine Liste mit einem Tupel, so die map Funktion versucht, drei namedtuples einen für jeden Tupel Feld Gedanken zu erstellen.

Mein erster Ansatz zur Lösung dieses war explizit eine Liste zu erstellen, und wie es das Tupel Ergebnis etwas anfügen:

languages = [] 
languages.append(c.fetchone()) 
for language in map(LanguageRecord._make, languages): 
    # do something with language 

Mein zweiter Ansatz war fetchall() zu verwenden, obwohl ich einen Datensatz nur wollen. Ich kann das Namensfeld mit einer unique Einschränkung in der Datenbank festlegen, um nur ein Ergebnis zu garantieren.

for language in map(LanguageRecord._make, c.fetchall()): 
    # do something with languages 

konnte Ein anderer Ansatz Verwendung fetchall()[0] ohne unique constrain sein nur ein Ergebnis garantize.

Meine Frage ist, welche die beste und gebräuchlichste Methode ist, um mit diesem Problem umzugehen, sollte ich immer fetchall verwenden, um eine gemeinsame Schnittstelle zu pflegen und die Datenbank die Eindeutigkeitslogik verwalten zu lassen? oder sollte ich explizit eine liste wie im ansatz 1 erstellen? Gibt es einen einfacheren Weg, um diese Aufgabe zu erfüllen?

+0

Sie können auch die Datenbank-Cursor durchlaufen, gibt es keine Notwendigkeit, alle Datensätze zu holen, es sei denn, Sie wollen zu, so kann der Code als 'map (LanguageRecord._make, c)' umgeschrieben werden. –

Antwort

19

Es gibt einen viel einfacheren Weg! Sqlite3 bietet dem Benutzer die Möglichkeit, "row factories" zu definieren. Diese Zeilenfabriken übernehmen den Cursor und die Tupelzeile und können den gewünschten Objekttyp zurückgeben.

Sobald Sie die Reihe Fabrik mit

con.row_factory = my_row_factory 

dann Zeilen durch die Cursor zurück gesetzt wird das Ergebnis von my_row_factory auf die Tupel-Reihe angelegt sein. Zum Beispiel

import sqlite3 
import collections 

LanguageRecord = collections.namedtuple('LanguageRecord', 'id name creation_date') 
def namedtuple_factory(cursor, row): 
    return LanguageRecord(*row) 

con = sqlite3.connect(":memory:") 
con.row_factory = namedtuple_factory 
cur = con.cursor() 
cur.execute("select 1,2,3") 
print(cur.fetchone()) 

ergibt

LanguageRecord(id=1, name=2, creation_date=3) 

Für ein weiteres Beispiel dafür, wie eine namedtuple Fabrik zu definieren, siehe this post.


By the way, wenn Sie setzen

conn.row_factory = sqlite3.Row 

dann Zeilen werden als dicts zurückgegeben, dessen Schlüssel die Spaltennamen der Tabelle. Anstatt auf Teile des namedtuple mit Dingen wie row.creation_date zuzugreifen, könnten Sie einfach die eingebaute sqlite3.Row Zeilenfactory verwenden und auf das Äquivalent mit row['creation_date'] zugreifen.

+1

'sqlite3.Row' ist kein echtes dict, da es' .get() 'oder' .__ contains __() 'nicht implementiert. – rgov

0

Ich denke, besser zu verwenden for language in map(LanguageRecord._make, c.fetchall()[:1]): Weil es IndexError mit fetchall() [0] verursachen kann.

Wenn Sie ein Ergebnis benötigen und es bereits "WHERE" in Abfrage gibt. Wie ich verstehe, sollte Abfrage eine Zeile zurückgeben. Frühe Optimierung ist böse. :)

1

Eine verbesserte row_factory eigentlich ist das, was für alle Arten von Anfragen wiederverwendet werden können:

from collections import namedtuple 

def namedtuple_factory(cursor, row): 
    """Returns sqlite rows as named tuples.""" 
    fields = [col[0] for col in cursor.description] 
    Row = namedtuple("Row", fields) 
    return Row(*row) 

conn = sqlite3.connect(":memory:") 
conn.row_factory = namedtuple_factory 
cur = con.cursor() 
Verwandte Themen