Django Rest Framework Form

2019-02-28 02:14发布

问题:

What I want to do:

Django Rest Framework comes with a renderer for returning a HTML form from a serializer[1]. After poring through the docs and then through the code, I still cannot figure out how to get it to render a blank form.

What I think the problem might be:

I can't figure out how to instantiate a blank serializer. In the docs for DRF they say the serializer has a very similar API to django's forms. When you want a blank form in a template, you instantiate one with MyForm() and pass it to the template context. However when I try that with a serializer, the .data attribute is just {'detail': 'Not Found.'}

Please help

I feel like I must be missing something blindingly obvious because rendering a blank form seems like a pretty major use case for an HTMLFormRenderer.

Some Source Code

class CustomViewSet(ModelViewSet):
    lookup_field = 'id'
    pagination_class = MyCustomPagination
    serializer_class = MyModelSerializer
    renderer_classes = (JSONRenderer, BrowsableAPIRenderer, HTMLFormRenderer)

    def create(self, request, *args, **kwargs):
        # return an html form for GET requests to /api/my_model/create/.form

I'm pretty sure I have the urlconf wired up right that the request ends up in the right place, because when I GET /api/my_model/<pk>/.form I get a form in response, with the model attributes pre-entered. I just want a way to get a blank form...

回答1:

I can't figure out how to instantiate a blank serializer.

DRF allows you to initialize a serializer with no data and no instance attached to it. So in your case, your view code would just look like

serializer = self.get_serializer()

I feel like I must be missing something blindingly obvious because rendering a blank form seems like a pretty major use case for an HTMLFormRenderer.

From that point, serializer should contain all of the information required to render a form. If you want to stick with the default forms as they are rendered by Django REST framework, you can use the HTMLFormRenderer that they provide.

serializer = self.get_serializer()
renderer = HTMLFormRenderer()
form_html = renderer.render(serializer.data)

It's important to note that the HTMLFormRenderer is not subject to standard deprecation policies. So if you do actually use it, make sure to follow any release notes closely when upgrading to make sure that things don't unexpectedly break.



回答2:

I managed to return an html snippet for a form using the following method on my viewset:

class CustomViewSet(ModelViewSet):

    @list_route()
    def form(self, request, *args, **kwargs):
        serializer = self.get_serializer()
        renderer = HTMLFormRenderer()
        form_html = renderer.render(serializer.data, renderer_context={
            'template': 'rest_framework/api_form.html',
            'request': request
        })
        return HttpResponse(form_html)

I needed to use a vanilla django HttpResponse, rather than rest framework's Response object, to prevent the Response object trying to re-render the form_html with the viewset's default renderer. Also, the list_route decorator is needed to get rest_framework's DefaultRouter to add a my_model/form/ endpoint, it doesn't mean the view has to return a list.



回答3:

A serializer must declare at least one field in order to work with it.

To create a absolutely blank serializer just add a custom SerializerMethodField that returns None or a hidden field that accepts null values:

class EmptySerialiser(serializers.Serializer):
    empty = serializers.SerializerMethodField()

    def get_empty(self):
        return None

or

class EmptySerialiser(serializers.Serializer):
    empty = serializers.HiddenField(default=None,allow_null=True)