Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Different fields for SHOW and INDEX actions #18

Open
bmihelac opened this issue Oct 25, 2010 · 40 comments
Open

Different fields for SHOW and INDEX actions #18

bmihelac opened this issue Oct 25, 2010 · 40 comments

Comments

@bmihelac
Copy link

Hi all, is it possible to display different fields for resource depending of action.

The reason I want it is to avoid burdening of index action with all has_many fields that resource includes.

For example in Author index view I would like to return only his name, but for show action I would like to include all books resources.

thanks,
Bojan

@wlt008
Copy link

wlt008 commented Nov 14, 2010

+1
I am deeply hoping for this feature: "detail field" and "list field" can be separated

@onyxfish
Copy link
Contributor

+1 (via duplicate at #44)

@bmihelac
Copy link
Author

It seems to me that there are two things which could make easier to get subset of data in index actions:

  1. Make full_dehydrate use different fields for index and get methods. Maybe something like:

    Meta:
    index_exclude_fields = ['some_m2m_field']

This would allow us to save some db query.

  1. Pass additional 'method' parameter to dehydrate. This would allow customization of bundle like to skip some fields which are not needed in index and we want to spare some bandwidth.

I didnt evaluate 1. would have impact on caching.
What do you think?

@toastdriven
Copy link
Contributor

Also duped in #48, but each issue has worthwhile aspects.

I wanted to push off this feature if I could, but I'm way outgunned. It'll need to be there for better file support anyhow, so it has to get addressed. Will target 1.0 on this.

@onyxfish
Copy link
Contributor

onyxfish commented Jan 8, 2011

After examining my use-case for this a little more, I'd like to suggest it be implemented as something more flexible than just show/list views. What I really need to be able to do is take a querystring flag like this:

&shape=[full|simple|none]

and have the output reflect the user's selected level-of-detail. I looked for a way to hack this in, but as the fields are included/excluded when the ModelResource class is created, I could not come up with any way to alter what was available later in the response cycle. Moreover, the request object is not available at the steps in the process where I would want to make such a determination.

@gstf
Copy link

gstf commented May 6, 2011

+1

@AndrewIngram
Copy link

I'll mention this thought here first as it seems relevant to this issue, but it might need to be spun off into its own.

If a mechanism for showing different fields is developed, it would be useful if it could be integrated with authentication too. Being able to expose more/less fields depends on the user's permissions would be very powerful. If this is already possible, I haven't been able to find any mention of it.

@aldarund
Copy link

+1

5 similar comments
@vhbit
Copy link

vhbit commented Jul 12, 2011

+1

@berggren
Copy link

+1

@petergx
Copy link

petergx commented Aug 22, 2011

+1

@stuartkeith
Copy link

+1

@adilatilgan
Copy link

+1

@dolph
Copy link

dolph commented Oct 2, 2011

+1

I'd expect to be able to control the fields displayed (using either fields or excludes) on a per method (POST, PUT, GET) in addition to list/detail.

@daviddul
Copy link

Here's a workaround, see the example below: you don't exclude anything from the original model's resource model (UserResource in the example) so it'd be full in it's index view. You have to go into the dehydrate method of the modelresource that includes your sub model (the BlogPostResource includes the author in it) and just delete the elements of the bundle.

example:

class BlogPostResource(ModelResource):
     author = fields.ForeignKey(UserResource, 'author', full=True)
     ....
     class Meta:
         ...

def dehydrate(self, bundle):
         del bundle.data['author'].data['field_you_dont_wanna_show_here']
         del bundle.data['author'].data['field_you_dont_wanna_show_here']
         return bundle

So when you want to list the users, you still get all the fields but when you list blogposts, you can for example just get the author's first name and last name.

What do you think?

@ashwoods
Copy link

Example of a very, very dirty workaround: Doesnt show project fields in list_view, but it does in detail_view, without having to access the resource itself, getting the url itself without hardcoding it should be posible but haven't had time to check it out:

class CompanyResource(ModelResource):
       """
       Tastypie resource for Company
      """
       projects = fields.ToManyField('api.resources.ProjectResource',
                                  'projects',full=True)
       class Meta:
           queryset = Company.objects.all()
           resource_name = 'companies'

       def dehydrate(self, bundle):
           if bundle.request.path == "/api/v1/companies/":
               del bundle.data['projects']
           return bundle

@daveyss
Copy link

daveyss commented Nov 13, 2011

+1

2 similar comments
@ghost
Copy link

ghost commented Jan 5, 2012

+1

@pavlo-v-chernykh
Copy link

+1

@dericcrago
Copy link
Contributor

I'll also hop on board the it'd be nice to have train, but what @ashwoods pointed out is enough to get me by. I've modified it slightly to be a bit more dynamic.

    def dehydrate(self, bundle):
        if self.get_resource_uri(bundle) == bundle.request.path:
            print "Detail"

        if self.get_resource_uri(bundle) != bundle.request.path:
            print "Not Detail - Could be list or reverse relationship."

        return bundle

@dericcrago
Copy link
Contributor

So I gave this some more thought and came up with something that comes pretty close to letting me do what I think @bmihelac was looking for.

Using the example @ashwoods provided, suppose we only wanted to display the projects field if it was a detail response:

class CompanyResource(ModelResource):
    """
    Tastypie resource for Company
    """

    class Meta:
        queryset = Company.objects.all()
        resource_name = 'companies'
        additional_detail_fields = {'projects': fields.ToManyField('api.resources.ProjectResource', 'projects',full=True)}

    def dehydrate(self, bundle):
        # detect if detail
        if self.get_resource_uri(bundle) == bundle.request.path:
            # detail detected, include additional fields
            bundle = self.detail_dehydrate(bundle)

        return bundle

    # detail_dehydrate is basically full_dehydrate
    # except we'll loop over the additional_detail_fields
    # and we won't want to do the dehydrate(bundle) at the end
    def detail_dehydrate(self, bundle):
        """
        Given a bundle with an object instance, extract the information from it
        to populate the resource.
        """
        # Dehydrate each field.
        # loop over additional_detail_fields instead
        #for field_name, field_object in self.fields.items():
        for field_name, field_object in self._meta.additional_detail_fields.items():
            # A touch leaky but it makes URI resolution work.
            if getattr(field_object, 'dehydrated_type', None) == 'related':
                field_object.api_name = self._meta.api_name
                field_object.resource_name = self._meta.resource_name

            bundle.data[field_name] = field_object.dehydrate(bundle)

            # Check for an optional method to do further dehydration.
            method = getattr(self, "dehydrate_%s" % field_name, None)

            if method:
                bundle.data[field_name] = method(bundle)

        # dehydrating the bundle will create an infinite loop
        #bundle = self.dehydrate(bundle)
        return bundle

@pix0r
Copy link

pix0r commented Mar 26, 2012

+1, using fix from @dericcrago for now.

@swiharta
Copy link

+1

1 similar comment
@aaloy
Copy link

aaloy commented May 16, 2012

+1

@toastdriven
Copy link
Contributor

Partial implementation in #526, not sure I'm sold on all of it & it lacks tests/docs.

@funkybob
Copy link

Just saw this ticket... and also like the 'shape' approach mentioned by onyxfish above...

Was thinking my solution in #526 was a little limited, in case people wanted different 'shapes' in other cases...

to the suggestions to remove fields after dehydrating ... my whole reason is to avoid computing the values in the first place.

However, the idea for detail_dehydrate hook to allow conditionally adding more details, I like.

@dhatch
Copy link
Contributor

dhatch commented Jul 17, 2012

Looks like two possible implementations both including tests and docs are available. I wrote one in #569 and #538 also performs similar functionality (#538 allowing a bit more flexibility since use_in may be a callable). My implementation adds meta attributes to control this functionality (which is consistent with the current fields attribute) while #538 adds an attribute to fields. Both seem valid, just a design decision as to which way to go. Adding to the meta seems consistent to me and is easier to use given that some fields can be automatically generated and modifying their initialization parameters may not be possible. Another alternative would be to combine both pull requests and allow for the use_in parameter to be automatically set based on the meta attribute, however this seems to add more complexity to the API than is necessary. Thanks to @issackelly for pointing out related pull requests to me.

@funkybob
Copy link

[chiming in as I was the original cause behind #538, it being a cleanup of my #526]
Makes a lot of sense... the Meta approach would, indeed, gel with the excludes list for ModelResource, etc...

As I said in another ticket, a "simple" solution such as this would be, IMHO, sufficient for a 1.0 release... with a more complex solution like "client selectable 'shapes' " being perhaps desirable for a later release...

@dhatch
Copy link
Contributor

dhatch commented Jul 17, 2012

@funkybob Agreed, certainly the more complex solution would be helpful from a client side, but it would be nice to have this functionality included asap so it can begin being used before 1.0 is released.

@numan
Copy link
Contributor

numan commented Jul 19, 2012

Actually using the PR in a production application, I really like the flexibility of the callback provided by #538. I have a couple of use cases where I have to hide a resource at runtime based on permission.

This wouldn't be possible for me using #569

@glibersat
Copy link

Hi, any news? That PR makes life so easier, thanks!

@djedi
Copy link

djedi commented Dec 3, 2012

Going with hack provided by @dericcrago

@giovannic
Copy link

+1

@snanda85
Copy link

A simple workaround that I am using is to override the get_detail & get_listmethods to edit such fields.
This saves the overhead of actually fetching the data for the field and then deleting it from the bundle, but i am not sure if this method is threadsafe, as it looks like that the Resource objects are not created on every api call.
It'll be great if somebody can comment on this.

Here is the code:

class ArticleResource(BaseModelResource):
    owner = fields.ToOneField(UserResource, 'owner', full=True)

    class Meta:
        resource_name = "articles"

    def get_list(self, request, **kwargs):
        self.fields.pop("comments", None)
        return super(ArticleResource, self).get_list(request, **kwargs)

    def get_detail(self, request, **kwargs):
        self.fields["comments"] = fields.ToManyField(CommentResource, 'comments', full=True)
        return super(ArticleResource, self).get_detail(request, **kwargs)

@vchakoshy
Copy link

+1

@dnozay
Copy link

dnozay commented Apr 30, 2013

another workaround is to have different resources for detail and list views:

from tastypie.resources import ModelResource
from django.contrib.auth.models import User

# detail will show everything except password
class UserResourceDetail(ModelResource):
    class Meta:
        queryset = User.objects.all()
    excludes = ('password',)
    resource_name = 'user'

# list will only show username & date_joined (and exclude password)
class UserResource(UserResourceDetail):
    class Meta(UserResourceDetail.Meta):
        fields = ('username', 'date_joined')
    get_detail = UserResourceDetail().get_detail

# ... register & use UserResource

@amineck
Copy link

amineck commented Aug 1, 2013

+1

@nkeilar
Copy link

nkeilar commented Sep 29, 2013

+1 for @dnozay workaround

@mindcruzer
Copy link

+1 @dnozay, awesome

Note that if you want get_resource_uri to work correctly with the user detail view, you'll need to add the following after UserResource is defined.

UserResourceDetail.get_resource_uri = UserResource().get_resource_uri

Otherwise resource_uri will be empty in all detail responses.

@SeanHayes
Copy link
Member

Possibly related: #1265

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests