How to render an arbitrary set of key->value pairs from a JSONField in a Jinja2 template? - django

I'm trying to add debug information to a frontend; for reasons that don't need to be gone into at the moment I'm storing the pertinent information in a JSONField.
Storing and retrieving the information works correctly, but when I try to render it via J2 to the page it's supposed to be at I'm running into some issues.
Here's the segment in question:
{% for log in connection.debug_logs.all %}
<tr>
<td></td>
<td>{{ log.log_message }}</td>
<td>
{% for key in log.log_data %}
{{ key }}: {{ log.log_data.key }} <br/>
{% endfor %}
</td>
</tr>
{% endfor %}
What I'm hoping for is for that to produce a series of lines of key: value. What I'm getting instead is:
<td>Login requested by vmx1.internal (11.22.33.44)</td>
<td>
Framed-Route: <br/>
Service-Type: <br/>
Framed-IP-Address: <br/>
Framed-IPv6-Route: <br/>
control:Auth-Type: <br/>
Framed-IPv6-Prefix: <br/>
Delegated-IPv6-Prefix: <br/>
ERX-Egress-Policy-Name: <br/>
ERX-Ingress-Policy-Name: <br/>
ERX-Virtual-Router-Name: <br/>
control:Cleartext-Password: <br/>
</td>
Using {{ log.log_data | pprint }} does yield the keys and values, but renders them as a plaintext JSON string which gets flattened by the html renderer and isn't terribly useful for debugging purposes.
Trying 'log.log_data[key]' instead yields a 'Could not parse the remainder' error.
I've tried the suggestions in this question as well as these and a few others that came up during google searches, but none of them seem to address this issue -- all of them are either working with known keys, working with an actual dict instead of a JSONField, or sometimes both.
I'm probably missing something very simple and straightforward, but I've run out of ways to phrase my question in a search engine. Any tips?
EDIT No, it is not actually a dictionary. I've also tried the solutions in this answer and that leads back to the Could not parse the remainder error.
So the data I'm looking for is absolutely there, I'm just having issues trying to get it to render properly.
Third and final edit: The problem was apparently that Django templates are not quite Jinja2 templates, and rather than {% for key, value in log.log_data.items() %} I needed to use {% for key, value in log.log_data.items %}

Do the conversion from json inside your view to have a greater range of utilities than inside the jinja2 template world.
import json
DEBUG_LOGS_JSON = "[
{"log_data": {"Framed-Route": "route1", "Service-Type": "type1"}, "log_message": "my"},
{"log_data": {"Framed-Route": "route2", "Service-Type": "type2"}, "log_message": "name"},
{"log_data": {"Framed-Route": "route3", "Service-Type": "type3"}, "log_message": "Tarquinius"},
]"
def my_view(request):
my_dict = json.loads(DEBUG_LOGS_JSON) # instead you could also restructure the data passed to the template here.
return render("my_template.html", context=my_dict)
{% for dictionary in my_dict %}
<tr>
<td>{{ dictionary.log_message }}</td>
<td>
{% for key, value in dictionary.log_data.items() %}
{{ key }}: {{ value }} <br/>
{% endfor %}
</td>
</tr>
{% endfor %}
If this does not represent the structure of your json, then please provide an example. Let me know how it goes.

Related

FF92.0 on linux introduces caching issues with django 3.2 inline forms

I've noticed a strange behavior with FF 92.0 (Manjaro Linux, but likely all linux I guess) when using inline forms.
Setup:
Django inline forms
A way to add inlines ajax. I'm using the django forms empty_form
Some arbitray # of inline saved on the server for that object (for example, say 3)
Load the form, add say 2 inlines using javascript. TOTAL_FORMS now shows value=5
Do NOT submit the form, but F5 for a refresh instead
After reload, the inlines shown in the table will mirror that of the server
However the TOTAL_FORMS from the management_form will show value=5, instead of the 3 it should.
page.js:
function insert_inlinedets_form () {
let form_idx = $('#id_details-TOTAL_FORMS').val();
console.log("inserting new form " + form_idx);
let newrow = $('<tr></tr>').appendTo($('#details_form'));
newrow.append($('#empty_form_inlinedets').html().replace(/__prefix__/g, form_idx));
$('#id_details-TOTAL_FORMS').val(parseInt(form_idx)+1);
console.log("added row to inlinedets formset");
};
function remove_inlinedets_form () {
console.log("remove last form ");
let form_idx = $('#id_details-TOTAL_FORMS').val();
if (form_idx>0) {
$('#details_form > tr').last().remove();
$('#id_details-TOTAL_FORMS').val(parseInt(form_idx)-1);
console.log("removed row from inlinedets");
calc_grand_total(); // existing rows haven't changed but the total might
} else {
$('#id_details-TOTAL_FORMS').val(); // if no form left, this SHOULD be 0.
console.log("No more dets left - nothing done.");
}
};
html - empty form:
<div style="display:none">
<table>
<thead></thead>
<tbody>
<tr id="empty_form_inlinedets">
{% for field in formset.empty_form %}
{% if field.is_hidden %}
<td style="display:none;">{{field}}</td>
{% else %}
<td>{{field}}</td>
{% endif %}
{% endfor %}
</tr>
</tbody>
</table>
</div>
html - target table to append to:
<div class="table-responsive shadow encart">
{{ formset.management_form }}
{{ formset.non_form_errors }}
<table class="table table-bordered dbase-table" id="bottom_product_form" width="100%" cellspacing="0">
<caption style="caption-side:top">Products</caption>
<thead>
<tr>
<!-- th content -->
</tr>
</thead>
<tbody id="details_form">
{% for form in formset %}
<tr>
<!-- td content -->
</tr>
{% endfor %}
</tbody>
</table>
</div>
I have validated the following:
The response.rendered_content that the server returns does indeed show the correct TOTAL_FORMS number (3, in the above example)
This does NOT happen upon a hard refresh (ctrl shift r on FF). TOTAL_FORMS, under the same setup, shows the correct # of 3.
This does NOT happen on Google Chrome upon a normal refresh.
So.... any ideas on how I should approach this? What setting could be causing the issue? Please consider in any answer:
I won't have control of user behavior. So simply "not using soft refresh on FF" isn't valid.
I won't have control of user browser settings, so adjusting some settings wouldn't really work (although KNOWING which FF setting may cause the issue is still useful)

executing python script in django with arguments from the frontend

I want to manipulate the values associated with the entries of an external mongodb database using a django webapp.
I've created the app and I'm currently displaying all the entries and their associated value in a simple table.
My idea is simply to create a button, that calls a python script with an argument (the id of that entry) and then changes it from false to true. The problem is, this seems like rocket-science, I've been at this for days now and I just can't get it to work as I have little to no proficiency when it comes to Ajax or Jquery, and all relevant examples I can find don't simply seem to pertain very well to my situation despite how basic it would appear, nor fit with Django 2.0+.
I'm using Django 2.1.5
def change_value(idnumber):
db_value = get_db_status_value(idnumber)
if db_value is True:
change_db_entry_status(idnumber, False)
else:
change_db_entry_status(idnumber, True)
my_template.html
<table>
<thead>
<tr>
<th> Status </th>
<th> Button </th>
</tr>
</thead>
<tbody>
{ % for entry in entry_data % }
<tr>
<td> {{ entry.status }} </td>
<td> <button type="button">{{ entry.idnumber }}</button> </td>
</tr>
</tbody>
{% endfor %}
</table>
I simply can't figure out how I can get the change_value function in there that I can create a button for and include an argument ( entry.idnumber ). This seems incredibly difficult, which from what I understand is a design principle, but it seems a shame if I can't even accomplish something as basic as above.
I was hoping someone could explain how I'm actually supposed to go about this? So far, it seems I require AJAX or Jquery (unfortunately, I barely know the basics of this, and what usually trips me up is that the urls.py which exist in both project and application level seems to work a little different in django 2.0+ compared to older versions)
This isn't actually difficult. It's just that you're missing an understanding of the relationship between the client and the backend code.
Once the template is rendered, the user sees it as HTML in their browser. That's it as far as the backend (ie Django) is concerned. The only way to run any further code on the server is to send another request. A request involves the browser contacting the server, which requires a URL and a view in Django.
Now, one way to send that request is via Ajax, but for your purposes that isn't necessary; since you're just learning, it's easier if you make it a simple form. So, your template might look something like this:
{% for entry in entry_data % }
<tr>
<td> {{ entry.status }} </td>
<td><form action="{% url 'change_value' idnumber=entry.idnumber %}" method="POST"> {% csrf_token %} <button type="submit">{{ entry.idnumber }}</button> </form></td>
</tr>
{% endfor %}
</tbody>
Notice how every iteration of the for loop has a separate form, which posts to a specific URL including the ID number.
Next you need a URL:
path('change_value/<int:idnumber>/', views.change_value, name='change_value'),
and update your function to actually be a view, which needs to accept a request and return a response:
def change_value(request, idnumber):
if request.method != "POST":
return HttpResponseNotAllowed()
db_value = get_db_status_value(idnumber)
if db_value:
change_db_entry_status(idnumber, False)
else:
change_db_entry_status(idnumber, True)
return redirect('/')
You always need to redirect after a POST, but you could redirect back to the same URL that rendered my_template in the first place. (Also note I've put in a test to make sure the user is actually sending a POST; you don't want Google to crawl this URL and flip your values for you.)
And that's it, now you have a button that should toggle your value.
The answer from #Daniel is enough for you. But if you want to use ajax so that the page does not have to refresh to change the vaue, you may do something like:
In your template make changes as:
<table>
<thead>
<tr>
<th> Status </th>
<th> Button </th>
</tr>
</thead>
<tbody>
{ % for entry in entry_data % }
<tr>
<td id="status"> {{ entry.status }} </td>
<td> <button type="button" onclick="change_status(this)" id="{{ entry.idnumber }}">{{ entry.idnumber }}</button> </td>
</tr>
</tbody>
{% endfor %}
and a script as:
<script>
function change_status($this){
var request_data = $this.id;
console.log("data: " + request_data);
$.post({
url: "url that leads to your view",
data : { request_data: request_data},
success : function(json) {
document.getElementById('status').innerHTML = "" //insert the data returned from the function
}
})}
</script>

Django Template Wont Display Complex String

I've got a list in my view, and I am trying to output it to a template. The code for this is:
{% for user in list %}
<tr class="{{ user.1 }}">
{% for item in user %}
<td class="{% autoescape off %}{{item}}{% endautoescape %}">{% autoescape off %}{{item}}{% endautoescape %}</td>
{%endfor%}
</tr>
{%endfor%}
The code itself works fine, however it will not print an element in the list if it is overly complex. It has no issue printing the same element if it is "test", however when it is populated with actual data, it displays a blank field, an moves onto the next element.
I cannot show you the exact data, but it contains speech marks, slashes, various letters and sometimes a percent. the string is usually around 25 chars long,Thank you!
EDIT: Thought I'd add that playing with autoescape doesn't help
The data that i'm trying to output is:
b"\x11\xb4\xc7\xabhM#\x04\x87\x00\xb9kV\xeet\x9d'[\xfb\x7f\x9bE\xd1P"
The entire list is:
[2, '<client>', '<acc_no>', '<username>', False, b"\x11\xb4\xc7\xabhM#\x04\x87\x00\xb9kV\xeet\x9d'[\xfb\x7f\x9bE\xd1P", 0]]

Django template indentation guideline

There is PEP 8 for Python, but I haven't seen a preferred indentation guideline for django templates.
What I mean is, I normally indent blocks like this:
<span>outside</span>
{% if condition %}
<span>within condition</span>
{% endif %}
<span>outside</span>
While this looks good on the editor, but it will look crap on view source like this:
<span>outside</span>
<span>within condition</span>
<span>outside</span>
It would even look worse within the HTML indentation, see below:
<div>
<span>outside</span>
{% if condition %}
<span>within condition</span>
{% endif %}
</div>
will become:
<div>
<span>outside</span>
<span>within condition</span>
</div>
While I agree having better layout in editor is way way more important, but I also get paranoid about the generated messy HTML source code.
I am currently following my own convention in django template guideline for consistency matter. The rule is simple:
Django tag does NOT increase the indentation level
HTML tag does increase the indentation level
It does not matter how many nested Django Tag you have, you should still on the same level of indentation. I consider this as the trade off for putting logic in templates which should be minimized if possible to avoid confusion.
<html>
<body>
<ul>
{% if condition %}
{% for item in menu_item %}
<li>{{ item }}</li>
{% endfor %}
{% endif %}
</ul>
<main>
{% block content %}
<p>Hello World</p>
{% endblock content %}
</main>
</body>
</html>
Side note
I am using 2 spaces for HTML indentation, because HTML tends to have a very deep nested.
For Vim user, please note that the syntax is not html but htmldjango
Thus my ~/.vimrc looks something like:
autocmd Filetype htmldjango setlocal ts=2 sts=2 sw=2 expandtab
Depending your editor, there are ways to set a specific indent width for HTML files.
As for the Django tags, it is actually a good thing not to not add a indent level. See that example:
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
Would be rendered like so:
<ul>
<li>Bacon</li>
<li>Ninja</li>
<li>Tumbleweed</li>
</ul>
And we do not want the two levels of indent. Instead:
{% block content %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblock content %}
My apologies for reviving an old question, but I think the way Emacs' web-mode.el indents Django templates deserves a mention here:
{% block content %}
<div
id="1"
class="fancy"
>
{% if link %}
<a href="{{ link }}">
Click here
</a>
{% endif %}
</div>
{% endblock %}
As you can see, it indents both Django tags and HTML tags, as well as multiline HTML tags. It supports <style> and <script> tags too. In fact, I like this behavior so much that I created DjHTML, a standalone indenter and a pre-commit hook that uses the same indentation rules as web-mode.
I think this is more of a personal preference and what is easy to read and easy to understand in the future (my time limit is "after six months") would be a better alternative than some cookbook recipes. What I use is a two type approach, not just for Django templates but for any language that accepts free formatting of the code. As such I format not critical portions as just left justified block of lines which is an admittedly eyesore but I tend to just ignore them so I can focus on important parts which is extensively indented, preferably in general single action per line and indented at each semantic group change thus all parent/child grouping, nested elements etc. are visible with just one look only.
{% if error_message %}
<p>
<strong>{{error_message}}</strong>
</p>
{% else %}
<table border>
<tr>
<td>
{{degerlendirme.adi}}
</td>
</tr>
<tr>
<td>
<table border>
<tr>
<td>
Tip
</td>
<td>
{{ tip }}
</td>
</tr>
<tr>
<td>
Açıklama
</td>
<td>
{{ degerlendirme.aciklama }}
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<form action="{% url 'perf:degerlendirme' degerlendirme.id %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="OK" />
</form>
</td>
</tr>
</table>
{% endif %}
I usually use Eclipse and there are several good HTML editing tools that you can install into that environment. I try to use them, but prefer to depend solely on indentation for ease of reading/coding/analysis cycle. That is mainly because I regularly need to use other editors, mostly vi. In this case if I rely mainly on Eclipse or whatever IDE I was using for understanding the code vi, nano or other text editors would make my life miserable.
As one of my professors said in a classroom, lots of years ago, spaces are free and tabs are even cheaper.
One last note; Once I needed to remove all white space from a Django template that was creating enormous nested tables in order to improve run time performance. Such might be needed for certain cases. In similar situations it is better to keep one working copy and generate runtime copy from that by a script or tool. I know that, there are some HTML de-clutter tools. In my case, tools I tried corrupted the template and I needed to perform that operation by hand.

Django template variables, RequestContext, and context processors

EDIT
I am building a messaging part of a website but for some reason I just noticed that the function will not actually show the messages in the template. Here is the function and template for the inbox page:
def show_inbox(request):
u = request.user
messages = Message.objects.filter(to_user=u)
context = RequestContext(request,{'messages':messages})
return render_to_response('users/messaging/inbox.html',context_instance=context)
<table class="table-list" cellspacing="0">
<th>Subject</th>
<th>Recieved at</th>
<th>Read</th>
<th>Replied</th>
{% if not messages %}
<tr><td class="left">No messages</td></tr>
{% endif %}
{% for message in messages %}
<tr class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}">
<td class="left largetd">
<img src="{{ message.from_user.profile_pic }}" class="small-pic" />
{{ message.message_subject }}
</td>
<td class="text-center">
<span class="small-text italics">{{ message.message_date_time }}</span>
</td>
<td class="smalltd text-center">{{ message.message_is_read }}</td>
<td class="smalltd text-center"></td>
</tr>
{% endfor %}
</table>
You have checked that the messages variable actually has messages before you call render_to_response() right? Otherwise, view-source on the resulting HTML and see if anything is there. You are missing the <tr> tag around your header cells.
Also, try calling render_to_response a little differently.
return render_to_response('users/messaging/inbox.html', {'messages': messages})
By default, the template will be rendered using a Context instance that is filled with values from the provided dictionary. Therefore you can call it in this simpler form. You aren't using any request variables in the view templates so you don't need the RequestContext, which uses context processors to provide your template with additional information pulled from the request.
#fceruti is correct that you had a collision with django.contrib.messages. When you used RequestContext, Django cycled through your TEMPLATE_CONTEXT_PROCESSORS (declared in the settings.py file) to provide more variables for your template. Specifically 'django.contrib.messages.context_processors.messages' added a messages variable, which overwrote your own. Change the name of your variable and you'd be fine.
Each processor is applied in order.
That means, if one processor adds a
variable to the context and a second
processor adds a variable with the
same name, the second will override
the first.