Updating a variable length html list via ajax - python

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
}
}
}

Related

AJAX request returns HTML file instead of data. ( Django & Python )

I have a profile page with user posts. People can like/dislike the posts. It works well but it reloads the page, that is why I am implementing AJAX.
The route goes like this.
Inside the profile view is the "like POST" request ending with a
data = {
'likes':post.likes.all().count()
}
return JsonResponse(data,safe=False)"
When clicking on the like button, I can see the data on a blank page if I want to. So I know it is receiving it.
Unfortunately, when using AJAX, instead of returning the data. it returns the profile view's
return render(request, "profile.html", context)
Here is my AJAX code
const post_id = $(this).attr('id')
const likeText = $( `.like_button${post_id} `).attr('name')
const trim = $.trim(likeText)
const url = $(this).attr('action')
let res;
const likes = $(`.like_count${post_id}`).text() || 0
const trimCount = parseInt(likes)
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken':$('input[name=csrfmiddlewaretoken]').val(),
'post_id':post_id,
},
success: function(data){
console.log('success', data)
},
error: function(data){
console.log('error', data)
}
})
Any help would be much appreciated. First time using AJAX, so as much details would be appreciated.
Not really surprising it's returning HTML since that's exactly what you're returning in the view: rendered HTML. If you want JSON you need then you want a JSONResponse object.
https://docs.djangoproject.com/en/4.1/ref/request-response/#jsonresponse-objects
So, instead of:
return render(request, "profile.html", context)
which will take the profile.html, inject the values from context and send you that as html, you should do something like:
response = JsonResponse({"mydata": "goes here", "some_more": data})
You can then parse this as JSON in your AJAX code. If it's the case that the context dictionary contains all the data you need and that is what you want, you can just swap out that one line:
response = JsonResponse(context)
Edit: To address the question in the comment. Suppressing the default form response in the frontend is not a Django thing, it's done with JS on the event using something like:
e.preventDefault()
See here for info: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

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

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.

How to remove key from request QueryDict in Django?

Here is one of my Django views:
def index(request):
message = request.GET.get('message', '')
context = RequestContext(request, {
'message': message
})
return render(request, 'bets/index.html', context)
My goal is to display message in template only once, and not to display it on page reload. I tried this:
request.GET['message'] = ''
but get error "This QueryDict instance is immutable". How do I remove key from QueryDict?
Even if you could remove that value from the querydict, that wouldn't help because it is based on the URL you've used to request that view, so when you refresh you're going to be using the same URL again with the existing parameters.
Rather passing the message value in the GET parameters, put it in the session, and use request.session.pop('message') in the index view.
Even better, use the built-in messages framework which does all that for you.
#Daniel Rosemans answer is correct, in that you should use session to store value.
However, to answer your original question in regards how to remove, GET and POST parameters are immutable. You can not change these querydicts. If you want to remove something from them (say, to prevent key from being iterated over) you need to make copy of the QueryDict and then pop item from it.
https://docs.djangoproject.com/en/3.1/ref/request-response/#querydict-objects
The QueryDicts at request.POST and request.GET will be immutable when
accessed in a normal request/response cycle. To get a mutable version
you need to use QueryDict.copy().
If you don't want to fix this problem in frontend, and still need Django's components like forms, this snippet may help.
fetch('', {
"method": "POST",
"headers": {
"X-CSRFToken": csrfToken,
"content-type": "application/json",
},
"body": JSON.stringify({
"csrfmiddlewaretoken": "{{ csrf_token }}",
"username": document.querySelector("#id_username").value,
"password": document.querySelector("#id_password").value,
})
}).then(res => res.json()).then(data => processData(data))
def fix_fetched_post(request):
try:
# fetch
post_data = json.loads(request.body.decode("utf-8"))
request.POST._mutable = True
for key, value in post_data.items():
request.POST[key] = value
request.POST._mutable = False
except:
# form
pass
return request

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