Parse JSON object to Django template - python

I am trying to parse a JSON object to Django template so that I can parse this json object to javascript.
Here how my view creates and parse the json object to the template:
countries = Country.objects.filter(Enabled=True)
citiesByCountry = {}
for country in countries:
citiesInCountry = City.objects.filter(Enabled=True, Country=country)
cities = []
for city in citiesInCountry:
cities.append(city.Name)
citiesByCountry[country.Name] = cities
context = {'citiesByCountry': json.dumps(citiesByCountry)}
return render(request, 'index.html', context)
Now I would like to retrieve all the keys (which will be countries) to my template this way:
{% for country in citiesByCountry%}
<option>{{ country }}</option>
{% endfor %}
But what I get is an option for each character in strings instead of the country name as a whole.
I tried to use .item1 but this didn't work either.
I don't show JavaScript code in my example above as the intent of the question is how to parse and retrieve strings from a JSON object. I need to process this data using javascript later. In specific once the user change country I would like to populate another dropdown that will handle the cities, and therefore I thought to use JSON and Javascript to achieve that, as I don't want to refresh the page on each change.
Any help?

To answer you question in the title:
In <head> (or somewhere), build your array of counties:
<script>
var country_objs = [];
{% for country in citiesByCountry%}
country_objs.push({{country|escapejs}});
{% endfor %}
<\script>
Docs for the filter: https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#escapejs
Then you can use it in JavaScript (but not in Django template). For example:
<select id="cities"><\select>
<script>
var $cities_select = $("#cities");
$(country_objs).each(function(){
$cities_select.append('<option value="' + this.id_or_something + '">' + this.name + '<\option>');
});
<\script>
But from your example I don't see why you need to encode it in JSON in the first place. Why not just pass a dict into the template (via context, just as everything else)?
P.S.
Sorry for using jQuery, l'm just lazy :)

I think you have two options:
Parse the JSON in the <option> tags with javascript.
<script>
var json = JSON.parse({{citiesByCountry}});
//Loop through json and append to <option>
</script>
Add an extra context that isn't JSON serialized. This is a little redundant but a bit simpler.
context = {'citiesByCountry_json': json.dumps(citiesByCountry), 'citiesByCountry': citiesbyCountry}
To be honest, I'd go for the second option since I don't see why you need to send it to the template in JSON in the first place.

When you do citiesByCountry = json.dumps({'a': "b", 'c': None}), citiesByCountry is a string. This is why you get character after character when you iterate over it.
This string representing a JSON object, you can "affect" as-is to a JavaScript variable:
var citiesByCountry = {{ citiesByCountry }};
Which will output something like:
var citiesByCountry = {"a": "c", "d": null};
However, considering what you want to achieve:
In specific once the user change country I would like to populate another dropdown that will handle the cities
I highly recommend you to checkout django-autocomplete-light, which in addition to providing auto-completion, provides a way to filter results based on other fields. This will save you much efforts.

Related

Changing css styles from view in Django

Sorry in advance if there is an obvious answer to this, I'm still learning the ropes with Django.
I'm creating a website which has 6 pre determined subjects (not stored in DB)
english, civics, literature, language, history, bible
each subject is going to be associated with a unique color.
I've got a template for a subject.html page and a view that loads from the url appname/subject/subjectname
what I need to do is apply particular css to style the page according to the subject accessed. for example if the user goes to appname/subject/english I want the page to be "themed" to english.
I hope I've made myself clear, also I would like to know if there is a way I can add actual css code to the stylesheet and not have to change attributes one by one from the back-end.
thanks very much!
In templates you can use conditionals for add css, like this:
<div class="{% if subject=='civics' %}civic-class{% endif %}"></div>
For this, subject value should come from view.
Now, for themed page, you could use the extends tag. Let's supose:
def your_view(request):
subject # Here you get the url subject, 'how' is up to you
if subject == 'english'
template_base = '/some/html/tenplate.html'
elif subject == 'civis':
template_base = '/some/other/template.html'
... # then you return 'template_base' variable to template
Then in template:
{% extends template_base %} # at the top
Hope this helps, is the same logic if you use Class-Based views.
Django's views are not responsible for the presentation, it's the template (and css etc of course)'s reponsability. Now assuming you have the same view serving different subjects, the view obviously need to know which is the current subject (I assume from a captured part of the url passed as argument to the view), so it can easily pass this information to the template, which in turn can use it to add a subject-specific class to the body tag. Then you only have to write your css accordingly.
As an example:
# urls.py
patterns = urlpatterns('',
#...
url(r'whatever/(P?<subject>[a-z-]+>)/$', 'myviews.index', ...),
)
# myviews.py
def index(request, subject):
# do whatever
context = {
# whatever else
'subject':subject
}
return render(request, "whatever/index.html", context)
# whatever/index.html
<html>
# headers etc
<body class="something {{ subject }} etc">
# whatever here
</body>
</html>
You can do this is many ways.
In general you need to return some variable from your view to the html and depending on this variable select a style sheet, if your variable name will match you style sheet's name you can do "{{variable}}.css", if not you can use JQuery.

Keep undefined variables

I am interested in rendering a template in multiple steps or keeping the tags for the undefined variables in Jinja2. I believe this would mean not only creating the 'UndefinedSilent" class (so the template won't crash on missing data) but also keeping the tags with the appropriate variable names if they are missing.
Example:
Let's say we have the name = "Test" included in the context, but quantity is missing.
Givent the following template:
<p>{{name}} has {{quantity}}</p>
After rendering, I need the template to become:
<p>test has {{quantity}}</p>
Does anyone know if this is achievable?
Providing DebugUndefined to named parameter undefined in the env, apparently does the trick. The rendered template preserves the {{<undefined variable}}.
Like here:
from jinja2 import Environment, BaseLoader, DebugUndefined
rtemplate = Environment(loader=BaseLoader,undefined=DebugUndefined).from_string("{{ a }} is defined, but {{ b}} is undefined")
print(rtemplate.render({"a":"a"}))
The result is:
a is defined, but {{ b }} is undefined
It is achievable using the default built-in filter.
<p>{{name|default('{{name}}')}} has {{quantity|default('{{quantity}}')}}</p>
The drawback is that the code becomes uglier and the variable names are duplicated, thus reducing maintainability.
Here is another approach that preserves undefined double curly expressions after rendering, including those that contain "multilevel" (dot-notated) references as well as any others.
The answer provided by Willy Pregliasco does not support preservation of undefined list types, eg {{ some_list[4] }} which is something I required. The below solution addresses this, as well as all possible schema types.
The idea is to parse the input template and try to resolve each expression with the provided context. Any that can not be resolved, we replace with a double curly expression that simply resolves to the original expression as a string.
Pass your template and context through the below preserve_undefineds_in_template function before calling render:
from jinja2 import Template, StrictUndefined, UndefinedError
import re
def preserve_undefineds_in_template(template, context):
patt = re.compile(r'(\{\{[^\{]*\}\})')
j2_expressions = patt.findall(template)
for j2_expression in set(j2_expressions):
try:
Template(j2_expression, undefined=StrictUndefined).render(context)
except UndefinedError:
template = template.replace(j2_expression, f"{{% raw %}}{j2_expression}{{% endraw %}}")
return template
Example:
template = """hello {{ name }}, {{ preserve_me }} {{ preserve.me[2] }}"""
context = { "name": "Alice" }
# pass it through the preserver function
template = preserve_undefineds_in_template(template, context)
# template is now:
# hello {{ name }}, {% raw %}{{ preserve.me }}{% endraw %} {% raw %}{{ preserve.me.too[0] }}{% endraw %}
# render the new template as normal
result = Template(template).render(context)
print(result)
The output is:
hello Alice, {{ preserve_me }} {{ preserve.me[2] }}
I also wated the same behaviour. The library jinja2schema provides the schema of variables needed for your template.
The steps for my solution are:
have a template
obtain the schema structure
give some data to fill
complete de data filling the missing items with the original string
from jinja2 import Template
import jinja2schema
def assign(schema, data, root=''):
'''Returns a corrected data with untouched missing fields
'''
out = {}
for key in schema.keys():
if isinstance(schema[key], (str, jinja2schema.model.Scalar)):
try:
out[key] = data[key]
except:
out[key] = f'{{{{ {root+key} }}}}'
elif isinstance(schema[key], (dict, jinja2schema.model.Dictionary)):
out[key]={}
try:
data[key]
except:
data[key] = {}
out[key] = assign(schema[key], data[key], root+key+'.')
return out
# original template
template_str = '<p>{{name}} has {{quantity}}</p>'
# read schema
schema = jinja2schema.infer(template_str)
# available data
data = {'name':'test'}
# data autocompleted
data_corrected = assign(schema, data)
# render
template = Template(template_str)
print(template.render(data_corrected))
The output is
<p>test has {{ quantity }}</p>
That was the intended result.
Hope it will help. This solution doesn't work with lists, but I think it is possible to extend the solution. You can also obtain the list of missing fields, if you need them.
This is the version with Template rather than Environment:
from jinja2 import Template, DebugUndefined
template = Template("<p>{{name}} has {{quantity}}</p>", undefined=DebugUndefined)
new_template = Template(template.render(name="Test"))
Thanks #giwyni
Another little hack if you just have few variables:
<p>{{name}} has {{ "{{quantity}}" }}</p>
The second replacement will replace to {{quantity}} so all is good ;)

Pyramid and json. Please demystify

I have a Pyramid web app that GETs data from the user, fetches some data from the backend based on the values posted to the view, and then render the fetched results.
This is the workflow:
user->enter name->fetch age,other_details based on 'name' from DB->return a neat table with fetched values
I use ajax to do the first part. i.e., posting values from webpage to view.
Here's the relevant code to POST
<script>
var studentname=$(#stuname).val();
$.ajax({
type: "POST",
url: "/trypass",
data: {name:studentname},
cache: false,
success: function(result) {
alert("Successfully inserted!");
}
});
</script>
<input type="text" id="stuname"></input>
<div id="tablegoeshere"><!--I need fetched results here in a table.--></div>
My views that handle the posted request(Just a semifunctional try):
#view_config(route_name='try', renderer='/trypass.pt')
#view_config(route_name='tryjson',renderer='json')
def upload_view(request):
student_name = request.POST.get('name')
age=DBSession.query(Student).filter(name==student_name).first()
return {"age":age.id} #I dont need this. I need the whole tuple returned but dont know how.
You can see I have stacked a json renderer below my view decorator, but in a different route. I followed it from the documentation but This does nothing than return the values in a new route which is of no use to me.
I researched a lot but not convinced why would I want to use a json renderer to render the returned tuples; and most importantly, HOW.
What I want to know is, how/where do I pass the json values and return it within the same template(trypass.pt)? I have a dedicated to fill in with the parsed json results. But I am absolutely clueless on how to do this. Please guide me. Thank you very much in advance.
MORE EDITS:-
After more research I found out that the getjson() method in javascript gets a json input and we can parse it. But my question still remains. How is the passing done? And am I AJAXing the right way? I also saw there are callbacks in AJAX which probably fetches my response and renders it to my html page. Point me in the right direction please.
Here is a slightly different way of doing it. This way only returns the html back to your ajax without any additional json data like the other answer.
student.pt
<table >
<tr>
<td>Student Age</td>
</tr>
<tr>
<td>${age}</td>
</tr>
</table>
test.js
$.ajax({
type: "POST",
url: "tryjson",
data: {name:studentname},
cache: false,
success: function(html) {
alert("Successfully return our ajax data and html!");
;now insert my html somewhere
}
});
views.py
#view_config(name='tryjson', renderer='templates/student.pt')
def server_view1(request):
student_name = request.POST.get('name')
age=DBSession.query(Student).filter(name==student_name).first()
return {'age':age.id}
Below is an example of "what I think you are asking". I tried to make it very basic so you can see what is going on. Hopefully it will get you where you need to be.
Also, once the ajax request returns with the rendered html (result['html']) you will need to insert it into the DOM.
Of course this is just one way of doing this using AJAX.
You need to study up on chameleon templating and fully understand that so you can create your table in 'student.pt'
student.pt
<table >
<tr>
<td>Student Age</td>
</tr>
<tr>
<td>${age}</td>
</tr>
</table>
test.js
$.ajax({
type: "POST",
url: "tryjson",
data: {name:studentname},
cache: false,
success: function(result) {
alert("Successfully return our ajax data and html!");
html = result['html'] #do want you want with the html here (rendered with "student_table.pt")
age = result['age'] #return this json data for example
}
});
views.py
from pyramid.renderers import render
#view_config(name='tryjson', renderer='json')
def server_view1(request):
student_name = request.POST.get('name')
age=DBSession.query(Student).filter(name==student_name).first()
template_vars = dict()
template_vars['age'] = age.id #the template will need this data so it can fill in.
result = dict()
result['html'] = render('templates/student.pt', template_vars, request)
result['age'] = age.id #Return this data for example
return result

Create a python dictionary of lists

I am new to python and django and was wondering how I would go about making a dict of lists.
My 4 lists are;
ap = request.POST.getlist('amount_paid[]')
pd = request.POST.getlist('paid_date[]')
method = request.POST.getlist('method[]')
comments = request.POST.getlist('comments[]')
How would I make that into a dictionary I could then loop over in a django template such as;
{% for i in the_dict %}
{{i.amount_paid}}
{% endfor %}
Thanks in advance!
Update:
hmm I not sure I posted my question properly. In php, I can do the following on an array of fields:
for($i=0;$i<count($_POST['amount_paid']);$i++) {
echo $_POST['amount_paid'][$i];
echo $_POST['paid_date'][$i];
}
All the form fields are input text fields.. How would I do this in Django?
Python's dict syntax is very simple. It's just key-value pairs inside a pair of curly braces, like this:
the_dict = {
'amount_paid': request.POST.getlist('amount_paid[]'),
'paid_date': request.POST.getlist('paid_date[]'),
'method': request.POST.getlist('method[]'),
'comments': request.POST.getlist('comments[]'),
}
Following your update, it looks like you don't want a dict at all but zip():
post = request.POST
lists = zip(post.getlist('amount_paid[]'),
post.getlist('paid_date[]'),
post.getlist('method[]'),
post.getlist('comments[]'),
)
for amount_paid, paid_date, method, comments in lists:
print amount_paid
print paid_date
# et cetera...
You could create:
the_dict = dict(amount_paid=ap, paid_date=pd, method=method, comments=comments)
Also your template code does not make sense. You are iterating over the keys of the dict and not using them in the body.
EDIT
{% for key in the_dict %}
{% for val in the_dict[key] %}
<input name="{{key}}" value="{{val}}"/>
{% endfor %}
{% endfor %}
As often happens, you're not really asking about the problem you actually need to solve: you've done things in a certain (wrong) way, and are asking about how to get yourself out of the problem you've got yourself into.
If I understand you correctly, you've got a set of fields - amount_paid, paid_date, method, comments - and each field appears multiple times on the form, so you've got one set of values for each entry. You're presumably trying to sort these into a list of dicts for each row.
Well, this is not the right way to go about it in Django. You should be using formsets, which give you one form for each row in a table - ie, exactly what you want to achieve.

How to convert tag-and-username-like text into proper links in a twitter message?

I'm writing a twitter-like note-taking web app.
In a page the latest 20 notes of the user will be listed,
and when the user scroll to the bottom of the browser window more items will be loaded and rendered.
The initial 20 notes are part of the generated html of my django template, but the other dynamically loaded items are in json format.
I want to know how do I do the tag-and-username converting consistently.
Thanks in advance.
There's a couple of pieces to consider here. On the server side, you have to be able to maintain what "chunk" of the notes list the user is on. The easiest way to do this is probably the Django paginator. It works basically by taking a QuerySet, setting a count for the number of items, then giving it the "page" number (or "chunk" number) and it returns those items.
You could do it with JSON, but it would be just as easy to do it with HTML as well. When we look at the client side part of this you'll see why.
So we can have a view "api" to handle a note "chunk" (note all my code samples here are abbreviated just for demonstration. You'd want to have error handling and all that)...
def get_notes_chunk(request, *args, **kwargs):
# Get our notes, however...
all_notes = Notes.objects.all()
# Paginate them based on the page we're on...
chunk_number = request.GET.get('c')
paginator = Paginator(all_notes, 20) # (show 20 at a time)
current_chunk = paginator.page(chunk_number)
# Render to template all that jazz
render_to_template( ... , { 'paginator':paginator, 'current_chunk':current_chunk }, ...)
Our template renders <li> tags which we'll stick into a <ul> on the client...
{% for note in current_chunk.object_list %}
<li>{{ note }}</li>
{% endfor %}
Now on the client, we need to write some javascript to handle this. It's up to you to determine on what event to fire the update, but as for how, we can use a little jQuery to handle this...
<script type="text/javascript">
var chunk_count = 1;
var notes_list_id = 'notes-list'
function load_next_chunk() {
chunk_count += 1;
$.get('{% url get_notes_chunk %}?c=' + chunk_count, function(html) {
$('#'+notes_list_id).append(html);
});
}
</script>
<body>
...
<ul id="notes-list">
<!-- Render chunk #1 here -->
</ul>
...
</body>
Some things that would probably make sense...
Refactor the rendering of the notes list into a template tag so that you can re-use it for your API and the main rendering of your page
Refactor the querying/paginating of the Notes (or whatever) model so that you can re-use it in both views
Figure out on what event the next chunk will be loaded and implement that.
The question isn't that clear... but for generating the HTML out of a Tweet take a look at twp(based on the official twitter-text-java lib):
http://github.com/BonsaiDen/twp
I'm not sure exactly what you're asking, but what's wrong with something like {{ user.get_absolute_url }}? For the tag detail URLs, it really depends on what you're looking for, but you would have to construct the url and view for that yourself.

Categories

Resources