Share queryset between Django and javascript ¶
templagetags/utils.py
import json from django.utils.safestring import mark_safe from django.core import serializers from django.utils.encoding import is_protected_type from django.core.serializers.json import DjangoJSONEncoder @register.filter def queryset_as_json(qs): """ Sample usage: {{user.list_tipi_movimento|queryset_as_json}} """ json_data = serializers.serialize("json", qs) return mark_safe(json_data) def object_as_dict(obj): data = {} for field in obj._meta.local_fields: # Code stolen from Serializer value = field.value_from_object(obj) # Protected types (i.e., primitives like None, numbers, dates, # and Decimals) are passed through as is. All other values are # converted to string first. if is_protected_type(value): data[field.name] = value else: data[field.name] = field.value_to_string(obj) # Collect ids of M2M related items for field in obj._meta.get_fields(): if field.is_relation and field.many_to_many: data[field.name] = [str(o.id) for o in getattr(obj, field.name).all()] return data @register.filter def object_as_json(obj): """ Sample usage: {{original|object_as_json}} """ try: data = object_as_dict(obj) except: data = {} json_data = json.dumps(data, cls=DjangoJSONEncoder) return mark_safe(json_data)
We these templatetags, you can easily share querysets or single records as global javascript variables:
my_template.html
{% block extrajs %} {{ block.super }} <script> posts_qs = {{posts|queryset_as_json}}; post = {{original.post|object_as_json}}; </script> {% endblock %}
utils.js
jQuery helper to navigate the serialized queryset and search for a specific object:
function lookup_queryset(qs, model, pk) { /* Find a specific record into serialized queryset; */ obj = null; $.each(qs, function(index, value) { if (value.pk == pk && value.model == model) { obj = value; return false; } }); return obj; }
Sample data structure: posts_qs = [ { "pk": "6fd32724-54f4-4174-9e38-1888ebf5da06" "model": "backend.post", "fields": {"subject": "the subject", "data": ...}, }, ... ]; Sample usage: function OnTipoMovimentoChange() { var selected = $('input[name=tipo_movimento]:checked', '#movimento_form').val(); if (selected) { var obj = lookup_queryset(posts_qs, "backend.tipomovimento", selected); if (obj) { ... } } }
If you need to serialize nested structures, use a DRF serializer as follows:
from django import template from django.utils.safestring import mark_safe from rest_framework.renderers import JSONRenderer from backend.serializers import LinkSerializer register = template.Library() @register.filter def serialize_links(queryset): """ Sample usage: var QUERYSET_LINKS = {{links|serialize_links}}; Returns a JSON-serialized dictionary, where the row id is the key; Example: data: { '29cfcecf-359f-448d-b408-5e613cd496e7': OrderedDict([ ('id', '29cfcecf-359f-448d-b408-5e613cd496e7'), ('category', OrderedDict([ ('id', '7d9b54f1-0006-42d9-98a9-f5f1b4f37130'), ('description', 'python'), ... ('ranking', 10), ('description', 'Primer on Python Decorators'), ... Serialized JSON: { "4fa56608-b6a0-492a-9989-7d3e46c1b581": { "id": "4fa56608-b6a0-492a-9989-7d3e46c1b581", "category": { "id": "a28337ed-1ecf-4bde-af7c-905456ebc5eb", "description": "raspberry_pi", ... }, "ranking": 10, "description": "Extending the life of your Raspberry PI SD Card", ... }, ... """ serializer = LinkSerializer(queryset, many=True) #data = serializer.data # For faster lookup, transform the list of results into a dictionary, using row id as key data = {row['id']: row for row in serializer.data} return mark_safe(JSONRenderer().render(data).decode())