3

I am using Django + TastyPie + Backbone.js. I am trying to figure out how to bootstrap the initial data for the models on the first request, instead of fetching them after the initial page load as recommended here: http://backbonejs.org/#FAQ-bootstrap

There are two issues--one is trying to load a single Django model and the other is trying to serialize a queryset.

As an example, I have a variable called user in the context which is a Django User and represents the current user who is logged in.

I do not want to do this:

var curUser = new App.Models.User({id: 1});
curUser.fetch();

and cause another request to the server, since I already have the user model loaded.

I want to boostrap this data into a Backbone model like:

var curUser = new App.Models.User({{user|json}});

(similar to How to load bootstrapped models of backbone in Django, but I do not want to do a special case on each view converting everything to json)

where I have created a custom template filter to convert to json

def json(object):
    """Return json string for object or queryset.    
    """
    if isinstance(object, QuerySet):
        return mark_safe(serialize('json', object))
    if isinstance(object, Model):
        object = object.to_dict()
    return mark_safe(simplejson.dumps(object))

register.filter('json', json)

The issue is if I seralize a django model, I get something that looks like this:

[{"pk": 1, "model": "auth.user", "fields": {"username": "user@gmail.com", "first_name": "Jeremy", "last_name": "Keeshin", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2013-07-15T22:31:02", "groups": [], "user_permissions": [], "password": "passwordhash", "email": "user@gmail.com", "date_joined": "2012-06-14T00:59:18"}}]'

What I really want is the json representation to match up with the api that I've defined using TastyPie:

class UserResource(ModelResource):

    """A resource for the User model."""

    class Meta:
        queryset = User.objects.all()
        resource_name = 'user'
        authorization = Authorization()
        fields = ['username', 'first_name', 'last_name', 'id']
        filtering = {
            'username': ALL,
            'email': ALL,
            'first_name': ALL,
            'last_name': ALL
        }

Here I only get a few fields passed back, not all of them when I serialize the model. I know Django lets you serialize only certain fields, but those fields are set on a per model basis, and I wouldn't want to include that call on every view.

I see a similar answer here Django Serialize Queryset to JSON to construct RESTful response with only field information and id, but this requires writing this call on every view.

My current solution is adding a method to_dict monkeypatched onto the User model

def to_dict(self):
    """Return a subset of fields as a dictionary."""
    return {
        'id': self.id,
        'first_name': self.first_name,
        'last_name': self.last_name,
        'email': self.email
    }

because this is easy to serialize. Additionally a django model cannot be serialized (not in a list of one) by itself, only querysets can be serialized.

I imagine lots of people are figuring out good ways to bootstrap their django model data into backbone models upon the initial page load (especially when working with TastyPie), but I haven't figured out a reasonable way to do this.

With querysets, there is additionally much more django info passed along, and I am trying to figure out a way to have the serialization match the output from the TastyPie api.

Does anyone have best practices to boostrap django models and querysets into backbone models?

Update:

I have changed my json filter to use TastyPie bundles like

from core.api import UserResource
from core.api import UserProfileResource
from tastypie.serializers import Serializer


def json(object):
    """Return json string for object or queryset."""

    TYPE_TO_RESOURCE = {
        'User': UserResource,
        'UserProfile': UserProfileResource
    }

    Resource = TYPE_TO_RESOURCE[object.__class__.__name__]

    r = Resource()
    bundle = r.build_bundle(object)
    r.full_dehydrate(bundle)
    s = Serializer()
    return mark_safe(s.serialize(bundle, 'application/json'))

Source: http://django-tastypie.readthedocs.org/en/latest/cookbook.html#using-your-resource-in-regular-views

This seems to be closer, work with single models, stay DRY with which fields I want that I have listed in TastyPie, but does not handle multiple models yet. I'm also not sure if it is problematic to have this in a filter.

Update

Also use this: https://gist.github.com/1568294/4d4007edfd98ef2536db3e02c1552fd59f059ad8

def json(object):
    """Return json string for object or queryset."""

    TYPE_TO_RESOURCE = {
        'User': UserResource,
        'UserProfile': UserProfileResource,
    }

    if isinstance(object, QuerySet):
        Resource = TYPE_TO_RESOURCE[object[0].__class__.__name__]
        r = Resource()

        bundles = [r.build_bundle(model) for model in object]
        bundle = [r.full_dehydrate(b) for b in bundles]

    elif isinstance(object, Model):
        Resource = TYPE_TO_RESOURCE[object.__class__.__name__]
        r = Resource()
        bundle = r.build_bundle(object)
        r.full_dehydrate(bundle)
    else:
        mark_safe(simplejson.dumps(object))

    s = Serializer()
    return mark_safe(s.serialize(bundle, 'application/json'))
Community
  • 1
  • 1
jkeesh
  • 3,289
  • 3
  • 29
  • 42

0 Answers0