Sunday, March 20, 2011

How to include extra data in the Django serializer response?

Hi, I am trying to make a livesearch with Django and jQuery. What I've done is make the javascript ask for some data with the getJSON function, then I've set up a view in Django that returns a JSON response automated by the Django serializer.

And this works well, it returns a json response with the text/javascript content-type. To avoid sending all the data, (alot that i dont need) i did this:

response.write(serializers.serialize("json", soknad_list, fields=('name', 'image', 'genre')))

But for instance, the 'genre' field is a manyToMany field, so is it possible to get the values from genre.all.0 for instance and not just the genre ID?

And the model has a function get_absolute _url, is it possible to include this in the JSON response, if so how?

So i guess my question is, is it possible to include stuff other than the raw field data in the JSON response, if not, how would you go about solving my problem?

From stackoverflow
  • Django's JSON serialization is based on simplejson, which you can use directly and extend at will to handle whatever kind of objects. So you mostly have two options here: either manually build a list of dicts with relevant data and pass it to simplejson.dumps() (which by default support strings, lists, dicts and numerics), or write your own json encoder that knows how to serialize your specific dataset. FWIW, here's a (not well tested but worked so far) Django model aware json encoder:

    from django.utils import simplejson
    from django.utils import datetime_safe
    from django.utils.functional import Promise
    from django.utils.translation import force_unicode
    from django.utils.encoding import smart_unicode
    from django.core.serializers.json import DjangoJSONEncoder
    
    class ModelJSONEncoder(DjangoJSONEncoder):
        """
        (simplejson) DjangoJSONEncoder subclass that knows how to encode fields.
    
        (adated from django.serializers, which, strangely, didn't
         factor out this part of the algorithm)
        """
        def handle_field(self, obj, field):
            return smart_unicode(getattr(obj, field.name), strings_only=True)
    
        def handle_fk_field(self, obj, field):
            related = getattr(obj, field.name)
            if related is not None:
                if field.rel.field_name == related._meta.pk.name:
                    # Related to remote object via primary key
                    related = related._get_pk_val()
                else:
                    # Related to remote object via other field
                    related = getattr(related, field.rel.field_name)
            return smart_unicode(related, strings_only=True)
    
        def handle_m2m_field(self, obj, field):
            if field.creates_table:
                return [
                    smart_unicode(related._get_pk_val(), strings_only=True)
                    for related
                    in getattr(obj, field.name).iterator()
                    ]
    
        def handle_model(self, obj):
            dic = {}
            for field in obj._meta.local_fields:
                if field.serialize:
                    if field.rel is None:
                        dic[field.name] = self.handle_field(obj, field)
                    else:
                        dic[field.name] = self.handle_fk_field(obj, field)
            for field in obj._meta.many_to_many:
                if field.serialize:
                    dic[field.name] = self.handle_m2m_field(obj, field)
            return dic
    
        def default(self, obj):
            if isinstance(o, Promise):
                return force_unicode(o)
    
            if isinstance(obj, Model):
                return self.handle_model(obj)
    
            return super(ModelJSONEncoder, self).default(obj)
    

    HTH

    Espen Christensen : Hi, thanks i will definitely try this one out, thanks alot! I'll get back to you if i succeed.
  • I found out that the simplest thing was to not use the serializer at all. I dont know why I didnt think of this before, but i just used a generic object list view and changed the mimetype to text/javascript and made a JSON template insted of a html template.

    Very simple, and that way i managed to get all the data i wanted into the JSON response. This way you can add everything that you can add to a html template into a JSON response, even paginating.

    Example extraction of the view i created:

    return object_list(request, queryset=object_list, template_name='search/results.js', template_object_name='result', paginate_by=12, mimetype='text/javascript')

    Michael : templating out json isn't a good idea, you definitely should use a json serialization library
  • I like the generic view solution, but, would you give to us more details.... thanks...

  • There is a handy django third party app / serializer that will allow you to include extra data. It also allows you to include model relations and exclude a list of fields.

    It's available at http://code.google.com/p/wadofstuff/wiki/DjangoFullSerializers

  • thanks @sebalt, that worked.

    @espen can you provide more details on the solution (urls & view & template)

    Bill the Lizard : Since this question was asked/answered back in January, Espen is unlikely to come back and answer you. If you're having a similar problem, you may want to post a separate question with the details and link to this one for reference.
    Espen Christensen : I am actually here, but I dont know what i can help with. It is just a standard django view that you will find out how to do in the Django docs. But insted of returning a text/html mimetype html template, you return a text/javascript mimetype .js document. You see how to return that in the answer above. And then you can find out how a json response should look like and just write a template that looks like that...
    izzy : Thanks figured it out. both solutions above work. Not sure why i was voted down?

0 comments:

Post a Comment