Skip to content

Instantly share code, notes, and snippets.

@czue
Last active December 15, 2023 21:41
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save czue/90e287c9818ae726f73f5850c1b00f7f to your computer and use it in GitHub Desktop.
Save czue/90e287c9818ae726f73f5850c1b00f7f to your computer and use it in GitHub Desktop.
A simple django template tag that lets you automatically render json from a python object
"""
Usage:
{% load json_tags %}
var = myJsObject = {{ template_var|to_json }};
Features:
- Built in support for dates, datetimes, lazy translations.
- Safe escaping of script tags.
- Support for including QuryDict objects.
- Support for custom serialization methods on objects via defining a `to_json()` method.
"""
import datetime
import json
from decimal import Decimal
from django import template
from django.conf import settings
from django.http import QueryDict
from django.utils.encoding import force_str
from django.utils.functional import Promise
from django.utils.safestring import mark_safe
register = template.Library()
ISO_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
def json_handler(obj):
if callable(getattr(obj, 'to_json', None)):
return obj.to_json()
elif isinstance(obj, datetime.datetime):
return obj.strftime(ISO_DATETIME_FORMAT)
elif isinstance(obj, datetime.date):
return obj.isoformat()
elif isinstance(obj, datetime.time):
return obj.strftime('%H:%M:%S')
elif isinstance(obj, Decimal):
return float(obj) # warning, potential loss of precision
elif isinstance(obj, Promise):
return force_str(obj) # to support ugettext_lazy
else:
return json.JSONEncoder().default(obj)
@register.filter
def to_json(obj):
def escape_script_tags(unsafe_str):
# seriously: http://stackoverflow.com/a/1068548/8207
return unsafe_str.replace('</script>', '<" + "/script>')
# json.dumps does not properly convert QueryDict array parameter to json
if isinstance(obj, QueryDict):
obj = dict(obj)
# apply formatting in debug mode for ease of development
indent = None
if settings.DEBUG:
indent = 2
return mark_safe(escape_script_tags(json.dumps(obj, default=json_handler, indent=indent)))
@aruseni
Copy link

aruseni commented Jul 16, 2018

Hi czue! The tag for loading tags is load. So you should change import to load in the description.

@czue
Copy link
Author

czue commented Jan 22, 2019

thanks! sorry that was a silly mistake.

@mark0978
Copy link

You can make it a tiny bit better with this:

    indent = None
    if settings.DEBUG:
        indent = 2
    return mark_safe(escape_script_tags(json.dumps(obj, default=json_handler, indent=indent)))

So that the JSON is minimal when in production and more readable when in DEBUG mode.

@czue
Copy link
Author

czue commented Sep 1, 2019

Thanks for the suggestion @mark0978 ! Updated.

@SOesterreicher
Copy link

Hello Cory Zue,
under which license do you offer this code snippet? Any easy permissive license?
Thanks in advance
Siegfried

@czue
Copy link
Author

czue commented Nov 14, 2019

sure, yeah do whatever you want with it! I don't know if a github comment is binding but feel free to consider it MIT

@SOesterreicher
Copy link

Thank you very much.
Siegfried

@Veilkrand
Copy link

you need to import settings in order to use settings.debug in line 58.
from django.conf import settings

@czue
Copy link
Author

czue commented Jan 14, 2021

oops, thanks! fixed

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