Search code examples
djangotastypie

How to hydrate ToOneField and ToManyField in tastypie


I have some tastypie resources, and I'm dehydrating so I can return resources' slugs (names) in the results, but I'm having trouble re-hydrating them.

I tried setting, e.g., bundle.data['profile'] = { 'name': bundle.data['profile'] } but I couldn't get that to work, it looks as though it needs all of the data in order to build the object, not just some fields

In this example, hydrate_profile works, but it depends on bundle.obj.profile to be already loaded. I'm not sure I can always assume that.

In the case of dehydrate_roles, I don't know what should I return, returning a list of Models or a QuerySet doesn't work, it looks like the right thing to do (in both methods) would be to return the correct resource uri, in both cases, but I don't know how to build it without too much hardcoding: I'd need to retrieve the original Model classes from the fields (or at least from the Resource class), in order to obtain the object, in order to get it's PK and then build the uri.. sounds too far-fetched to me.

So: what should I be doing? Is my hydrate_profile implementation right, or will it come bite me in the butt? what should I return in hydrate_roles?

class MinionResource(ModelResource):
    """
    Resource for :models:`inventory.minions.models.Minion`
    """
    roles = fields.ToManyField(RoleResource, 'roles')
    profile = fields.ForeignKey(clouds_api.CloudProfileResource, 'profile')

    class Meta:
        queryset = models.Minion.objects.all()
        resource_name = 'minion/minion'
        filtering = {
            'id': ALL,
            'name': ALL,
            'roles': ALL_WITH_RELATIONS,
            'profile': ALL_WITH_RELATIONS,
        }

    def dehydrate_profile(self, bundle):
        return bundle.obj.profile.name
    def hydrate_profile(self, bundle):
        bundle.data['profile'] = bundle.obj.profile
        return bundle

    def dehydrate_roles(self, bundle):
        return list(bundle.obj.roles.all().values_list('name', flat=True))

Solution

  • I can't exactly answer what is right, but as far as I know, on Profile part, you will get problem when you create new object that doesn't have profile linked yet.

    However, what I regularly do is to only define if I want information or not.

    class MinionResource(ModelResource):
        roles = fields.ToManyField(RoleResource, 'roles', full=True)
        profile = fields.ForeignKey(clouds_api.CloudProfileResource, 'profile', full=True)
    

    This should be enough already. However, if you don't like to be this way, you can do this.

    class MinionResource(ModelResource):
        roles = fields.ListField()
        profile = fields.ForeignKey(clouds_api.CloudProfileResource, 'profile', full=True)
    
        def dehydrate_roles(self, bundle):
            return map(str, bundle.obj.roles.all())