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:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesuvicornprotocolshttph11_impl.py", line 373, in run_asgi result = await app(self.scope, self.receive, self.send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesuvicornmiddlewareproxy_headers.py", line 75, in __call__ return await self.app(scope, receive, send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesfastapiapplications.py", line 208, in __call__ await super().__call__(scope, receive, send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletteapplications.py", line 112, in __call__ await self.middleware_stack(scope, receive, send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarlettemiddlewareerrors.py", line 181, in __call__ raise exc File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarlettemiddlewareerrors.py", line 159, in __call__ await self.app(scope, receive, _send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarlettemiddlewarecors.py", line 84, in __call__ await self.app(scope, receive, send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletteexceptions.py", line 82, in __call__ raise exc File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletteexceptions.py", line 71, in __call__ await self.app(scope, receive, sender) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletterouting.py", line 656, in __call__ await route.handle(scope, receive, send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletterouting.py", line 259, in handle await self.app(scope, receive, send) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletterouting.py", line 61, in app response = await func(request) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesfastapirouting.py", line 226, in app raw_response = await run_endpoint_function( File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesfastapirouting.py", line 161, in run_endpoint_function return await run_in_threadpool(dependant.call, **values) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesstarletteconcurrency.py", line 39, in run_in_threadpool return await anyio.to_thread.run_sync(func, *args) File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesanyioto_thread.py", line 28, in run_sync return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable, File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesanyio_backends_asyncio.py", line 805, in run_sync_in_worker_thread return await future File "C:sourceIlionxCloudiliona-store-packagesvenvlibsite-packagesanyio_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:sourceIlionxCloudiliona-store-packagesstore-packagesclientAzureTableEntityQueryClient.py", line 39, in query_packages_storage for entity in entities: TypeError: 'AbbreviatedPackage' object is not iterable
Advertisement
Answer
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:
- Use the
.__dict__
method for your object when appending to your list. I personally don’t like this solution, as it’s uncontrolled. - Write
__iter__
,__str__
, and__repr__
methods to properly serialize to JSON along with aCustomEncoder
for thejson.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: