I want to display countries and states in Django form, for that I am trying to get data from json, create form, pass json data to form and get state of the country on ajax request. I managed to write the process as far as I learned, but at last form is not rendered on Django template. How can I render Django form with following code structure?
My Model:
from django.db import models
class Address(models.Model):
country = models.CharField(null=True, blank=True, max_length=100)
state = models.CharField(null=True, blank=True, max_length=100)
def __str__(self):
return '{} {}'.format(self.country, self.state)
My Forms.py:
import json
def readJson(filename):
with open(filename, mode="r", encoding="utf-8") as fp:
return json.load(fp)
def get_country():
""" GET COUNTRY SELECTION """
filepath = './static/data/countries_states_cities.json'
all_data = readJson(filepath)
all_countries = [('-----', '---Select a Country---')]
for x in all_data:
y = (x['name'], x['name'])
all_countries.append(y)
return all_countries
def return_state_by_country(country):
""" GET STATE SELECTION BY COUNTRY INPUT """
filepath = './static/data/countries_states_cities.json'
all_data = readJson(filepath)
all_states = []
for x in all_data:
if x['name'] == country:
if 'states' in x:
for state in x['states']:
y = (state['name'], state['name'])
all_states.append(state['name'])
else:
all_states.append(country)
return all_states
class AddressForm(forms.ModelForm):
country = forms.ChoiceField(
choices = get_country(),
required = False,
label='Country / Region*',
widget=forms.Select(attrs={'class': 'form-control', 'id': 'id_country'}),
)
class Meta:
model = Address
fields = ['country']
My Form.html
<form class="" action="" method="post">
{% csrf_token %}
{% for error in errors %}
<div class="alert alert-danger mb-4" role="alert">
<strong>{{ error }}</strong>
</div>
{% endfor %}
<div class="row">
<div class="col-lg-6">
<div class="mb-4">
{{ form.country}}
</div>
</div>
<div class="col-lg-6">
<div class="mb-4">
<div class="form-group">
<label >Select a Province/State</label>
<select id="id_province" class="form-control" name="state">
<option value="-----">Select Province/State</option>
</select>
</div>
</div>
</div>
</div>
</form>
My Views:
def readJson(filename):
with open(filename, mode="r", encoding="utf-8") as fp:
return json.load(fp)
def return_state_by_country(country):
""" GET STATE SELECTION BY COUNTRY INPUT """
filepath = './static/data/countries_states_cities.json'
all_data = readJson(filepath)
all_states = []
for x in all_data:
if x['name'] == country:
if 'states' in x:
for state in x['states']:
y = (state['name'], state['name'])
all_states.append(state['name'])
else:
all_states.append(country)
return all_states
def getProvince(request):
country = request.POST.get('country')
provinces = return_state_by_country(country)
return JsonResponse({'provinces': provinces})
def processForm(request):
context = {}
if request.method == 'GET':
form = AddressForm()
context['form'] = form
return render(request, './ecommerce/checkout.html', context)
if request.method == 'POST':
form = AddressForm(request.POST)
if form.is_valid():
selected_province = request.POST['state']
obj = form.save(commit=False)
obj.state = selected_province
obj.save()
return render(request, './ecommerce/checkout.html', context)
My Ajax:
<script>
$("#id_country").change(function () {
var countryId = $(this).val();
$.ajax({
type: "POST",
url: "{% url 'ecommerce:get-province' %}",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'country': country
},
success: function (data) {
console.log(data.provinces);
let html_data = '<option value="-----">Select Province/State</option>';
data.provinces.forEach(function (data) {
html_data += `<option value="${data}">${data}</option>`
});
$("#id_province").html(html_data);
}
});
});
</script>
I am trying to print form.country on template but its not working. What could be the problem?
With ModelForms I find that this type of configuration, in which everything falls under class Meta: works.
However im dubious about that get_country() method. If you share it I can take a deeper look and maybe even test it to make sure that it's not doing anything funky.
If your list of countries is somewhat static and not too long you might wanna consider using a TextChoices enum type in your model attribute to limit the choice selection. Django forms will automatically render a dropdown widget listing the items from your enum.
You can checkout this answer if you want to look into that, which further links to the django docs.
class AddressForm(forms.ModelForm):
class Meta:
model = Address
fields = ['country']
widgets = {
"country": forms.ChoiceField(
choices = get_country(),
attrs={
"class": "form-control",
"id": "id_country"
}
),
}
labels = {
"country" : "Company Country Location"
}
I have a model:
lifecycle_status_choice = (
('Lead', 'Lead'),
('Opportunity', 'Opportunity'),
('Customer', 'Customer'),
)
bill_status_choice = (
('Paid','Paid'),
('Pending','Pending'),
('Partially Paid','Partially Paid'),
)
class Bill(models.Model):
company_name = models.ForeignKey(Contact, on_delete=models.CASCADE)
grand_tot = models.DecimalField(max_digits=9, decimal_places=2)
status = models.CharField(max_length=100, choices=bill_status_choice)
lifecycle_status = models.CharField(max_length=100, choices=lifecycle_status_choice)
...
which has a fk to:
class Contact(models.Model):
associated_company = models.CharField(max_length=100)
...
Views.py
...
from bills.models import Bill
from django.db.models import Sum
def home(request):
data = Bill.objects.all().order_by('date')
context = {
'data ':data ,
}
return render(request, 'home.html',context)
Front end chartsjs:
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: [
{% for data in data %} '{{data.company_name}}', {% endfor %}
],
datasets: [{
label: '# of Votes',
data: [{% for data in data %} {{ data.grand_tot }}, {% endfor %}],
The problem:
I am using charts.js in my front end template and I am trying to display a chart in which as a user creates new company names the chart should display the total amount grouped by company names how would I achieve that?
eg:
I create two invoices for "xyz" company with amounts: $100 & $200 || & create one invoice for "abc" company with amount $100
I want the chart to group the "xyz" invoice amounts and display $300 (ie:100+200) || & "abc" = $100
FYI I'm aware I'm using something completely wrong in the view function but its just something that I tried.
Thanks
You can .annotate(…) [Django-doc] with:
from django.db.models import Sum
Contact.objects.annotate(bills_total=Sum('bill__grand_tot'))
The Contact objects that arise from this QuerySet will have an extra attribute .bills_total.
You can filter this with as satus paid with:
from django.db.models import Q, Sum
contacts = Contact.objects.annotate(bills_total=Sum('bill__grand_tot', filter=Q(bill__status='Paid')))
You can thus for example render this with:
{% for contact in contacts %}
{{ contact.associated_company }}: {{ contact.bills_total }}
{% endfor %}
or convert it to a list of Python dictionaries for example and then return it as a JSON blob.
I have a Django project with data being submitted by form , I am trying to use chart.js to display the data. I need to check whether an array exists() if so then add an item to 'labels' array in 'mycharts.js' . Heres what I have so far
django views.py
ing1 = request.POST.getlist('ingredients1')
ing2 = request.POST.getlist('ingredients2')
ing3 = request.POST.getlist('ingredients3')
( I've passed the above into my context )
mycharts.js
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {chart.js
labels: ['{{ing1.0}}', '{{ing2.0}}', {% if ing3|length > 1 %} '{{ing3.0}}', {% endif %}],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
(etc...)
if I take out the if statement inside of 'labels' array , then it shows the 'ing1' and 'ing2' headings , but I can't get it to work with this if statement , whats the correct way of doing it.
I put 'ing1, 'ing2' and 'ing3' into a list called 'title' inside my views , then put that list into the context , then did the following inside the chart.js script :
{% for item in title %} '{{ item }}', {% endfor %}
now it adds the correct data to the labels array.
Twig malforms my array keys and adds " " brackets to my array keys passed from a symfony2 controller.
The array is passed to a Javascript graphics library which requires:
[{x:"disabled test object",y:"17",label:"disabled test object"}]
Instead {{ array|json_encode|raw }} as suggested by the Twig docs and other SO questions I've read through returns the unreadable:
[{"x":"disabled test object","y":"17","label":"disabled test object"}]
I figure this should not be complicated to achieve but going through the json_encode options so far has not resulted in a clear answer. I am unsure whether there is something I can do from PHP so I've added the tag for now.
EDIT:
I am now attempting to step through the array manually using Twig. {{dump(points)}} confirms it is filled properly
{% set points = chart.dataPoints|json_encode|raw %} <=== this was the problem
dataPoints:
{% for point in points %}
{{ dump(point) }}
{ x: {{ point.x }}, y: {{ point.y }}, label: {{ point.label }} }
{% if loop.lastIndex != true %}
,
{% endif %}
{% endfor %}
But this does not work either as the dump is never reached. Is this the correct way of trying to access objects in a foreach via Twig though? This code is an amalgamation of several Twig docs tutorials.
EDIT 2, the solution:
The line {% set points = chart.dataPoints|json_encode|raw %} turned the whole array into a single string, making it impossible for javascript to interpret as an array. After removing this, all that was left was to make sure the query result had the proper array keys and to transform the X-axis data before passing it to Twig.
$i = 0;
$points = array();
/** #var array $query_result*/
foreach($query_result as &$row) {
foreach($row as $value) {
$point[] = [
'x' => ($i)*10,
'y' => $value['y'],
'label' => $value['label']
];
$points[$i] = $point;
$i++;
}
}
Quotes are not a problem for CanvasJS. As you can see in the example below, "x": works (I took this example):
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Top Oil Reserves"
},
data: [{
dataPoints: [
{ x: 1, y: 297571, label: "Venezuela"},
{ "x": 2, "y": 267017, label: "Saudi" }
]
}]
});
chart.render();
}
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js"></script>
</head>
<body>
<div id="chartContainer" style="height: 200px; width: 100%;"></div>
</body>
</html>
So we need to provide a JSON array for dataPoints:
Define the PHP array in your Controller's function and pass it to the template:
public function myAction()
{
// …
$json = array(
array(
'x' => 1,
'y' => 297571,
'label' => 'Venezuela',
),
array(
'x' => 2,
'y' => 267017,
'label' => 'Saudi',
),
);
return array(
// …
'json' => $json,
);
}
Display the array in the template and pass it to dataPoints :
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js"></script>
<script>
var json = {{ json|json_encode|raw }};
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Top Oil Reserves"
},
data: [{
dataPoints: json
}]
});
chart.render();
}
</script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
The rendered output will be:
// …
<script>
var json = [{"x":1,"y":297571,"label":"Venezuela"},{"x":2,"y":267017,"label":"Saudi"}];
// …
CanvasJS will be able to read this JavaScript array and display the chart.
It looks like the first one is a JavaScript object, and the second one is JSON, try running JSON.parse on the string like this to convert it back in to an object:
var fixed = JSON.parse('[{"x":"disabled test object","y":"17","label":"disabled test object"}]');
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
I want to use template variable inside JavaScript :
My problem is using for loop inside the javascript code is every thing between for loop is going to repeat .. But i don't want that....Below i have pasted my code ..Can anybody tell me the better way of doing this....because this looks ugly..
Here is my code:
{% block extra_javascript %}
<script src="/static/js/Chart.min.js"></script>
<script>
$(document).ready(function(){
var data = {
labels : ["January","February","March","April","May","June","July","August","September","October","November","December"],
datasets : [
{
fillColor : "rgba(151,187,205,0.5)",
strokeColor : "rgba(151,187,205,1)",
data : [{{ stats_dict.0.Jan }},{{ stats_dict.1.Feb }},{{ stats_dict.2.March }},{{ stats_dict.3.April }}
,{{ stats_dict.4.May }},{{ stats_dict.5.June }},{{ stats_dict.6.July }},{{ stats_dict.7.August }},{{ stats_dict.8.Sept }},
{{ stats_dict.9.Oct }},{{ stats_dict.10.Nov }},{{ stats_dict.11.Dec }}]
}
]
};
//Get context with jQuery - using jQuery's .get() method.
var ctx = $("#myChart").get(0).getContext("2d");
//This will get the first returned node in the jQuery collection.
var myNewChart = new Chart(ctx).Bar(data,{});
});
</script>
{% endblock %}
Context :
[[{'stats_dict': [{'Jan': 0}, {'Feb': 0}, {'March': 0}, {'April': 0}, {'May': 0}, {'June': 0}, {'July': 0}, {'August': 0}, {'Sept': 3}, {'Oct': 0}, {'Nov': 0}, {'Dec': 0}]}], {'csrf_token': <django.utils.functional.__proxy__ object at 0xa4d874c>}, {'perms': <django.contrib.auth.context_processors.PermWrapper object at 0xa4d878c>, 'user': <django.utils.functional.SimpleLazyObject object at 0xa47abcc>}, {}, {'LANGUAGES': (('ar', 'Arabic'), ('az', 'Azerbaijani'), ('bg', 'Bulgarian'), ('bn', 'Bengali'), ('bs', 'Bosnian'), ('ca', 'Catalan'), ('cs', 'Czech'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('el', 'Greek'), ('en', 'English'), ('en-gb', 'British English'), ('eo', 'Esperanto'), ('es', 'Spanish'), ('es-ar', 'Argentinian Spanish'), ('es-mx', 'Mexican Spanish'), ('es-ni', 'Nicaraguan Spanish'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('fi', 'Finnish'), ('fr', 'French'), ('fy-nl', 'Frisian'), ('ga', 'Irish'), ('gl', 'Galician'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('hr', 'Croatian'), ('hu', 'Hungarian'), ('id', 'Indonesian'), ('is', 'Icelandic'), ('it', 'Italian'), ('ja', 'Japanese'), ('ka', 'Georgian'), ('kk', 'Kazakh'), ('km', 'Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('lt', 'Lithuanian'), ('lv', 'Latvian'), ('mk', 'Macedonian'), ('ml', 'Malayalam'), ('mn', 'Mongolian'), ('nb', 'Norwegian Bokmal'), ('ne', 'Nepali'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('pa', 'Punjabi'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('pt-br', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('sq', 'Albanian'), ('sr', 'Serbian'), ('sr-latn', 'Serbian Latin'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('ta', 'Tamil'), ('te', 'Telugu'), ('th', 'Thai'), ('tr', 'Turkish'), ('tt', 'Tatar'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('vi', 'Vietnamese'), ('zh-cn', 'Simplified Chinese'), ('zh-tw', 'Traditional Chinese')), 'LANGUAGE_BIDI': False, 'LANGUAGE_CODE': 'en-us'}, {'MEDIA_URL': ''}, {'STATIC_URL': '/static/'}, {'TIME_ZONE': 'America/Chicago'}, {'messages': <django.contrib.messages.storage.fallback.FallbackStorage object at 0xa47ac6c>}]
Step 1 - Never EVER mix JavaScript and Django template language. It's a setup for disaster.
Step 2 - Refactor
Refactor first step:
Create a new view that outputs a JSON'd dict of dates. (I'm not coding your application but I'll give you the pointers on how to improve)
def date_view(request):
stats = Stats.objects.all().order_by('month')
#do some other stuff that you already have done.
return {'january':stats.january, and so forth...}
Step b) - Refactor your current code to something along the lines of this:
var dataForChart = {
labels: [months],
datasets: [{
fillColor: ...,
strokeColor: ...,
data: null,
}]
}
$.get('/myStatsViewUrl', function(data) {
dataForChart["data"] = data;
});
Step c)
Rejoice that you now have decoupled Djangos template from your javascript and much rejoicing was done 'cause you can now reuse it anywhere in your code!