views:

88

answers:

3

I am trying to implement paging across ajax calls. The page should not refresh when the user wants to see the next x num of results.

Here is my problem. Returning the QuerySet is super simple. I just do (sumaJson is custom)

data = serializers.serialize('sumaJson', result_page.object_list, relations=('first_major', 'country_of_origin', 'second_major'))
return HttpResponse(data, mimetype="application/json") 

Now I also want to return things like

result_page.has_previous()
result_page.has_next()
result_page.paginator.count

and so on. I for the life of me can't figure out how to get both across in one response. I can't add this info to result_page.object_list because then the serializer fails. If I something of the sort of

simplejson.dumps(paging_info + result_page.object_list)

Then in the javascript the QuerySet is no longer a list of objects but just a big string of characters which can't be interpreted with

$.each(data.data, function(index, item){

I tried some bad hacks like creating a fake object and putting it in the object_list, serializing this and then deleting the object. This allows me to get the data across. However, I don't want to be creating and deleting fake objects.

I don't want to meddle with the serializer. I don't want to send a second ajax request once I get the querySet back to get the paging info.

Am I missing something? Is there an easy way to get both across in one response? Thanks!

+1  A: 

What I've done in the past for ajax calls is I return the json as the HttpResponse, like you're doing, and add a header for any additional fields that I want to return.

data = serializers.serialize('sumaJson', result_page.object_list, relations=('first_major', 'country_of_origin', 'second_major'))
response = HttpResponse(data, mimetype="application/json") 
response['X-VALUE'] = 'asdf' #this is the header, you can create as many of these as you'd like
return response

and on the javascript side...

$.ajax({
  url: '/whatever/here/',
  success: function(data, code, xhr) {
    alert(xhr.getResponseHeader('X-VALUE'));
  }
});

hope this helps.

Matthew J Morrison
This might not be the best idea at all, please see this answer on an other question. HTTP headers aren't guaranteed to be delivered under all circumstance. http://stackoverflow.com/questions/3326210/can-http-headers-be-too-big-for-browsers/3431476#3431476
HoLyVieR
That's disappointing, I was about to say that this was an ingenious idea. Back to square one...
Dpetters
+2  A: 

When I'm serializing a collection of objects, I typically include the pagination information in the response body itself. If I had 50 objects that I wanted to serve up 10 per page, the JSON would look something like this:

{
    "count": 50,
    "objects": [
        {
            ...
        }
    ],
    "pages": {
        "count": 5,
        "current": "http://api.example.com/objects/?page=3",
        "first": "http://api.example.com/objects/",
        "last": "http://api.example.com/objects/?page=5",
        "next": "http://api.example.com/objects/?page=4",
        "previous": "http://api.example.com/objects/?page=2"
    }
}
jpwatts
I would love to do something of the sort, but I'm just not sure how to get to this structure. The serializer can only take QuerySets and lists of objects not dictionaries. I can do this with simplejson.dumps but then I lose the ability to do the deep serialization that I need.
Dpetters
I use `simplejson.dumps` with a custom JSON encoder (`dumps` takes an optional `cls` keyword argument) that takes care of encoding my model instances.
jpwatts
I know that a popular deep serializer is provided by wadofstuff, but it only works on querysets. Any suggestions for a good JSON encoder that does deep serialization of models?
Dpetters
I don't usually want to expose all fields (or necessarily limit myself to just model fields), so I write a custom function for each resource. One approach I've used to simplify things is to define a `dump` method in an abstract base class that is then extended by each model I create. My custom JSON encoder class then tries to call each object's `dump` function, if it exists.
jpwatts
Right, this makes perfect sense. I ended up doing just that and it does indeed work better than using anything that anyone's already built because I'm no forced to pass every single bit of information. Thanks for you help!
Dpetters
+1  A: 

simplejson.dumps() can serialize dictionaries deeply, even recursively.

There are two approaches you can take to this. The first is to use the Django ORM's values() method on QuerySets: it churns out pure python dictionaries with object IDs instead of references, suitable for serializing.

If you need even deeper than that, you may have to write something to create the dictionary structure proposed by jpwatts. If you need that kind of power, I have a post on my personal blog about adding functors, generators, iterators, and closures to simplejson. The example demonstrates how to turn a Treebeard tree structure into a javascript object.

The code is:

from django.utils.simplejson.encoder import JSONEncoder

class ExtJsonEncoder(JSONEncoder):
    def default(self, c):
        # Handles generators and iterators
        if hasattr(c, '__iter__'):
            return [i for i in c]

        # Handles closures and functors
        if hasattr(c, '__call__'):
            return c()

        return JSONEncoder.default(self, c)

jpwatts has the correct approach. You may just have to write some code to get there.

Elf Sternberg
this approach worked. the only painful part is building up the structure of the json object myself. My model has a ton of fields.
Dpetters
Is there some way you can abstract the fields and handle them as generic kinds of data? That might make the workload easier. When confronted with a big data collection like this, I write a few one- or two-line inner handling functions and a dispatch table, then iterate over the fieldnames with getattr(), mapping the field to its handler. It's a useful way to keep each conceptual unit ("how to convert" vs. "what to convert") within one IDE pane.
Elf Sternberg