1

Disclaimer - Django noob, especially REST Framework. I'm trying to create an app that for this purposes passes JSON to the template. I figured Django Rest would be ideal.

I have set up a user profile so the user can select various attributes (works fine), and now want to pass a JSON of all the user's selections to template. Think of it as a "my profile" page.

View:

        profile = Profile.objects.filter(user=request.user)
        serializer = ProfileSerializer(profile, many=True)
        myteam = JsonResponse(serializer.data, safe=False)

        print(myteam.content) ## to see what's being passed

        context = {'myteam': myteam.content}

        return render(request, 'main/myteam.html', context)

Template:

<script>
console.log({{myteam}});
<\script>

Django server output:

b'[{"user": "DAVE", "GK1": {"id": 1001, "ShortName": "J. Strummer", "Club": "CLUB", "Shirt": "SHIRT.png", "ShortClub": "ETC", "ShortPos": "FW", "CurrentVal": "10", "TotalPoints": 1}, "GK2": {"id": 320, "ShortName": "D. Jones", "Club": "CLUB2", "Shirt": "ETABE.png", "ShortClub": "ETABE", "ShortPos": "GK", "CurrentVal": "4.5", 

Template - Google Chrome JS console:

Uncaught SyntaxError: Invalid or unexpected token

Template - Chrome details:

console.log(b&#39;[{&quot;user&quot;: &quot;mikey&quot;, &quot;GK1&quot;: {&quot;id&quot;: 1001, &quot;ShortName&quot;: &quot;J. Strummer&quot;, &quot;Club&quot;: &quot;ETC&quot;, &quot;Shirt&quot;: &quot;SHIRT.png&quot;, &quot;ShortClub&quot;: &quot;ETC&quot;, &quot;ShortPos&quot;: &quot;FW&quot;, &quot;CurrentVal&quot;: &quot;10.0&quot;, &quot;TotalPoints&quot;: 1},  // lot's more of this

Noob Conclusion:

I don't seem to be getting a "clean" JSON object passed to the template from the server. Possibly due to the b'[ .... is this bytes literal?

Perhaps I should be using another method to pass the JSON to the template?

TWIST - in my testing I followed the REST tutorial and was able to setup a simple view which returned JsonResponse:

player = Player.objects.all()
serializer = PlayerSerializer(player, many=True)
return JsonResponse(serializer.data, safe=False)

Now if you go to this url mapping it displays in browser a perfect JSON example. I don't see why there should be a difference between

myteam = JsonResponse(serializer.data, safe=False)

and

return JsonResponse(serializer.data, safe=False)

I've been stuck on this on and off for literally days now so any help would be hugely appreciated.

mdgsec
  • 83
  • 1
  • 13
  • 2
    This is really hard to follow, especially because you've just given unconnected snippets of code. Where is the rest of your view? How is that JSON getting to the template? What does the template actually look like? – Daniel Roseman Aug 19 '18 at 11:35
  • 1
    Apologies, trying to keep it as lite as possible but arguably confusing matters. The template for example is literally a shell html doc with a script tag to see what javascript is getting it's hands on. Further thoughts - using .content may be hindering me.... – mdgsec Aug 19 '18 at 11:42
  • 1
    Hi @charliedontsurf - I shouldn't listen to the above comment too much, I found what you've produced to be a great help. I know how much of a pain it is to write massive questions! :) –  Aug 19 '18 at 11:43

2 Answers2

3

I believe you may need to do this:

    from rest_framework.renderers import JSONRenderer

    profile = Profile.objects.filter(user=request.user)

    serializer = ProfileSerializer(profile, many=True)

    content = JSONRenderer().render(serializer.data)

    return JsonResponse(content)

Or this:

    from rest_framework.renderers import JSONRenderer

    profile = Profile.objects.filter(user=request.user)

    serializer = ProfileSerializer(profile, many=True)

    return JsonResponse(serializer.data)

Rather than creating a JsonResponse object and passing it back through context.

There is another alternative, however, to the above as you'll want your endpoint to be extremely flexible.

Such API querying functionality is available from a 3rd-party package. I use this a lot, and find it works really well:

pip install djangorestframework-queryfields

Declare your serializer like this:

from rest_framework.serializers import ModelSerializer
from drf_queryfields import QueryFieldsMixin

class MyModelSerializer(QueryFieldsMixin, ModelSerializer):
    ...

Then the fields can now be specified (client-side) by using query arguments:

GET /identities/?fields=id,data

Exclusion filtering is also possible, e.g. to return every field except id:

GET /identities/?fields!=id
  • Hmm, unless I am misunderstanding views, this results in the following error: **TypeError at /myteam/** b'[{"user":"Dave","GK1":{"id":1001,"ShortName":"J. Strummer","Club":"ETC", #### snipped out lots of JSON data #### :"FW","CurrentVal":"10.0","TotalPoints":0}}]' is not JSON serializable – mdgsec Aug 19 '18 at 12:15
  • Hmmmm, let me just take a little look. –  Aug 19 '18 at 12:37
  • There is an alternative, but I'm not sure how effective this would be long-term and in terms of performance (and would require authorisation and other stuff)..... https://stackoverflow.com/a/12460434/3127448 This looks at existing (simple) API views to source the data. It seems counter-intuitive to me though when Django should be able to calculate the data in the view and pass it to template in a clean format.... – mdgsec Aug 19 '18 at 14:36
  • Hi charlie. You're quite right - indeed what I would do is have a GenericListAPIView that Django can generate - that you can then pass in extra filtering params to generate something much more flexible than your current view. –  Aug 19 '18 at 15:47
2

There are a few things going on here.

Firstly, you have taken the rendered output of a response and passed the content back into a template. This isn't the right thing to do; you should skip the JsonResponse altogether and pass serializer.data into a renderer before sending it to the template.

    serializer = ProfileSerializer(profile, many=True)
    data = JSONRenderer().render(serializer.data)
    context = {'myteam': data}
    return render(request, 'main/myteam.html', context)

Secondly, the encoding is due to Django templates' automatic HTML escaping. You should mark your JSON as safe.

console.log({{ myteam|safe }});
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Disclaimer: Although 100% valid, this doesn't quite work if you're creating an app which relies on RESTful HTTP requests...this will re-render the page. The pre-requisite in the question was to pass JSON to the template - likely to be if he is using some sort of front-end JS framework. –  Aug 19 '18 at 11:52
  • Hi, thanks for deconding my question :) Only thing is (and is actually one of the many things I tried before) when doing this, the data going to the template is now: [OrderedDict([('user', 'DAVE'), ('GK1', OrderedDict([('id', 1001), ('ShortName', 'J. Strummer'), ('Club', 'ETV'), ('Shirt', 'ETC.png'), and therefore not in a JSON format! – mdgsec Aug 19 '18 at 11:52
  • @MichaelRoberts - indeed I am trying to integrate knockout.js! I'm looking to get nice clean data from the server rather than getting tied in knots browser-side parsing data. Thanks to both for responses so far. – mdgsec Aug 19 '18 at 12:01
  • Apologies, I missed out the renderer step - updated, try it now. – Daniel Roseman Aug 19 '18 at 12:02