gtalarico / pyairtable

Python Api Client for Airtable
https://pyairtable.readthedocs.io
MIT License
765 stars 138 forks source link

On acquiring data, a null entry causes the key to not instantiate. #229

Closed MiaulerMousebane closed 1 year ago

MiaulerMousebane commented 1 year ago
I generated a testing table while learning the API for AirTable. Essentially, this looks as below, where NULL is the absence of any data at all, rather than the textual representation: Name Ident ColA
A 1 Item 1
Small 2 Item 2
Thing 3 Item 3
NULL 4 Item 4

The code is

from pyairtable import Api, Base, Table import json

table=Table('patextXXXXXX','appXXXXXX','tblXXXXXX') for record in table.iterate(page_size=1, max_records=1000,fields=['Name']): print (json.dumps(record))

While running a test to filter for "Name", the following output occurs: [{"id": "recXXXXXXXXXXXXX", "createdTime": "2023-02-19T00:55:05.000Z", "fields": {"Name": "Thing"}}] [{"id": "recXXXXXXXXXXXXX", "createdTime": "2023-02-19T01:01:15.000Z", "fields": {}}] [{"id": "recXXXXXXXXXXXXX", "createdTime": "2023-02-19T00:55:05.000Z", "fields": {"Name": "A"}}] [{"id": "recXXXXXXXXXXXXX", "createdTime": "2023-02-19T00:55:05.000Z", "fields": {"Name": "Small"}}]

Rather than generate a dict of {"Name": ""}, it incorrectly ignores that there is a Name key available with the contents of NULL, returning an empty dictionary {} instead.

As a request for a non-existent field results in an HTTP 422 error, I'd assert that a successful query for a known field (that returns nothing) should autovivify the key in the returned dictionary with an empty value.

BAPCon commented 1 year ago

Is it reproduced when using an actual filter rather than specifying the fields in the kargs?

mesozoic commented 1 year ago

What's the benefit of changing this behavior? A change of this sort would create a subtle inconsistency, because any time a developer doesn't pass fields= to the API, there will be no way for pyairtable to guess and populate null values.

So this would work fine...

>>> records = table.all(fields=["Name"])
>>> names = [record["Name"] for record in records]

...but the following would raise KeyError:

>>> records = table.all()
>>> names = [record["Name"] for record in records]

It might be safer for developers to have to always code as if every key is potentially missing, rather than having to maintain context about whether fields= was passed to the API endpoint that retrieved whatever data they're using.

mesozoic commented 1 year ago

I don't think we'll modify the behavior of the library here, since it would be a breaking change for existing code.