Python sqlite вставляет именованные параметры или null

Я пытаюсь вставить данные из словаря в базу данных, используя именованные параметры. У меня это работает с простым оператором SQL, например.

SQL = "INSERT INTO status (location, arrival, departure) VALUES (:location, :arrival,:departure)"
dict = {'location': 'somewhere', 'arrival': '1000', 'departure': '1001'}
c.execute(SQL,dict)

Вставляет где-то место, 1000 в столбец прибытия и 1001 в колонку отправления.

Данные, которые у меня будут на самом деле, будут содержать местоположение, но могут содержать либо прибытие, либо вылет, но могут не иметь обоих (в этом случае ничто, ни NULL не могут войти в таблицу). В этом случае я получаю sqlite3.ProgrammingError: вы не указали значение для привязки 2.

Я могу исправить это, используя defaultdict:

c.execute(SQL,defaultdict(str,dict))

Чтобы сделать вещи немного более сложными, у меня действительно будет список словарей, содержащих несколько мест с приходом или отъездом.

    ({'location': 'place1', 'departure': '1000'},
    {'location': 'palce2', 'arrival': '1010'},
    {'location': 'place2', 'departure': '1001'})

и я хочу иметь возможность запускать это с помощью c.executemany, но теперь я не могу использовать defaultdict.

Я мог бы прокручивать каждый словарь в списке и запускать множество операторов c.execute, но execemany кажется более аккуратным способом сделать это.

Я упростил этот пример для удобства, фактические данные имеют гораздо больше записей в словаре, и я создаю его из текстового файла JSON.

У кого-нибудь есть предложения по тому, как я могу это сделать?

Ответ 1

Используйте None для вставки NULL:

dict = {'location': 'somewhere', 'arrival': '1000', 'departure': None}

Вы можете использовать словарь по умолчанию и генератор для использования с помощью executemany():

defaults = {'location': '', 'arrival': None, 'departure': None}

c.executemany(SQL, ({k: d.get(k, defaults[k]) for k in defaults} for d in your_list_of_dictionaries)

Ответ 2

Существует более простое решение этой проблемы, которое должно быть осуществимо в большинстве случаев; просто перейдите к executemany списку defaultdict вместо списка dict.

Другими словами, если вы построите с нуля свои строки как defaultdict, вы можете передать список строк defaultdict непосредственно в команду executemany вместо того, чтобы создавать их как словари, а затем исправлять ситуацию перед использованием executemany.

В следующем рабочем примере (Python 3.4.3) показана точка:

import sqlite3
from collections import defaultdict
# initialization
db = sqlite3.connect(':memory:')
c = db.cursor()
c.execute("CREATE TABLE status(location TEXT, arrival TEXT, departure TEXT)")
SQL = "INSERT INTO status VALUES (:location, :arrival, :departure)"
# build each row as a defaultdict
f = lambda:None # use str if you prefer
row1 = defaultdict(f,{'location':'place1', 'departure':'1000'})
row2 = defaultdict(f,{'location':'place2', 'arrival':'1010'})
rows = (row1, row2)
# insert rows, executemany can be safely used without additional code
c.executemany(SQL, rows)
db.commit()
# print result
c.execute("SELECT * FROM status")
print(list(zip(*c.description))[0])
for r in c.fetchall():
    print(r)
db.close()

Если вы запустите его, он печатает:

('location', 'arrival', 'departure')
('place1', None, '1000') # None in Python maps to NULL in sqlite3
('place2', '1010', None)