psycopg2.extras.DictRow ведет себя по-разному при использовании for и next - PullRequest
0 голосов
/ 01 августа 2020

Это v2.8.5, работающая в python 3.8.5. Следующее работает, как ожидалось:

  curs = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
  curs.execute("select fid from A")
  for row in curs:                                                   
    print(row['fid'])                                                   

, но не работает:

  row = next(curs, None)                                             
  print(row['fid'])                                                       

  File "/usr/local/lib/python3.8/site-packages/psycopg2/extras.py", line 168, in __getitem__
    x = self._index[x]
KeyError: 'fid'

Тип row в обоих случаях - <class 'psycopg2.extras.DictRow'>.

Очевидно где-то ошибка пилота?

Ответы [ 2 ]

1 голос
/ 01 августа 2020

psycopg2.extras.DictRow уже реализует / обертывает ваши вызовы next(...) и поставляется с функцией __inter__, которая подготавливает и заполняет DictRow ( source ).

Как вы можете см. в реализации функции _build_index он заполняет OrderedDict. При наведении курсора создается (заполненный) _index для каждого DictRow:

pgconn = psycopg2.connect("dbname=mf port=5959 host=localhost user=mf_usr")
curs = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)

curs.execute("select * from users where id > 366200 and id < 366203")

for r in curs:
    print(r._index)

Out:

OrderedDict([('id', 0), ('firstname', 1), ('lastname', 2), ('birth', 3), ('ua', 4), ('nationality', 5)])
OrderedDict([('id', 0), ('firstname', 1), ('lastname', 2), ('birth', 3), ('ua', 4), ('nationality', 5)])

Использование next(...) «извне» ведет себя странно и не использует __iter__, поэтому вы получаете пустой индекс, поэтому KeyError падает в:

...
curs.execute("select * from users where id > 366200 and id < 366203")

row = next(curs, None)
print(row._index)

Out:

OrderedDict()
0 голосов
/ 01 августа 2020

DictCursor - это гибридная структура:

https://www.psycopg.org/docs/extras.html

"class psycopg2.extras.DictCursor (* args, ** kwargs)

Курсор, который хранит список имен столбцов -> сопоставления индексов "

Попробуйте с RealDictCursor, который возвращает фактический словарь:

" class psycopg2.extras.RealDictCursor (* args , ** kwargs)

Курсор, который использует реальный dict в качестве базового типа для строк.

Обратите внимание, что этот курсор чрезвычайно специализирован и не допускает нормального доступа (с использованием целочисленных индексов) для извлечения данных. Если вам нужен доступ к строкам базы данных и как словарю, и как к списку, используйте общий c DictCursor вместо RealDictCursor. "

Например:

con = psycopg2.connect("dbname=production host=localhost user=postgres", cursor_factory=RealDictCursor) 

cur = con.cursor()
cur.execute("select * from cell_per")
next(cur)["cell_per"]
18
...