Reputation: 927
I am kinda new to python and trying to create a DTO that minimizes the amount of properties exposed from my api. I use an Azure table storage to get records and then loop over it to create a smaller object omiting properties. Though somewhere in the process I get: "TypeError: 'AbbreviatedPackage' object is not iterable"
In my main I have the follwing call:
@app.get("/list/")
def process_results(request: Request, x_api_key: str = Depends(X_API_KEY)):
packageClient = QueryClient(az_connection_string, "appstoredev")
results = packageClient.query_packages_storage("PartitionKey eq 'app'")
return results
The query_packages_storage()
def query_packages_storage(self, filter_query):
from azure.data.tables import TableClient
table_client = TableClient.from_connection_string(conn_str=self.connection_string, table_name=self.table_name)
entities = table_client.query_entities(filter_query)
json_entities = []
for entity in entities:
print(entity['ImageUrl'])
filtered_entity = AbbreviatedPackage(
entity['ImageUrl'],
entity['PackageName'],
entity['DisplayName'],
entity['Summary']
)
json_entities.append(filtered_entity)
return json.dumps(json_entities)
AbbreviatedPackage class
class AbbreviatedPackage():
def __init__(self, image_url, package_name, display_name, summary):
self.imageUrl = image_url
self.packageName = package_name
self.displayName = display_name
self.summary = summary
When I debug the json_entitiesobject gets filled properly
Any help would be much sppreciated. Cheers
** Edit
I get the errors while being in the loop
INFO: 127.0.0.1:60027 - "GET /list/ HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 373, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__
return await self.app(scope, receive, send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\fastapi\applications.py", line 208, in __call__
await super().__call__(scope, receive, send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
raise exc
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\middleware\cors.py", line 84, in __call__
await self.app(scope, receive, send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\exceptions.py", line 82, in __call__
raise exc
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\routing.py", line 656, in __call__
await route.handle(scope, receive, send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\routing.py", line 259, in handle
await self.app(scope, receive, send)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\routing.py", line 61, in app
response = await func(request)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\fastapi\routing.py", line 226, in app
raw_response = await run_endpoint_function(
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\fastapi\routing.py", line 161, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\anyio\to_thread.py", line 28, in run_sync
return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\anyio\_backends\_asyncio.py", line 805, in run_sync_in_worker_thread
return await future
File "C:\source\IlionxCloud\iliona-store-packages\venv\lib\site-packages\anyio\_backends\_asyncio.py", line 743, in run
result = func(*args)
File "C:/source/IlionxCloud/iliona-store-packages/store-packages/main.py", line 49, in process_results
results = packageClient.query_packages_storage("PartitionKey eq 'app'")
File "C:\source\IlionxCloud\iliona-store-packages\store-packages\client\AzureTableEntityQueryClient.py", line 39, in query_packages_storage
for entity in entities:
TypeError: 'AbbreviatedPackage' object is not iterable
Upvotes: 4
Views: 21170
Reputation: 3121
Your AbbreviatedPackage
object can't be converted to JSON automatically. When you run json.dumps
with your list of AbbreviatedPackage
objects they shouldn't be serializable by default, throwing this error (as each object is trying to be iterated over and does not have an __iter__
method).
A few options:
.__dict__
method for your object when appending to your list. I personally don't like this solution, as it's uncontrolled.__iter__
, __str__
, and __repr__
methods to properly serialize to JSON along with a CustomEncoder
for the json.dumps
cls
attribute:# https://changsin.medium.com/how-to-serialize-a-class-object-to-json-in-python-849697a0cd3#8273
class Label:
def __init__(self, label, x, y, width, height):
self.label = label
self.x = x
self.y = y
self.width = width
self.height = height
def __iter__(self):
yield {
"label": self.label,
"x": self.x,
"y": self.y,
"width": self.width,
"height": self.height
}
def __str__(self):
return json.dumps(self, ensure_ascii=False, cls=CustomEncoder)
def __repr__(self):
return self.__str__()
Reference: https://changsin.medium.com/how-to-serialize-a-class-object-to-json-in-python-849697a0cd3#8273
And the CustomEncoder
class:
import json
class CustomEncoder(json.JSONEncoder):
def default(
self,
o,
):
"""
A custom default encoder.
In reality this should work for nearly any iterable.
"""
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, o)
I did a full synthesis on this with code:
Upvotes: 7