2013-05-02 11 views
9

Gram trochę z python api dla sqlite3, mam małą tabelę dla języków sklepu z identyfikatorami, nazwą i polami data_powstania. Próbuję odwzorować nieprzetworzone wyniki zapytania na namedtuple, tak jak zalecają docs, w ten sposób mogę zarządzać wierszami w bardziej czytelny sposób, więc tutaj jest mój namedtuple.Mapowanie wyników wierszy na namedtuple w python sqlite

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

Kod, który docs sugerują odwzorowania jest następujący:

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

Jest to dobre rozwiązanie, gdy chcę wrócić zbiór języków, ale w tym przypadku chcę tylko do pobierania jednego język:

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

Więc moja pierwsza próba to było coś takiego:

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

Ten kod nie działa, ponieważ fetchone() zwraca krotki zamiast listę z jednej krotki, więc funkcja map próbuje utworzyć trzy namedtuples jednym dla każdej myśli pola krotka.

Moje pierwsze podejście do rozwiązania tego problemu było wyraźnie utworzyć listę i dołączyć do niego wynik krotka, coś takiego:

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

Drugie podejście było użyć fetchall() chociaż chcę tylko jeden rekord. Mogę ustawić pole nazwy z ograniczeniem unique w bazie danych, aby zagwarantować tylko jeden wynik.

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

Innym rozwiązaniem mogłoby być wykorzystanie fetchall()[0] bez unique ograniczyć do garantize tylko jeden wynik.

Moje pytanie brzmi: jaki jest najlepszy i najpowszechniejszy sposób radzenia sobie z tym problemem, czy powinienem używać zawsze fetchall, aby utrzymywać wspólny interfejs i pozwolić bazie danych zarządzać logiką unikalności? czy powinienem utworzyć listę jawnie jak w podejściu 1? Czy istnieje łatwiejszy sposób na wykonanie tego zadania?

+0

Można również iteracyjne nad kursorem bazy danych, nie ma potrzeby, aby pobrać wszystkie rekordy, chyba że chcesz do, więc kod można przepisać jako 'map (LanguageRecord._make, c)'. –

Odpowiedz

19

Jest o wiele łatwiejszy sposób! Sqlite3 umożliwia użytkownikowi zdefiniowanie "row factories". Te fabryki wierszy pobierają kursor i wiersz krotki i mogą zwracać żądany typ obiektu.

Po ustawieniu fabrykę rzędzie z

con.row_factory = my_row_factory 

następnie wierszy zwracanych przez kursor będzie wynikiem my_row_factory stosowane do krotki rzędzie. Na przykład,

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()) 

daje

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

do innego przykładu, jak zdefiniować fabrykę namedtuple patrz this post.


Nawiasem mówiąc, jeśli ustawisz

conn.row_factory = sqlite3.Row 

następnie wiersze są zwracane jako dicts, których klucze są nazwy kolumn w tabeli. Tak więc, zamiast dostępu do części nazwanego pakietu z takimi obiektami jak row.creation_date, można po prostu użyć wbudowanej fabryki wierszy sqlite3.Row i uzyskać dostęp do odpowiednika z row['creation_date'].

+1

'sqlite3.Row' nie jest prawdziwym dict, ponieważ nie implementuje' .get() 'lub' .__ zawiera __() '. – rgov

0

Myślę, że lepiej użyć for language in map(LanguageRecord._make, c.fetchall()[:1]): Ponieważ może to spowodować IndexError z fetchall() [0].

Jeśli potrzebujesz jednego wyniku i już istnieje zapytanie "WHERE". Jak rozumiem, zapytanie powinno zwrócić jeden wiersz. Wczesna optymalizacja jest zła. :)

1

Ulepszony row_factory jest rzeczywiście tym, który może być ponownie użyty do wszelkiego rodzaju zapytania:

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() 
Powiązane problemy