manuel
manuel

Reputation: 847

How can I create a custom nested ToMany resource in tastypie?

I have a slightly complex model structure in django that includes a UserProfile

class UserProfile(models.Model):
    shoppinglist = models.ManyToManyField(Offer)
    user         = models.OneToOneField(User)
    follows = models.ManyToManyField('self', related_name='followers', symmetrical=False, blank=True)

and an offer

class Offer(models.Model):
    description = models.CharField(max_length=200)

What this means is that each user can follow other users and can create a shopping list with offers they like.

I can create a query to see if an offer is in the shopping list of any of the people I follow like this

profile.follows.filter(shoppinglist=offer)

and it returns a queryset of UserProfile objects.

Now, I have and OfferResource and a UserProfileResource in my tastypie API representing this two models.

What I need to do is, whenever I get a list of Offers, add a custom field to each Offer with the list of people I follow that 'like' that offer.

offer: {
    description: 'Something'
    liked_by: [
        { ... },
        { ... }
    ]
}

I know I can override the 'dehydrate' method to add custom fields but if I add

def dehydrate(self, bundle):
    bundle.data['liked_by'] = profile.follows.filter(shoppinglist=bundle.obj)

The resulting list of users in the liked_by field is not serialised or dehydrated.

Any Ideas?

Upvotes: 1

Views: 693

Answers (3)

Blake
Blake

Reputation: 491

You can "embed" a bundle by using a lambda function for the "attribute" argument of a ToManyField on your OfferResource. For example:

liked_by = fields.ToManyField(
    UserProfileResource,
    attribute=lambda bundle: bundle.request.user.get_profile().follows.filter(
        shoppinglist=bundle.obj),
    full=True,
    null=True)

A little bit of documentation on that.

Upvotes: 5

dokkaebi
dokkaebi

Reputation: 9190

You can use the UserProfileResource to dehydrate a list of objects:

class OfferResource(ModelResource):

    ...
    users = fields.ToManyField(UserProfileResource, attribute='users', full=True)

    ...
    def dehydrate_users(self, bundle):
        res = UserProfileResource()
        objects = profile.follows.filter(shoppinglist=bundle.obj)
        bundles = [res.build_bundle(obj=obj, request=bundle.request) for obj in objects]
        return [res.full_dehydrate(bundle) for bundle in bundles]

It's also possible to provide a callable for the attribute kwarg, but it isn't clear what all you need to be able to look up your profile object. If you can get it within that scope, then you just need to return a queryset:

class OfferResource(ModelResource):

    ...
    users = fields.ToManyField(UserProfileResource, 
        attribute=lambda bundle: get_profile(bundle.request).follows.filter(shoppinglist=bundle.obj),
        full=True)

Upvotes: 2

tonino.j
tonino.j

Reputation: 3871

To me, it seems like you will probably need to read tastypie docs and figure out what it is that you need to extend. Because my guess is that you will need to extend something there. Not in the model, but in tastypie (serializers, fields). IT is hard to answer because your intent is still not 100% clear.

Also, since you seem to want to aggregate more models, check out if this link helps you:

Upvotes: 0

Related Questions