django {% url %} with parameters (list of dicts) - python

I'm following the suggestion at: Refresh <div> element generated by a django template
I'm passing along a few variables, a la:
url: '{% url 'search_results' 'sched_dep_local' flights|escapejs %}',
The problem is that 'flights' is a list of dicts that the search_results template needs access to, and it's pretty large and contains things like apostrophes
[{'foo': 'bar'}, {'foo': 'baz'}] and so on
So the only way I can use it with {% url %} appears to be with escapejs to get rid of the apostrophes, but then in views.py, I need it to be a list of dicts again, so I can do things like:
def search_results(request, sort_key, flights):
flights = search_utils.sort(flights, sort_key)
return render_to_response('search_results.html', { 'flights' : flights} )
Is there a simple way to do this? Alternatively, am I going about this whole thing all wrong?
ETA: See also (explains what I'm trying to do and why):
<script>
$(".sort").on("click", function() {
$.ajax({
url: '{% url 'search_results' 'sched_dep_local' flights|escapejs %}',
success: function(data) {
$('#search-results').html(data);
}
});
});
</script>
I have a template (in search_results.html) that prints some data for each flight in flights. I want to sort that data and rerender the template, but I can't figure out how.

This isn't the right way to deal with complex data. Rather than sending it via the URL, you should be using a POST and sending it in the body of the request: since you're using jQuery, you can just do method: "POST" in that call. In the backend, you can deserialize it from JSON.
However, it does seem a bit strange to do this at all; the data is evidently coming from the Django backend already, so it's not clear why you want to post it back there.

Related

Django - rendering templates and passing them via AJAX

Ok, so this a more of a conceptual question.
Basically i just found out i can do the following:
Use ajax to start a function that returns a rendered HTML template, send that as a response to ajax, then apply that HTML code to a host div. This feel so much more versatile and powerfull than using include tags.
I can render, and re-render forms (or any little snippet of HTML) in a flash with minimal JS, and, most importantly without refreshing the page.
I understand that passing whole html blocks via AJAX might not be optimal, so i am avoiding abusing this.
However this feels "hacky" to me, so my question is, Why should i NOT do this?
if you want some example code:
{% load crispy_forms_tags %}
{% load static %}
<div id="formdiv1">
</div>
<script>
function getform_rec() {
$.ajax({
type: "GET",
url: "/backoffice/ajax/rec_form/",
success: function(data)
{
$("#formdiv1").html(data);
}
});
}
getform_rec()
</script>
and my view
def ajax_rec_form(request):
if request.method == "GET":
rec_id = request.GET.get("id", None)
if not rec_id:
form = Rec_form()
else:
form = Rec_form(instance=Rec.objects.get(id=rec_id))
return render(request, "rec-form.html", {"rec_id": rec_id, "form": form})
rec-form.html is just a html file with no extends or includes, and it just renders the form.
Yes the view is still incomplete it doesnt handle for submission, etc.. Because i want to make sure i'm not doing somethign very bad before continuing down this path.
Oh and getform_rec() is actually beeing called by a button, i just made it call on load for simplicity here.
You need a code like this:
data = loader.render_to_string(
'rec-form.html', {"rec_id": rec_id, "form": form}
)
return JsonResponse(data)

Updating a variable length html list via ajax

I would like to update a list in my template based on a response from an ajax call. As far as I understand it is not possible to send a list directly back from the view and iterate over it. That is why I tried to come up with an alternative, but I am kind of stuck. Here's my current code:
Template (shortened):
{% for a in attributes %}
<li> a.name </li>
{% endfor %}
Ajax:
$.ajax({
url: "{% url 'main:next_attributes' %}",
data: {'next':'abc'},
datatype : 'json',
success: function (data) {
console.log("Success");}})
console.log should be replaced by something which iterates over new values and updates the values in the list above. Tricky part here is, that the number of list items might be different (both lower or higher) than before. However, I am unclear how the response from the view might look, which is why this still has a placeholder (see next part).
Views.py:
def next_attributes(request):
keyword = request.GET.get('next', None)
next_attributes = Attributes.objects.filter(keyword=keyword)
data = {'attributes':next_attributes}
return JsonResponse(data)
Problem here is, that I cannot return a query result via JsonResponse..
In summary:
I want to get a new query result based on the filter given in the ajax request and update a list (variable length, based on query result) in my template. I would appreciate any pointers.
As pointed by #thebjorn, you can use Attributes.objects.filter(keyword=keyword).values('name') to get the list of values. A full example would be the following:
def next_attributes(request):
keyword = request.GET.get('next', None)
next_attributes = Attributes.objects.filter(keyword=keyword).values('name')
data = {'attributes':next_attributes}
return JsonResponse(data)
I am not entirely sure whether .values returns a JSON-serializable object, but the essence would be that.
Then, the expected object should look like this:
{'attributes': [{'name': 'name1'}, {'name': 'name2'}]}
Then, since you are using jQuery, you can do the following. Assuming your <li>'s are wrapped in a <ul> with id myList:
$.ajax({
url: "{% url 'main:next_attributes' %}",
data: {'next':'abc'},
datatype : 'json',
success: function (data) {
$('#myList').empty(); // Clear old values
data.attributes.forEach(function(element){
$('#myList').append("<li>"+element.name+"</li>"); // Please note that this does not include any sanitization of the data. Be careful with that
}
}
}

Django: AJAX and path params

I am trying to figure out how to communicate variables a particular js file that makes an AJAX request in Django.
Say we have an url with a path param that maps to a particular view:
url(r'^post/(?P<post_id>\d+)/$', TemplateView.as_view('post.html'))
And then the post.html includes a script that performs an AJAX request to fetch the post as JSON:
post.html
<script src="{{ STATIC_URL }}/js/post.js"></script>
<div id="post-container"></div>
This could be the js file. Let's say we do it this way because the post (as JSON) needs to be used in a js plugin to display it in a particular fancy manner.
post.js
$.ajax({
url: '/post/??post_id??',
contentType: 'application/json'
})
.done(function(post_data) {
togglePlugin(post_data);
});
My main concern is how to figure out what post_id is from the js file in order to make the appropriate call.
How are these parts usually connected in Django? The parts I am talking about are urls, path params, views and ajax.
To get urls from your template you should use {% url %} template tag, for that you should add a name to your url:
url(r'^post/(?P<post_id>\d+)/$', TemplateView.as_view('post.html'), name='my_name)
then in your can template call that url in this way {% url 'my_name' object.pk %} see this for more.
when you work with different file for your js, and you want to pass django variables, you should declare a <script> before import the js file, and there declare your variable:
<srcipt>var foo={{django_var}}</script>
<script src="{{ STATIC_URL }}/js/post.js"></script>
and then use the variable foo in the js.
You can pass a post_id variable to django template.
However if you don't want it to "hurt", put your .js code (script) in you html file, rendered by django. So you can use django template tags.
For example:
var url = "/post/{{post_id}}";
$.ajax({
type: 'POST',
url: url,
dataType: 'json',
data: {
some_field: field_data,
some_other_field: field_data
},
success: function(data) {
do_something();
},
error: function(data) {
do_somethin();
}
});

In Django view,How to save array which pass from template via Ajax, to database?

I create array in JavaScript and send it via Ajax to my view and I can get it in view.Now I want to save it to my database(mysql) but I get "internal error" from Ajax.I think Ajax when post data to view and Django want to connect MySQL database, connection by ajax was abroted.
this is my template Ajax:
var postUrl = "http://localhost:8000/student/{{id}}/student_add_course/";
$('form[name="myForm"]').submit(function(){
$.ajax({
url:postUrl,
type: "POST",
// stock is somthing like this.stock=[[1,12],[2,14],...]
data: {'stock': stock, 'counter':i,csrfmiddlewaretoken: '{{ csrf_token }}'},
error:function (xhr, textStatus, thrownError){
alert(thrownError);},
})
});
this is view:
def student_add_course(request,student_id):
if request.method=='GET':
context={'id':student_id , 'form':AddCourseForStudentForm()}
request.session['ListOfCourses']=[]
return render(request, 'student/AddCourseForStudentForm.html',context)
elif request.is_ajax():
listOFcourses=course_passes.objects.order_by('-id')
id =listOFcourses[0].id
counter=int(request.POST.get('counter'))
for i in range(0,counter):
selected_course=request.POST.getlist('stock[%d][]'%i)
//course_passes is my table.
// evrey thing is good until here but when I want to save it,I get error.
#a.save()
return render(request, 'student/add_course.html')
What is my code problem and How should I solve it?
Is there better way to do this?
I'm sorry for my bad English.
Edit:
Finally I understand my problem is in MySQL side. The value which I want to insert is not correct and I change
a=course_passes(id+1,int(selected_course[0]),int(selected_course[1]),int(student_id))
to
find_user=user.objects.get(user_code=student_id,task=2)
a=course_passes(id+1,selected_course[0],selected_course[1],find_user.id)
thanks for your comment.

Using the Django URL Tag in an AJAX Call

There are LOTS of post and pages discussing the use of Django and AJAX, and I've read hundreds over the past day or so looking for the answer to this question. A quick overview:
May of the examples show a hard-coded URL like this:
$.post("/projects/create/", {"name" : name}, function(data) {...
or some use the URL template tag, but with no parameters:
$.post("{% url create_project %}", {"name" : name}, function(data) {...
However, I'd like to include a Django-style parameter in a URL. Here's my url definition:
url(r'ajax/entity_name/(?P<pk>\w+)/$',EntityAjaxView.as_view(),name='entity_name'),
Yes, I'm using a class based view, and it is based on DetailView. This view looks by default for a pk value to be provided in the URL, and in a normal template I would use:
{% url entity_name id_number %}
to provide a link. In my code, I want to grab the value entered in an input box for the pk value. Here is a snippet of my JavaScript (which doesn't work):
var id_number = $('#id_endowmententity_set-' + rownum + '-id_number').val()
$.ajax({
type: "GET",
url: '{% url entity_name id_number %}',
So, my question is, can I use the URL template tag with a value from an input box?
(I know that I could use POST instead of GET and pass the id_number in the POST data, but that won't work well with the DetailView.)
Django is a server-side application. Javascript is client-side. Django templates get rendered on the server, so {% url entity_name id_number %} is evaluated on the server side, and then it's value is returned to the client. Just because of this, it's impossible for you to combine Django templates with javascript. However there are couple of things you can do to solve your problem.
Since you are making an ajax call, and the ajax call depends on some user input, usually the best route for the client to send any type of user input to the server is by either using querystring (thing after ? in the URL) or by sending a POST data. So the simplest thing is to change your your url not to include the pk in the url, but for the view to get that as part of GET or POST data.
url(r'ajax/entity_name/$', EntityAjaxView.as_view(), name='entity_name'),
and the view (sorry I'm not familiar with class based views):
def entity_name(request):
pk = request.GET.get('pk')
...
That seems to me to be the most elegant solution. If however you absolutely need to construct the url on the client side, you can generate a template url on the server side and then replace whatever parts you need on the client side to get the full url. This however requires more maintenance and therefore is more error prone. Simple js example of this approach:
var id_number = $('#id_endowmententity_set-' + rownum + '-id_number').val(),
url = '{% url entity_name 0 %}'.replace('0', id_number);
$.ajax({
type: "GET",
url: url,
...
});
It is possible to set an Ajax url on the element you are selecting using an attribute and it will behave like Django urls. Importantly, you can even access the url in Javascript file. I use it a lot
HTML
<div class="card-body" id="js-products" data-url="{% url 'chart-data' %}">
<div class="chart-area">
<canvas id="testChart"></canvas>
</div>
</div>
Note: the data-url attribute set on parent div
JAVASCRIPT
$(document).ready(function () {
var endpoint = $("#js-products").attr("data-url");
var defaultData = [];
var labels = []
$.ajax({
method: 'GET',
url: endpoint,
success: function (data) {
labels = data.labels
defaultData = data.data_default
setChart()
},
error: function (error_data) {
console.log(error_data)
}
})
function setChart() {
var ctx = document.getElementById('testChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
responsive: true,
data: {
labels: labels,
datasets: [{
label: 'Monthly Performance',
data: defaultData,
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
}
});
DJANGO VIEWS
Am using django rest framework class view but you can use either of function or class based view
class ChartData(APIView):
authentication_classes = []
permission_classes = []
def get(self, request, format=None):
labels = ['Products', 'User', 'May']
data_default = [SeedProduct.objects.all().count(),
User.objects.all().count(), 4]
data = {
'labels': labels,
'data_default': data_default,
}
return Response(data)
DJANGO URLS:
import the view class from views
path('api/chart/data', views.ChartData.as_view(), name="chart-data"),
It's pretty time consuming to go round trip to a server just to fetch a URL. The best strategy to keep URLs dry and avoid this is to generate javascript that emulates Django's native url reverse function and then serve that code statically with the rest of your client side JS.
django-render-static does just that.
This worked for me.
my URL was:
path('myurl/<str:type>', views.myfunction, name='myfunction')
my views.py file:
def myfunction(request,type):
return render(request, "payment.html", context)
In my template, I solved the issue by:
<button type="button" class="btn"
onclick="myfunction('forward');">My Button Name
</button>
<script>
function myfunction(type){
let url = "{% url 'appName:myfunction' 'ok' %}".replace('ok', type);
$.ajax({
method: 'POST',
url: url,
data: {
csrfmiddlewaretoken: '{{ csrf_token }}'
}
});
}
</script>

Categories