Show Filters and Ordering in Django Rest Framework

2019-02-22 06:21发布

问题:

I'm using the Django Rest Framework I noticed on the web browseable part of the API there is a button called 'options' when clicked it shows the following...

HTTP 200 OK Vary: Accept Content-Type: text/html Allow: HEAD, GET, OPTIONS
{
    "parses": [
        "application/json", 
        "application/x-www-form-urlencoded", 
        "multipart/form-data"
    ], 
    "renders": [
        "application/json", 
        "text/html"
    ], 
    "name": "Products", 
    "description": "API endpoint."
} 

my question is, is there anyway I could list out here all the filter options an other stuff for this url?

回答1:

You can make OPTIONS return whatever you want, by overriding the .metadata() method on the view.

See here: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L340


Update as of 2015: We now have a customizable metadata API that makes this easier: http://www.django-rest-framework.org/api-guide/metadata/



回答2:

You can totally do this. Here's a custom metadata class that I've been keeping up to date here on StackOverflow. This simply lists all the available filters, their types, and their choices. It also lists the ordering fields that are available on a class:

class SimpleMetadataWithFilters(SimpleMetadata):

    def determine_metadata(self, request, view):
        metadata = super(SimpleMetadataWithFilters, self).determine_metadata(request, view)
        filters = OrderedDict()
        if not hasattr(view, 'filter_class'):
            # This is the API Root, which is not filtered.
            return metadata

        for filter_name, filter_type in view.filter_class.base_filters.items():
            filter_parts = filter_name.split('__')
            filter_name = filter_parts[0]
            attrs = OrderedDict()

            # Type
            attrs['type'] = filter_type.__class__.__name__

            # Lookup fields
            if len(filter_parts) > 1:
                # Has a lookup type (__gt, __lt, etc.)
                lookup_type = filter_parts[1]
                if filters.get(filter_name) is not None:
                    # We've done a filter with this name previously, just
                    # append the value.
                    attrs['lookup_types'] = filters[filter_name]['lookup_types']
                    attrs['lookup_types'].append(lookup_type)
                else:
                    attrs['lookup_types'] = [lookup_type]
            else:
                # Exact match or RelatedFilter
                if isinstance(filter_type, RelatedFilter):
                    model_name = (filter_type.filterset.Meta.model.
                                  _meta.verbose_name_plural.title())
                    attrs['lookup_types'] = "See available filters for '%s'" % \
                                            model_name
                else:
                    attrs['lookup_types'] = ['exact']

            # Do choices
            choices = filter_type.extra.get('choices', False)
            if choices:
                attrs['choices'] = [
                    {
                        'value': choice_value,
                        'display_name': force_text(choice_name, strings_only=True)
                    }
                    for choice_value, choice_name in choices
                ]

            # Wrap up.
            filters[filter_name] = attrs

        metadata['filters'] = filters

        if hasattr(view, 'ordering_fields'):
            metadata['ordering'] = view.ordering_fields
        return metadata

Put that somewhere in your project, then set your DEFAULT_METADATA_CLASS, and you should be all set, with a new key on your OPTIONS requests like so:

"filters": {
    "sub_opinions": {
        "type": "RelatedFilter"
    },
    "source": {
        "type": "MultipleChoiceFilter",
        "choices": [
            {
                "display_name": "court website",
                "value": "C"
            },
        ]
    }
    ...more...
}

This will also display choices, mirroring the way it's handled elsewhere in DRF.