1

New to python and Pyramid.

I have a view that does pagination. How do I json encode it? I get an error "object at 0x2d16d90> is not JSON serializable" when I try this:

@view_config(route_name="paginate")  
def paginate(request):
    query = DBSession.query(MyTable)
    page_url = paginate.PageURL_WebOb(request)
    customers = paginate.Page(query, 
                     page=int(request.params.get("page", 1)), 
                     items_per_page=25, 
                     url=page_url)

    if "partial" in request.params:
        # Render the partial list page
        return render_to_response("templates/my_json.jinja2",
                                  {"customers": json.dumps(customers)},
                                  request=request)
    else:
        # Render the full list page
        return render_to_response("templates/my.jinja2",
                                  {"customers": customers},
                                  request=request)

2 Answers2

2

JSON is a strict textual format. You cannot just throw SQLAlchemy at the encoder and expect this to Just Work, you need to decide what the encoding for your objects will look like.

One way is for your SQLAlchemy objects to implement an extra method that returns a simple python dictionary; one that can be serialized. That means it can only use strings for keys, and can only contain lists, tuples, numbers and / or strings.

An example method could be:

def json_dump(self):
    return dict(name=self.name, phone=[p.number for p in self.phonenumbers])

This returns a dictionary with name and phone keys, where the phone key contains a list of phone numbers from a (purely made up for example purposes) phonenumbers relationship.

You can then use that information to create a new list of dictionaries that json.dumps can handle:

if "partial" in request.params:
    customer_json_data = [c.json_dump() for c in customers]
    # Render the partial list page
    return render_to_response("templates/my_json.jinja2",
                              {"customers": json.dumps(customer_json_data)},
                              request=request)

The upcoming Pyramid 1.4 adds specific support for serializing custom objects in the json renderer via the __json__ method hook, as well as support for adapters to do the conversion for you.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • hmmm. Almost works, but the pagination data seems to get screwed up as customers.page, customers.next_page, and customers.last_page all get set to "end", even though there are more pages. The data returned from the db seems to be doing what I asked for, though –  Sep 22 '12 at 19:47
  • FTR, Pyramid 1.4a1+ allows you to define custom objects in such a way that you can use the built-in JSON renderer to serialize them: http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/narr/renderers.html#json-serializing-custom-objects – Chris McDonough Sep 22 '12 at 21:26
  • @ChrisMcDonough: Cool, that basically formalizes the method I advocated into a `__json__` method + framework support for invoking them. – Martijn Pieters Sep 22 '12 at 21:29
0

Are you sure you want to render a template to send your JSON response? I've done something more like this in the past:

return HttpResponse(
        json.dumps(customers),
        status=200,
        mimetype="application/json")

FYI: that's Django code above... I'm not sure what the Pylons equivalent is, but I'm looking it up...

John Szakmeister
  • 44,691
  • 9
  • 89
  • 79
  • This won't work either. The problem is that the OP is trying to send a bunch of SQLAlchemy response objects to `json.dumps` and that *doesn't work*. – Martijn Pieters Sep 22 '12 at 06:38