Cannot read table row via HTTP API

Hi,

I have been trying to create an API which can read the contents of a specific row from a table and return it as a JSON object but am having no luck.

My services table data looks like that shown below:

trigger number (number) description (text) connecttime (number)
1000 Sales 10
1234 Support 20
2222 Finance 12

My Server Module code is below.

import anvil.users
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.server

@anvil.server.http_endpoint(’/services/:id’)
def get_service_details(id):
service_row = app_tables.services.get(triggernumber=int(id))
return service_row

If I call this in Postman with a url ā€˜/services/1000’ I get the response below:

Cannot send a LiveObjectProxy object over HTTP as response.
You must return either Media, a string, or a JSON-compatible object (lists/dicts/strings/numbers/None).

If I change the last line to return str(service_row) I get the response below:

<LiveObject: anvil.tables.Row>

Can anyone tell me how I can get a response in JSON format containing the contents of the row from the table?

The HTTP API typically returns data as either binary (Media) or JSON. The JSON encoder cannot encode arbitrary types which is why the error response recommends returning one of the built-in types.
In this case a single row would translate to a dictionary. The easiest way to convert a LiveObjectProxy to a dictionary is to use the dict function, i.e. return dict(service_row) which would give you all the fields in the row.
For public APIs, it is a good idea to only return necessary data and also to be explicit in what is returned, so you could also construct a dictionary object manually with just the fields required, e.g.

return {
    'description': service_row['description'],
    'time': service_row['connecttime'] 
}
1 Like

Thank you for the response which has been really helpful :smiley:

Using your example to pick specific fields I am able to get a dictionary containing the key/value pairs for the following

  • triggernumber (this is a number type in the table)
  • description (type text)
  • connecttime (type number)

The table has another column - servicemode - which contains text values obtained via a link to another table

When I try to add this to the dictionary I get the same error I originally had:

Cannot send a LiveObjectProxy object over HTTP as response[ā€œservicemodeā€].
You must return either Media, a string, or a JSON-compatible object (lists/dicts/strings/numbers/None).

I have done some playing about and found that the linked table value is being retrieved as a LiveObjectProxy so have converted this to a dict using the code below which outputs what I need.

@anvil.server.http_endpoint(’/services/:id’)
def get_service_details(id):
service_row = app_tables.services.get(triggernumber=int(id))
servicemode_dict = dict(service_row[ā€˜servicemode’])
servicemode_value = servicemode_dict[ā€œmodeā€]
return {
ā€˜trigger’: service_row[ā€˜triggernumber’],
ā€˜servicemode’: servicemode_value,
ā€˜description’: service_row[ā€˜description’],
ā€˜connecttime’: service_row[ā€˜connecttime’]
}

Is this a reasonable way of getting the information or is there some easier way? - I would really like to be able to get away without manually constructing the dictionary object as this may eventually have 20+ key/value pairs.

If the table structure is going to grow and you want to keep your code generic, you might try implementing a recursive function to convert a LiveObjectProxy into a dictionary. Within the function you could iterate through each field and check the value using isinstance and return the dictionary from a recursive call to your function if the value is a LiveObjectProxy and the plain value otherwise.

I haven’t tried this yet, but it’s been in my.mind as a I’d like to add json serialisation to my orm…

Portable classes are automatically serialised for passing between the anvil server and client. If you return an instance of one from an http endpoint function, does that also ā€˜magically’ work?

I can see how that might work if the caller is an Anvil app with the same Portable Class definition.

But the caller could also be a non-Python program. What it might receive, in that case, is not clear to me, especially if the Portable Object contains an Anvil-supplied Capability.

Yep. Just tried it and no joy. Oh well, back to marshmallow!