Reputation: 7621
This is v2.8.5 running in python 3.8.5. The following works as expected:
curs = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
curs.execute("select fid from A")
for row in curs:
print(row['fid'])
but this does not:
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'
The type of row
is <class 'psycopg2.extras.DictRow'>
in both cases.
Obvious pilot error somewhere?
Upvotes: 0
Views: 1006
Reputation: 18136
psycopg2.extras.DictRow
already implements/wraps your next(...)
calls and comes with a __inter__
function which prepares and filles the DictRow
(source).
As you can see in the implementation of a _build_index
function it populates an OrderedDict
.
Looping over the cursor, creates an (filled) _index
for each 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)])
Using next(...)
from the 'outside', behaves strange and does not use __iter__
, so you end up with an empty index, so the KeyError
drops in:
...
curs.execute("select * from users where id > 366200 and id < 366203")
row = next(curs, None)
print(row._index)
Out:
OrderedDict()
Upvotes: 2
Reputation: 19724
DictCursor
is a hybrid structure:
https://www.psycopg.org/docs/extras.html
" class psycopg2.extras.DictCursor(*args, **kwargs)
A cursor that keeps a list of column name -> index mappings "
Try with RealDictCursor
which returns an actual dictionary:
" class psycopg2.extras.RealDictCursor(*args, **kwargs)
A cursor that uses a real dict as the base type for rows.
Note that this cursor is extremely specialized and does not allow the normal access (using integer indices) to fetched data. If you need to access database rows both as a dictionary and a list, then use the generic DictCursor instead of RealDictCursor. "
As example:
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
Upvotes: 0