How to return a json to the same Django template?

Asked

Viewed 978 times

1

I am trying to return a json to the same template without rendering it again (as in the case of render(request, 'dir/base.html, Response))) using Jsonresponse, but it is giving this error: Object of type 'ndarray' is not JSON serializable. I don’t understand, because I am passing a variable in json format. There is another method?

response = {
        'classified': classified,
        'label': label
    }

return JsonResponse(request, response)

In HTML:

<form action="{% url 'documents:classify' %}" name="form" method="POST" enctype="multipart/form-data">
    {% csrf_token %}

    <input type="file" name="file">
    <button type="submit" class="btn btn-success">Classify</button>

</form>
{% if classified %}
    <br>
    Classification: <strong> {{ label }}</strong>
{% endif %}

2 answers

1

Your mistake is

Object of type 'ndarray' is not JSON serializable

That means the json module is working, it’s trying to convert something to json, and it doesn’t know how. This "thing" is an object of the type ndarray.

As your object is this:

response = {
        'classified': classified,
        'label': label
    }

That means either classified or label is or contains objects of the type ndarray within.

The json module needs to work with the python primitive types: str, float, int, bool, list, tuple, dict and set. Other objects cannot be converted to json automatically, you have to convert to a combination of these above objects before passing to the JsonResponse.

ndarray is not part of python, it comes from some external library. A popular library that has a class called ndarray is the numpy. The json module does not know how to handle ndarray. If you have a numpy.ndarray() should call the method tolist() to convert it to list, before trying to serialize into json:

minha_lista = minha_ndarray.tolist()

0


After an exchange of information in the comments/chat, I understood the goal, what you want, essentially, is to run something in the back end return to the front end without the re-rendering of the Django template. I decided to reformulate the answer, but first let’s talk about some concepts covered in the conversation:

Endpoint:
Endpoint is, in summary, the address of an exposed service, or even "what the service exposes", the main characteristic of an endpoint are expressed in the acronym ABC (Address, Binding and Contract), we can make an analogous association to these characteristics as follows: Address is where the service is hosted, Binding is how the service can be accessed, and Contract would be what can be obtained with the service. As answers that question, here at Sopt, complement this explanation.

Why the elucidation of Endpoint? To make it clear to Voce that endpoint is a service on the back end side, that is even if Voce develops an endpoint, and for that I would recommend studying the Django Rest Framework, If you like "Hands on", start with tutorial, still would not solve the problem the way Voce wants to do, I will try to make it clearer with some examples based on your code, but not before explain the crucial concept to work with Jango templates:

Context:
As a function of otmization, templates in Django are compiled only once and stored for later uses. A template can have variable names within key pairs, for example: {{ nome }} and {{ sobrenome }}. A context is a dictionary in which the keys are the names of the variables and the values represent their values (nothing more obvious). When we pass this context to the rendering method {{ nome }} can be "solved" as foo and {{ sobrenome }} as bar, of course it’s just a simple example. The object context represents the context in which the template was rendered. You can see more details here and here

Let’s look at an example (simpleton, again:-)) in which the template asks the user for a date and a numerical value, the data is passed to the view, and then the first submit, is applied a numerical value calculation (multplicated by 2) and returned to the template, follows the code:

<form method="POST">
    {% csrf_token %}
    <label for="data">Data:</label>
    <input id="data" type="date" name="data" value="" /><br>
    <label for="valor">Valor:</label>
    <input id="valor" type="number" name="valor" value="" /><br>
    <input type="submit">
    {% if data != None %}
        <br><br>
        <p>
            Sua última digitação:<br>
            Data Inicial: {{data}}<br>
            Valor: {{valor}}<br>
            Valor Corrigido: {{new_valor}}<br>
        </p>
    {% endif %}
</form>

Now let’s develop a view based on the view fragment you put in the question

def my_view(request):
    contexto = {'data': None, 'valor': None, 'new_valor': None}
    if request.method=='POST':
        data = request.POST['data']
        valor = request.POST['valor']
        new_valor = int(valor)*2
        contexto = JsonResponse({'data': data, 'valor': valor, 'new_valor': new_valor})
        return contexto

    return render(request, 'main/index.html', contexto)

Why doesn’t it work?
Try this at home and you won’t get the same error you reported in the question, but it won’t work out the way you expect, why? why unless you use mixins with Cbvs (Class-Based-Views) (What is not trivial), Voce will always have to re-render the view (at least the way you are using the templates/views). In this example you will see that although no exception is raised, your template will be replaced by a parse of the uploaded json (depends on the browser version), in repl.it will occur in browser error CSRF verification failed. If this context were sent to the template an error would be generated, because the object is not a dictionary type, context must necessarily be of the type dict.

Now let’s look at your code snippet:

response = {
        'classified': classified,
        'label': label
    }

return JsonResponse(request, response)

What are you trying to do in the last line: Passing to JsonResponse two objects, one of the type request and another of the kind dict, expecting some return and finally trying to return to the "browser", but if you do in a python terminal: help(JsonResponse) Voce will see that the first parameter to be sent to this class has to be a serializable, and as default a dictionary type. You are sending a request.

Open a Django terminal and do the following test:

>>> from django.http import JsonResponse
>>> response = {
    'classified': 1,
    'label': 'label'
    }

>>> data_json1 = JsonResponse(response)    
>>> print(data_json1)
<JsonResponse status_code=200, "application/json">

>>> print(data_json1.content)
b'{"classified": 1, "label": "label"}'

Re-rendering:
You can re-render the context as follows in the view (see this version on repl it.):

def home(request):
    contexto = {'data': None, 'valor': None, 'new_valor': None}
    if request.method=='POST':
        data = request.POST['data']
        valor = request.POST['valor']
        new_valor = int(valor)*2
        contexto = {'data': data, 'valor': valor, 'new_valor': new_valor}
        return render(request, 'main/index.html', contexto)

    return render(request, 'main/index.html', contexto)

Ai Oce can question, but in this way I would have to "reconstruct" the whole context to change only one variable? At least as far as I know, for the "Old" type of views you’re using, the answer is yes, do you want to change that? Again: Study the Cbvs.

So it’s impossible to access something in the back end without re-rendering?
The answer is no, there are several ways, but none of them so simple that it would give to elucidate in this answer, as already mentioned above, using mixins with Cbvs (Class-Based-Views) Voce can achieve unthinkable results with views in the old style, which you are using.

Perhaps the most obvious way is to develop a REST API (see the answers to this question, here at Sopt), in which Django would essentially be the back-end providing the endpoints (again, I would suggest the DRF), and the front-end could be developed into a specific FW for front-end, for example Vue.js, React.Js, Angular, etc..

So the best way is to always re-render to continue using Django templates?
Yes and no, to continue using Django templates without having to re-render, Oce needs to execute the requests asynchronously to continue on the same page, for that you will have to use AJAX on the front-end side, and although, in my opinion, this is the best choice (if you’re a solo developer, or you don’t have separate teams for back and front), it’s far from trivial. You can start with this guide: Django and AJAX Form - Say goodbye to the page refresh

Bonus: The messages framework:
Without knowing its context, I venture to say that the framework messages can be useful for Oce, if not for this specific case, for sure for many others, see the text of your presentation:

"Very common in web applications, it is often necessary to display a notification message in a "watertight" way (also called "flash messages") after processing user inputs.

For this, Django provides full support for cookie-based and session-based messages, both for anonymous users and for authenticated users. The message structure allows you to temporarily store messages in a request and retrieve them for display in a request subsequente (generally the next). Each message is marked with a specific level that determines its priority (for example, information, warning or error)."

Completion:
I always say that I love Django, but if I had to point out something that bothers me would be the level of abstraction, so it’s very difficult to learn this framework "in the header", even to post and understand a question/answer on a site like this is complicated, but one thing you need to be aware of: If you really want to take advantage of the language resources, get out of the views in the old style and especially read the documentation.

See the version 1 and version 2 of the examples in repl.it:

  • I get it, @Sidon. But how would I return this json without rendering the template? Just give Return directly or use some Response class?

  • Just make the change I indicated, just put the quotes and return.

  • There was this error here: Attributeerror: 'Dict' Object has no attribute 'status_code'.

  • You have to convert to json in the template. I’m on mobile, so q ir p computer ver p vc

  • How are you treating Sponse in the template? post the code.

  • I edited the question to add the template code.

  • I am unable to run Jango locally and unfortunately in repl.it is still very limited, but only by its code does not understand what the need to send a json, only the context would not be enough, with json vc will have to parse with JS in the template.

  • That part of context I haven’t studied yet. You could provide me an example of how it would be?

  • I’m kind of stuck here, but explain to me what you need to do that I try to help, let’s move to chat, okay?

  • Edited response, according to what was executed in the chat.

  • Thank you, @Sidon. I will read it slowly because it was a lot of explanations.

Show 7 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.