I have data coming from the database as the below format on query execution
[(‘Country-1’, ‘state1’), (‘Country-1’, ‘state2’), (‘Country-1’, ‘state3’),
(‘Country-2’, ‘state1’), (‘Country-2’, ‘state2’), (‘Country-2’, ‘state3’),
(‘Country-3’, ‘state1’), (‘Country-3’, ‘state2’), (‘Country-3’, ‘state3’)]
I want to convert to as this resultset as below format
context = {
'countries': [ { 'Countryname': 'country1’,
'state': [ { 'Statename': 'state1'},
{'Statename': 'state2'},
{'Statename': 'state3'} ]
},
{ 'Countryname': 'country2’,
'state': [ { 'Statename': 'state1'},
{'Statename': 'state2'},
{'Statename': 'state3'} ]
},
{ 'Countryname': 'country3’,
'state': [ { 'Statename': 'state1'},
{'Statename': 'state2'},
{'Statename': 'state3'} ]
}
]
}
So that I can iterate the data in the in HTML in Django to create the tree format:
<ul class = "myUL">
{% for country in data %}
<li class = "caret"> {{ country.countryname }} </li>
<ul class="nested">
{% for state in country.statename %}
<li>{{state.statename}}</li>
{% endfor %}
</ul>
{% endfor %}
Expected output for HTML is:
Country-1
State1
State2
State3
Country -2
State1
State2
State3
Country -3
State1
State2
State3
Try the following:
Data parser:
data = [('Country-1', 'state1'), ('Country-1', 'state2'), ('Country-1', 'state3'), ('Country-2', 'state1'), ('Country-2', 'state2'), ('Country-2', 'state3'), ('Country-3', 'state1'), ('Country-3', 'state2'), ('Country-3', 'state3')]
reformatted_data = {}
for pair in data:
state_list = reformatted_data.get(pair[0], None)
if state_list:
if pair[1] in state_list:
pass
else:
reformatted_data[pair[0]].append(pair[1])
else:
reformatted_data[pair[0]] = [pair[1]]
# Try this print in your console to make sure it's working properly
print(reformatted_data)
Template to display data:
<ul class = "myUL">
{% for country, statelist in data.items %}
<li class = "caret"> {{ country }} </li>
<ul class="nested">
{% for state in statelist %}
<li>{{ state }}</li>
{% endfor %}
</ul>
{% endfor %}
Related
When the user clicks a specific button, I want to call an synchronous function inside the already used view function, but passing a parameter from JavaScript. How can I do it?
Template:
<input class="form-check-input" type="checkbox" value="{{ subject.id }}" id="flexCheckDefault{{ subject.name }}" onclick="checkRequisite(this.defaultValue)">
Javascript:
function checkRequisite(id){
}
View:
if request.user.is_authenticated and request.user.groups.filter(name='student'):
subjects = subject.objects.all()
async def checkResquisite(id):
requisite = Requisite.objects.filter(subject_requisite_id=id)
context = {'subjects': subjects, 'requisite': requisite}
template = loader.get_template('student/subject/select.html')
return HttpResponse(template.render(context, request))
elif request.user.is_authenticated and request.user.groups.filter(name='teacher'):
return render(request, 'admin/home/index.html', {})
else:
return redirect('login')
I think there are a few misconcepts here. Async functions are called from the frontend to the backend (with ajax, fetch...), not the other way around:
async function checkRequisite(id){
response = await fetch(...);
}
Also, normally you would have two different views, I believe just as a good practice to have your code more organized and descriptive of what your views do exactly.
def load_template(request):
...
return render(...)
def ajax_view(request):
...
return JsonResponse(...)
But, to answer your question, the code below does the following:
On the template, with every click on checkboxes search which of them are selected take their value (subject.id), push into a list and send that list of IDs to backend using a post request with the fetch API.
There (on the backend), check the type the request method and filter requisite based on that list of IDs.
student/subject/select.html
{% extends 'base.html' %}
{% block content %}
{% for subject in subjects %}
<label>{{ subject.name }}</label>
<input class="form-check-input" type="checkbox" value="{{ subject.id }}" id="checkbox" onclick="checkRequisite()">
<br>
{% endfor %}
<hr>
<div id="demo"></div>
{% endblock %}
{% block script %}
<script>
async function checkRequisite() {
var id_list = [];
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].type == "checkbox") {
if (inputs[i].checked) {
id_list.push(inputs[i].getAttribute('value'))
}
}
}
var payload = {
subject_ids: id_list,
};
var data = new FormData();
data.append( 'data' , JSON.stringify( payload ) );
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
await fetch("{% url 'core:load-template' %}", {
method: 'post',
body: data
}).then((response) => {
return response.json();
}).then((data) => {
let element = document.getElementById("demo").innerHTML = '';
for (let key in data['requisites']){
let element = document.getElementById("demo").innerHTML += `<p>Requisite: ${data['requisites'][key]['name']} | Subject: ${data['requisites'][key]['subject']}<p><br>`;
}
});
}
</script>
{% endblock %}
views.py
def load_template(request):
if request.user.is_authenticated and request.user.groups.filter(name='student'):
queryset = Subject.objects.all()
requisite = None
if request.method == 'POST':
data = json.loads(request.POST.get('data'))
requisites = Requisite.objects.filter(subject__id__in=data['subject_ids'])
response = {}
for requisite in requisites:
response[requisite.id] = { 'name': requisite.name, 'subject': requisite.subject.name }
return JsonResponse({ 'requisites': response })
return render(request, 'student/subject/select.html', {'subjects': queryset })
elif request.user.is_authenticated and request.user.groups.filter(name='teacher'):
return render(request, 'admin/home/index.html', {})
else:
return redirect('login')
urls.py
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
path('load/template/', views.load_template, name='load-template'),
]
I am using Django and Chart.js, but I don't think what's more important here is an understanding of Javascript and HTML.
To create charts using Chart.js, I need the charts to have different names. For this, I have thought about using a counter variable.
<script>**var count = 0;**</script>
<ul>
{% for object in objects %}
<li>
...
<script>
{% block jquery %}
console.log(count);
var endpoint = '/api/chart/data/';
fetch(endpoint, {
method: "GET",
}).then(response => response.json())
.then(data => {
console.log('Success:', data);
**count = count + 1;**
...
eval('var chart_id = "myChart{{ count }}"');
eval('var ctx{{ count }} = document.getElementById(chart_id)');
eval('var myChart = new Chart(ctx{{ count }}, graph_data)');
console.log("myChart{{ count }}")
})
.catch((error) => {
console.log("Error:")
console.log(error);
});
{% endblock %}
</script>
<canvas id= "myChart{{ count }}" width="400" height="400"></canvas>
...
</li>
{% endfor %}
</ul>
However, when I look in the source code on my dev server, I can see that the id for all of the canvas tags is simply "myChart". Why is this? What am I doing wrong?
Try this:
{{ forloop.counter }} will return the current iteration count going on.
<ul>
{% for object in objects %}
<li>
...
<script>
{% block jquery %}
console.log(count);
var endpoint = '/api/chart/data/';
fetch(endpoint, {
method: "GET",
}).then(response => response.json())
.then(data => {
console.log('Success:', data);
...
eval('var chart_id = "myChart{{ forloop.counter }}"');
eval('var ctx{{ forloop.counter }} = document.getElementById(chart_id)');
eval('var myChart = new Chart(ctx{{ forloop.counter }}, graph_data)');
console.log("myChart{{ forloop.counter }}")
})
.catch((error) => {
console.log("Error:")
console.log(error);
});
{% endblock %}
</script>
<canvas id= "myChart{{ forloop.counter }}" width="400" height="400"></canvas>
...
</li>
{% endfor %}
</ul>
I have an SMS function on my Django app, but I want to happen is record the message and timestamp each time I send a message. I need to store the value inside sms_message on another table every time I click send sms.
The entry on the other table will be like this:
id: 1
Description: As of {z.timestamp}, the Rainfall Warning is now at {z.level}. Please prepare for evacuation
Timestamp: XXXXX
Help, how to do it? Thanks!
#views.py
def send_sms(request):
aws_access_key_id = "XXXXXXXXXXXX"
aws_secret_access_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
aws_region_name = "us-east-1"
z = Rainfall.objects.latest('timestamp', 'level')
sender_id = "Test"
sms_message = f'As of {z.timestamp}, the Rainfall Warning is now at {z.level}. Please prepare for evacuation'
client = boto3.client(
"sns",
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
region_name=aws_region_name
)
topic = client.create_topic(Name="notifications")
topic_arn = topic['TopicArn'] # get its Amazon Resource Name
numbers = Mobile.objects.all()
for i in numbers:
client.subscribe(
TopicArn=topic_arn,
Protocol='sms',
Endpoint=i.mobile_number
)
response = client.publish(
Message=sms_message,
TopicArn=topic_arn,
MessageAttributes={
'string': {
'DataType': 'String',
'StringValue': 'String',
},
'AWS.SNS.SMS.SenderID': {
'DataType': 'String',
'StringValue': sender_id
}
}
)
messages.info(request, 'SMS sent successfully!')
return HttpResponseRedirect('/advisory/')
#sendsms.html
{% block content %}
{% if messages %}
{% for message in messages %}
{% if message.tags %} <script>alert("{{ message }}")</script> {% endif %}
{% endfor %}
{% endif %}
<form method="post" action="{% url 'send_sms' %}">
{% csrf_token %}
<button class="btn btn-danger btn-block btn-round">Send SMS</button>
</form>
{% endblock content %}
I’d like to make a request in my j=Javascript code to the Apostrophe’s server (like PUT, POST…). I did that but it does not work:
{% extends "layout.html" %}
{% block main %}
<div class="main-content">
<h3>Hello world!
{% if not data.user %}
<a class="login-link" href="/login">Login</a>
{% endif %}
</h3>
<p>This is a very bare bones Apostrophe project. Now, get to work and make a real website!</p>
</div>
{{
apos.area(data.page, 'body', {
widgets: {
'apostrophe-images': {
size: 'full'
},
'apostrophe-rich-text': {
toolbar: [ 'Styles', 'Bold', 'Italic', 'Link', 'Unlink' ],
styles: [
{ name: 'Heading', element: 'h3' },
{ name: 'Subheading', element: 'h4' },
{ name: 'Paragraph', element: 'p' }
]
}
}
})
self.apos.tasks.add(self.__meta.name, 'insert-stuff', function(apos, argv, callback) {
var req = self.apos.tasks.getReq();
return self.find(req, { cool: true }).toArray().then(function(err, pieces) {
if (err) {
return callback(err);
}
});
};
}}
{% endblock %}
When I run the script (going to localhost:3000), I receive an error in my console. I have the error :
e.stack: Template render error: (apostrophe-pages:pages/home.html) [Line 34, Column 3]
expected variable end
at Object.exports.prettifyError (C:\Windows\System32\test-project\node_modules\nunjucks\src\lib.js:34:15)
at new_cls.render (C:\Windows\System32\test-project\node_modules\nunjucks\src\environment.js:469:27)
at Object.self.renderBody (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-templates\index.js:309:47)
at Object.self.renderForModule (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-templates\index.js:176:19)
at Object.self.render (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-module\index.js:173:34)
at Object.self.renderPageForModule (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-templates\index.js:666:28)
at C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-module\index.js:349:31
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:726:13
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:52:16
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:269:32
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:44:16
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:723:17
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:167:37
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:52:16
at iterate (C:\Windows\System32\test-project\node_modules\async\lib\async.js:260:24)
at Object.async.forEachOfSeries.async.eachOfSeries (C:\Windows\System32\test-project\node_modules\async\lib\async.js:281:9)
:: 2018-08-17T10:43:23+0200: template error at /
Current user: admin
{ Template render error: (apostrophe-pages:pages/home.html) [Line 34, Column 3]
expected variable end
at Object.exports.prettifyError (C:\Windows\System32\test-project\node_modules\nunjucks\src\lib.js:34:15)
at new_cls.render (C:\Windows\System32\test-project\node_modules\nunjucks\src\environment.js:469:27)
at Object.self.renderBody (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-templates\index.js:309:47)
at Object.self.renderForModule (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-templates\index.js:176:19)
at Object.self.render (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-module\index.js:173:34)
at Object.self.renderPageForModule (C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-templates\index.js:666:28)
at C:\Windows\System32\test-project\node_modules\apostrophe\lib\modules\apostrophe-module\index.js:349:31
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:726:13
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:52:16
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:269:32
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:44:16
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:723:17
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:167:37
at C:\Windows\System32\test-project\node_modules\async\lib\async.js:52:16
at iterate (C:\Windows\System32\test-project\node_modules\async\lib\async.js:260:24)
at Object.async.forEachOfSeries.async.eachOfSeries (C:\Windows\System32\test-project\node_modules\async\lib\async.js:281:9) name: ‘Template render error’ }
As nunjucks now supports using set as a block I wanted to do something like this:
{% set navigationItems %}
{% for item in items %}
{ name: item.name, url: item.url }{% if not loop.last %},{% endif %}
{% endif %}
{% endset %}
Then call this variable as the input object on another macro, like so:
{{ navigation(items=[navigationItems]) }}
However, navigationItems is evaluated as a string, not an array-literal. Any idea how, or if this is possible?
Thanks.
I'm not exactly sure what you're trying to accomplish. It looks like you want to loop over one array called items and copy it into a new array called navigationItems. Perhaps items contains more keys than you want to pass to the macro?
I'm going to make that assumption, otherwise you could simply copy items into navigationItems like so:
{% set navigationItems = items %}
This example works:
{% macro navigation(items) %}
<ul>
{% for item in items %}
<li>{{ item.name }} - {{ item.url }}</li>
{% endfor %}
</ul>
{% endmacro %}
{% set websites = [
{
name: 'Google',
url: 'http://google.com',
description: 'A search engine'
},
{
name: 'GitHub',
url: 'http://github.com',
description: 'A webapp for your git repos'
},
{
name: 'StackOverflow',
url: 'http://stackoverflow.com',
description: 'The answer: 42'
}] %}
{% set navigationItems = [] %}
{% for website in websites %}
{% set navigationItems = (navigationItems.push({name: website.name, url: website.url}), navigationItems) %}
{% endfor %}
{{ navigation(items=navigationItems) }}
websites values contain a description key which is not passed on to the navigationItems array. If it were me, I'd just pass website directly to the navigation macro since your keys: name and url are the same in both arrays.
The pattern here is almost like a map method in Javascript or Ruby.