Hide a field in GET but not POST

6,670 views
Skip to first unread message

Scott White

unread,
Feb 19, 2013, 10:43:40 AM2/19/13
to django-res...@googlegroups.com
Hi,

I have a ListCreateAPIView based on a model with a FileField. The issue is that I need the file field available for POST requests, but don't necessarily want it displayed in the GET results. I don't want to exclude the field in the serializer since I need it for the POST. I tried overriding get_field like this:

    def get_field(self, model_field):
        if model_field.name == 'sample_file' and self.context['request'].method == 'GET':
            return None
        else:
            return super(SampleSerializer, self).get_field(model_field=model_field)

but I get a KeyError since 'sample_file' is still the Meta option 'fields' . I guess I'm looking for the POST equivalent to the Meta option 'read_only_fields', something like 'write_only_fields' but that isn't an option.

Any ideas?

Thanks,
Scott

Tom Christie

unread,
Feb 20, 2013, 3:33:31 AM2/20/13
to django-res...@googlegroups.com
You probably want to override `get_serializer_class` on the view, and return a different serializer for `POST` and `GET` requests...

Jim Newsome

unread,
Feb 20, 2013, 8:54:19 AM2/20/13
to django-res...@googlegroups.com
That's an interesting solution, but how can you tell from inside `get_serializer_class` whether it's a `GET` or `POST` request?

I've been struggling a bit with a similar issue. I'm implementing some basic `User` management, and want the password to be write-only (and to ensure it gets hashed when it is set to a new value). For the moment I ended up just leaving `password` out of the serializer and creating RPC style views for user creation and password update.

--
You received this message because you are subscribed to the Google Groups "django-rest-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-fram...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Tom Christie

unread,
Feb 20, 2013, 9:04:13 AM2/20/13
to django-res...@googlegroups.com
> but how can you tell from inside `get_serializer_class` whether it's a `GET` or `POST` request?

Inside class based views you always have the request available as `self.request`, so you can just do `self.request.method`.

Scott White

unread,
Feb 20, 2013, 10:38:46 AM2/20/13
to django-res...@googlegroups.com
Tom,

Thanks, this works. I had also tried overriding 'post' in the ListCreateAPIView, but it seemed like the wrong approach. This is much simpler.

One minor issue I see is that the browsable HTML page doesn't show the file field so posting from the web no longer works. This isn't a deal breaker for me in this case, but others may want to be aware of it.

Scott White

unread,
Feb 20, 2013, 11:58:17 AM2/20/13
to django-res...@googlegroups.com
Tom,

I was curious about the web view issue, so I poked around a bit. After debugging, I noticed my overridden 'get_serializer_class' was getting called twice, which makes sense for the ListCreate view. Once to to know how to display the list, and again to know how to display the form. I thought I may be able to determine which scenario was calling the method, but only self instance of the view is available inside the method. 

Tracing through the list scenario:
ListModelMixin list() gets the serializer

Tracing through the create scenario:
BrowsableAPIRenderer method get_form() call view.get_serializer, then calls serializer_to_form_fields(serializer), where the 'read_only' attribute is read for all the fields.

Just brainstorming, but what about these 2 options:

1) Create something like a 'write_only' attribute in the Field class. This would by more DRY for the serializers, though I'm not sure about the implementation. For example, it doesn't make sense to have read_only=True and write_only=True

2) For the GenericAPIView view, perhaps make an additional method like 'get_serializer_for_post'  and a class variable like 'model_post_serializer_class', that if None get_serializer_for_post would return the regular model_serializer_class. Then, in BrowsableAPIRender the get_form() method could call the new get_serializer_for_post.

And, btw, many thanks for an awesome framework.

-Scott

Henk Vos

unread,
Feb 24, 2013, 6:13:35 PM2/24/13
to django-res...@googlegroups.com
I find myself often in a situation where i need just a subset of fields in a list (GET) e.g. a data grid or select box where there is no need to put all the model data on the wire.
I solved this like in the example below. Not sure if this the best way to do it, but it works.
I define an attribute list_fields on the serializer class and subclass the ListCreateAPIView and override the get method. Then if list_fields is defined it will use that attrib for serialization =>


class AssignmentSerializer(serializers.ModelSerializer):
    list_fields = ('id', 'subject', 'status')

    class Meta:
        model = Assignment
        fields = ('id', 'subject', 'due_date', 'status', 'person', 'category', 'description', 'client_input')


class RapassoListCreateAPIView(ListCreateAPIView):
    def get(self, request, *args, **kwargs):
        serializer = self.serializer_class
        serializer.Meta.fields = getattr(serializer, 'list_fields', None) or serializer.Meta.fields

        return self.list(request, *args, **kwargs)


class AssignmentList(RapassoListCreateAPIView):
    model = Assignment
    serializer_class = AssignmentSerializer

Marc Aymerich

unread,
Feb 26, 2013, 10:57:05 AM2/26/13
to django-res...@googlegroups.com
On Wed, Feb 20, 2013 at 5:58 PM, Scott White <whi...@gmail.com> wrote:
Tom,

I was curious about the web view issue, so I poked around a bit. After debugging, I noticed my overridden 'get_serializer_class' was getting called twice, which makes sense for the ListCreate view. Once to to know how to display the list, and again to know how to display the form. I thought I may be able to determine which scenario was calling the method, but only self instance of the view is available inside the method. 

Tracing through the list scenario:
ListModelMixin list() gets the serializer

Tracing through the create scenario:
BrowsableAPIRenderer method get_form() call view.get_serializer, then calls serializer_to_form_fields(serializer), where the 'read_only' attribute is read for all the fields.

I was trying to do the same, and acctually I think there is a way to distinct between the form and the list because at first look it seems that when get_serializer_class is called from list() self has an additional attribute: 'response', so you can do something like (warning hack downthere):

    def get_serializer_class(self):
        if self.request.method == 'GET' and not hasattr(self, 'response'):

br
--
Marc

Philip Neustrom

unread,
Jan 27, 2014, 6:12:57 PM1/27/14
to django-res...@googlegroups.com
For people looking to limit fields per-request using a query parameter, check out my snippet here:

https://gist.github.com/philipn/8659192

Chiyuan Goh

unread,
Sep 2, 2014, 2:40:38 AM9/2/14
to django-res...@googlegroups.com
write_only_fields is now supported.
Reply all
Reply to author
Forward
0 new messages