Skip to content

Instantly share code, notes, and snippets.

@mohammed-io
Last active March 1, 2021 08:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mohammed-io/281551d5f0627f8f6ded4c52be5e3850 to your computer and use it in GitHub Desktop.
Save mohammed-io/281551d5f0627f8f6ded4c52be5e3850 to your computer and use it in GitHub Desktop.
from functools import wraps
def multi_format(default='json'):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
request.match_format = lambda *args, **kwargs: _match_format(request, default_format=default, *args, **kwargs)
return func(*(request, *args[1:]), **kwargs)
return wrapper
return decorator
def _match_format(request, default_format, **formats):
last_segment = request.path.split('/')[-1]
maybe_format = last_segment.split('.')[-1]
if last_segment == maybe_format:
for (format, result) in formats.items():
if f"/{format}" in request.headers.get('Accept', {}):
return result() if hasattr(result, '__call__') else result
elif maybe_format in formats.keys():
result = formats[maybe_format]
return result() if hasattr(result, '__call__') else result
result = formats[default_format]
return result() if hasattr(result, '__call__') else result
@mohammed-io
Copy link
Author

mohammed-io commented Feb 25, 2021

Just add the annotation @multi_format() on any view function.

You will have a method called match_format() on the request object. You can use it to match the response according to the format needed.

@multi_format()
@api_view(...)
def some_view(request):
    ...
    return request.match(
        json=JsonResponse(....), # for returning json
        html=lambda : render(....) # lambda is optional if you have some processing.
    );

You might also add a segment/parameter to your URL. Something like:

  url(r'/foo(.?P<format>\w+', ........)

Then add a parameter to the function

@multi_format()
@api_view(...)
def some_view(request, format):
    ...
    return request.match(
        json=JsonResponse(....), # for returning json
        html=lambda : render(....) # lambda is optional if you have some processing.
    );

Now, it can respond to /foo.json and /foo.html as well.

Happy hacking.

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