python url not found - Accessing views.py from AJAX - python

New to Python and Django and I'm trying to make a simple ajax call from a button click to pass certain data to my views.py, however, when I try to make a url as seen on my ajax code below, the documentId.id does not append unless I directly append in without the "?id=".
{%for document in documents%}
{{document.filename}}
<input type="button" id="{{document.id}}" onclick="loadData(this)" name="load-data" value="Add"/>
{%endfor%}
<script type ="text/javascript">
function loadData(documentId){
$.ajax({
url:"upload-data/load" + "?id=" + documentId.id,
data: {'documentId': documentId},
type: 'GET',
success: function(){
window.location.href = "http://127.0.0.1:8000/url/locations";
}
});
}
</script>
This gives me then an error that says the url cannot be found. I have a line in my urls.py below:
url(r^"upload-data/load/([0-9]+)/$', views.loadFile, name="load-data"),
Other than this method, I am stumped as to how I am going to extract my data to my views.py.
def loadFile(request):
documentId = request.GET.get('id')
newLayer = Layer(get_object_or_404(Document, pk = documentId))
newLayer.save()
layers = Layer.objects.all()
return render(request, 'url/loaded.html', { 'layers': layers})
The persisting error in the console would be:
http://127.0.0.1:8000/upload-data/load/ [HTTP/1.0 404 Not Found]

Use something like this:
def loadFile(request):
documentId= request.GET.get('id', '').
newLayer = Layer(get_object_or_404(Document, pk = documentId))
newLayer.save()
layers = Layer.objects.all()
return render(request, 'url/loaded.html', { 'layers': layers})
And update your url as :
url(r^"upload-data/load/', views.loadFile, name="load-data")
And the script would be like :
<script type ="text/javascript">
function loadData(documentId){
$.ajax({
url:"upload-data/load/?id="+ documentId.id,
data: {'documentId': documentId},
type: 'GET',
success: function(){
window.location.href = "http://127.0.0.1:8000/url/locations";
}
});
}
</script>
Thanks.

In JavaScript you need
"upload-data/load/" + documentId.id
Django doesn't use ?id= in url definition r^"upload-data/load/([0-9]+)/$'. It expects ie. upload-data/load/123 instead of upload-data/load?id=123
EDIT: and you need id in def loadFile(request, id).
And then you don't have to use request.GET.get('id')

From the above answers and comments it seems like rather than passing id as a url param you want to pass the same as a get param. In that case make your urls like below.
url(r^"upload-data/load/', views.loadFile, name="load-data")
and in views, check for get params by replacing id with documentId. document id will be in your dict named as data passed to view. So look for request.GET.get('data','') and from data extract id as below
def loadFile(request):
data = request.GET.get('data', None)
if data:
documentId = data['documentId']
newLayer = Layer(get_object_or_404(Document, pk = documentId))
newLayer.save()
layers = Layer.objects.all()
return render(request, 'url/loaded.html', { 'layers': layers})
else:
return JsonResponse({'error': 'pass document id'}, status=400)
Since you are passing a get param from javascript named as documentId not id.
HTH

Related

Django AJAX: Errors When Trying To Pass A List In POST Request

I am trying to pass a list to the view so that i can filter by that list. i have taken some ideas from another Stack Overflow question here which should be working but is giving me some errors.
attempt 1 shown below prints as an empty array []
attempt 2 shown below gives error the JSON object must be str, bytes or bytearray, not NoneType
the ajax code consol log error Array [1] so the list works there
EDIT: I know the error is that the data being passed is NoneType, which means that the list with the values inside isn't being passed through. I'm getting a error log from the AJAX call so there must be an error there. Can anyone point out where I'm wrong?
Here is the code
view
def find(request):
Posts = Post.objects.all()
genre_list = Genres.objects.all()
if request.method == 'POST':
#attempt 1
genres_selected_use = request.POST.getlist('genres_selected') # this when printed shows an empty array []
#attempt 2
#genres_selected_use = json.loads(request.POST.get('genres_selected')) # this gives error *the JSON object must be str, bytes or bytearray, not NoneType*
for arg in genres_selected_use:
Posts = Posts.filter(genres=arg)
print(genres_selected_use)
#return redirect('find')
context = {
'products': Posts,
'keyword_list': Keywords.objects.all(),
}
return render(request, 'find/find.html', context)
AJAX
$('.filter_form').submit(function(e){
const url = $(this).attr('action')
var genres_selected_initial = [];
var genre_item = document.getElementById(`filter_idAction`);
var genres_selected_id = $(genre_item).attr('value');
var g = parseInt(genres_selected_id)
if (genre_item.classList.contains("clicked_filter")) {
genres_selected_initial.push(g);
}
//var genres_selected = genres_selected_initial; //attempt 1
var genres_selected = JSON.stringify(genres_selected_initial); //attempt 2
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
'genres_selected': genres_selected,
},
success: function(response){
console.log(genres_selected)
},
error: function(response){
console.log('error', genres_selected)
}
})
})
form
<form class="filter_form" action="{% url 'find' %}" method="POST">
{% csrf_token %}
#rest of form
</form>
I am guessing there is an easy fix for this?
Try using request.POST.getlist('genres_selected', request.POST.getlist('genres_selected[]'))
Sometimes the ajax field will append "[]" appended to the field name when it is given a list.
For anyone with a similar problem
it was solved by adding e.preventDefault()

Django child model data not saving with Ajax raising ValueError: Data didn't validate

I am trying to save data in a pair of related models in my Django application using Ajax. Though the parent table data is saved, I am unable to save data in the child model. The system gives off the following error message:
Error: ValueError: The mappedTargFields could not be created because the data didn't validate.
How do I save data in child table as well? Even with extensive search I am unable to find a solution to save inline formsets data (which I normally do using CBV + management form). Or is it not possible to save formset data using jQuery/Ajax?
Given below are the relevant codes:
views.py
def SaveMapAjax(request, object_id=False):
if request.method == 'POST' and request.is_ajax():
qs_targ_model_form = mappedTargModelForm(request.POST)
target_field_formset = CreateMappedTargFieldsFormset(request.POST)
if qs_targ_model_form.is_valid():
qs_targ_model_form.save()
target_field_formset = CreateMappedTargFieldsFormset(request.POST, instance=qs_targ_model_form, prefix='')
target_field_formset.save()
msg='Data saved!!!'
return JsonResponse(msg, safe=False)
else:
msg= 'Error occured in save!'
else:
qs_targ_model_form = mappedTargModelForm()
target_field_formset = CreateMappedTargFieldsFormset(instance=qs_targ_model_form)
return JsonResponse(msg, safe=False)
Formset
CreateMappedTargFieldsFormset = inlineformset_factory(
mappedTargModel, # Parent table
mappedTargFields, # Child table
form=mappedTargFieldsForm, # Modelform on Child table
extra=2,
can_delete=False, min_num=1, validate_min=True)
Template
<!--html-->
<form action="" method="POST" class="form" id="dataMapperForm">
{% csrf_token %}
{{ target_model_form.as_p }}
<div class="col-md-12 text-nowrap" style="font-family:'Courier New'">
{{ target_field_formset.management_form }}
</div>
</form>
<!--The ajax part-->
<script>
$(function() {
$('#dataMapperForm').on('submit', function(e) {
var frm = $('#dataMapperForm');
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url 'save_mapper' %}',
data: frm.serialize(),
success: function(data) {
console.log('Success!!');
$("#dataMapperForm")[0].reset();
},
error: function(data) {
console.log('Error');
},
});
});
});
</script>
Edit:
Following is the error it is throwing on target_field_formset.errors:
[{'mapper_item': ['Select a valid choice. That choice is not one of the available choices.'], 'mapper_header': ['The inline value did not match the parent instance.']}, {'mapper_item': ['Select a valid choice. That choice is not one of the available choices.'], 'mapper_header': ['The inline value did not match the parent instance.']}, {'mapper_item': ['Select a valid choice. That choice is not one of the available choices.'], 'mapper_header': ['The inline value did not match the parent instance.']}]
Edit2:
On printing the serialized data to the console I am getting the follwoing:
&mapper_doc_type=MAPPER // Parent model field
&mapper_name=Test // Parent model field
&mapper_target_model=employee // Parent model field
&mapper_hdr_tab-TOTAL_FORMS=3
&mapper_hdr_tab-INITIAL_FORMS=0
&mapper_hdr_tab-MIN_NUM_FORMS=1
&mapper_hdr_tab-MAX_NUM_FORMS=1000
&mapper_hdr_tab-0-mapper_item=name // Child model "pk"
&mapper_hdr_tab-0-mapper_header=name // Child model "fk" field
&mapper_hdr_tab-0-mapped_field=name // Child model field
&mapper_hdr_tab-0-mapped_field_col_name=NAME
To my mind, value assignment to mapper_item should be an integer and not name as it shows above. Similarly for the next item mapper_header which should be the primary key of the parent table and not name.
Something I am doing here is very wrong.
you check that form is_valid, but don't check that formset is_valid
if qs_targ_model_form.is_valid():
qs_targ_model_form.save()
target_field_formset = CreateMappedTargFieldsFormset(request.POST, instance=qs_targ_model_form, prefix='')
if target_field_formset.is_valid():
target_field_formset.save()
msg='Data saved!!!'
return JsonResponse(msg, safe=False)
else:
msg = 'Formset is not valid'
else:
msg= 'Error occured in save!'

Sending data from a Django template to views.py via Ajax

I am sending some data from html template to views.py via ajax.From the id sent to views.py I am creating sql records. However I was wondering if there is any way to send data from views.py to template to notify that the data is added to sql.
code-
$('#tn1').click(function(){
var msg='';
alert('inside alert');
if ($('textarea#message') != "") {
var message = $('#notesarea').val();
alert(message);
msg=message;
}
$.ajax({
url: 'post_note',
data: {
'note': msg
},
success: function (data) {
alert(data)
}
});
views.py
def post_note(request,id):
post_id = request.GET['note']
print(post_id)
//sql insertion code,once its done i want to notify to the front end..print some alert message.
return render(request, './profile.html')
You should use something like JSONResponse in your view, then you data will appear in success function
success: function (data) {alert(data)}
You can do this using JQuery Ajax in the template and by creating an "API view" in your views.py that is basically just a regular view that returns a JSONResponse after checking to verify the request is Ajax. As an example of the "API" option, using JQuery:
In your views.py file (using the GET method which you should only use if the note is short, will fit into the URL bar, and if you don't have major security concerns, otherwise see the POST example at bottom):
from django.http import JsonResponse
def post_note_api(request):
data = {}
if request.GET.get('post_note', None) is not None:
post_note = request.GET.get('post_note')
# save the note and indicate success
data['result'] = True
data['message'] = "Note posted successfully"
...
if request.is_ajax():
return JsonResponse(data)
else:
return HttpResponseBadRequest()
In your urls.py:
...
path('/api/post_note/', post_note_api, name="post_note_api"),
...
In your template (if you are using the GET method):
<script type="text/javascript">
$("#tn1").click(function(){
var message = $("#myTextArea").val();
$.ajax({ url: '{% url 'post_note_api' %}?post_note=' + message,
type: "GET",
dataType: "json",
cache: false
}).done(function(data) {
if (data.result === true){
alert(data.message);
}
});
});
});
</script>
If you are using the POST method instead of GET (which is probably the better option here):
<script type="text/javascript">
$("#tn1").click(function(){
var csrfToken = $( "input[name='csrfmiddlewaretoken']");
var message = $("#myTextArea").val();
$.ajax({ url: '{% url 'post_note_api' %}',
type: "POST",
dataType: "json",
data: {'post_note':message, 'csrfmiddlewaretoken':csrfToken.val()},
cache: false
}).done(function(data) {
if (data.result === true){
alert(data.message);
}
});
});
});
</script>
For the POST method, in your view just change request.GET.get('post_note') ... to request.POST.get('post_note') ... like so:
from django.http import JsonResponse
def post_note_api(request):
data = {}
if request.POST.get('post_note', None) is not None:
post_note = request.POST.get('post_note')
# save the note and indicate success
data['result'] = True
data['message'] = "Note saved successfully"
...
if request.is_ajax():
return JsonResponse(data)
else:
return HttpResponseBadRequest()
When you are sending data via POST don't forget to pass along your CSRF token as in the example above. This assumes you have a form on the page you can get it from, otherwise you can use something like this to get it:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
If you don't want to deal with the CSRF token, you can mark the view with the #csrf_exempt decorator and remove the 'csrfmiddlewaretoken' data element from the Ajax call in the template, but it may not be ideal or the most secure. An example of that:
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
#csrf_exempt()
def post_note_api(request):
...
Now, without knowing more, this is basically just pseudocode (plus I just wrote this off the top of my head so it may have errors). If you post more details I can update my answer, but I think this should get you started.
I have used below for sending data from HTML to views.py and then return a success response back to HTML. Hope this can be helpful:)
HTML Code:
<button class="button primary fit small" onclick="saveContent()">Save</button>
Javascript Code:
<script>
function saveContent(){
var code =editor.getSession().getValue();
var URL = "{% url 'save' %}";
var data = {'code': code};
$.post(URL, data, function(response){ // This is the main function that will help you
if(response === 'success'){ window.location.reload(); }
else{ alert('Error! :('); }
});
}
</script>
View.py:
def save_content(request):
if request.method == 'POST':
if 'code' in request.POST:
file_data = request.POST['code']
return HttpResponse('success')

Is there any way to access the variable inside a function from another function within the same class in TemplateView of django

$.ajax({
'url' : '{% url 'add_question_main' %}',
'type' : 'GET',
'data' : {
'num' : num
},
'success' : function(data) {
if (data == "success") {
alert('request sent!');
}
}
});
how to access the variable within a function from another function within the same class in django's TemplateView
class add_question_main(TemplateView):
template_name = 'add_question_paper/index.html'
def get(self,request,*args,**kwargs):
form = question_details_form()
no_txt = request.GET.get('num')
return render(request,self.template_name,{'form':form})
def post(self,request,*args,**kwargs):
form = question_details_form(request.POST)
return render(request,self.template_name,{'form':form})
I wonder how could I use the value of no_txt in function post?
because the get function does not get called when
a POST is issued, your best bet is to get that
same value independently by refactoring the call
into a helper method.
Option #1:
def get_no_txt(self):
return self.request.GET.get('num')
def get(self,request,*args,**kwargs):
form = question_details_form()
no_txt = self.get_no_txt()
return render(request,self.template_name,{'form':form})
def post(self,request,*args,**kwargs):
form = question_details_form(request.POST)
no_txt = self.get_no_txt()
return render(request,self.template_name,{'form':form})
Whether this will do what you want it to will also
depend on your template and what data you are submitting
with the form. In particular, your action attribute on your
form should have the num query parameter in it.

Pagination with appengine ndb Cursor - python : Same cursor is being generated leading to repeatition of output results

I realised from my appengine log that the same cursor is being generated each time, the call to the Homehandler is made.
Please any idea what i am doing wrong, below is a snippet of my code:
class HomeHandler(webapp2.RequestHandler):
def get(self):
#page=self.request.get("page", default_value="1");
q = Allnews.query().order(-Allnews.date_added)
cursor = ndb.Cursor(urlsafe=self.request.get('cursor',default_value=None))
items, next_curs, more = q.fetch_page(30, start_cursor=cursor)
if more:
next_c = next_curs.urlsafe()
else:
next_c = None
context = { "news":items,"cursor":next_c}
# context["headlines"]=Feed.query(Feed.feed_cat.title == 'Headlines')
# context["gossip"] = Feed.query(Feed.feed_cat.title == 'Gossip')
# context["sports"] = Feed.query(Feed.feed_cat.title == 'Sports')
self.response.out.write(template.render('templates/homefeed.html',context))
this is the section of my homefeed.html template, I m using 'infinite scrolling' technique to fetch more results
<script>
{% if cursor %}
$(window).scroll(function()
{
var src=$("#src_val").val();
if($(window).scrollTop() == $(document).height() - $(window).height())
{
$('div#loadmoreajaxloader').show();
$.ajax({
url:"/home",
type:'GET',
data: {cursor: '{{cursor}}',feed_id:src },
success: function(news)
{
if(news)
{
$("#wrapper").append(news);
$('div#loadmoreajaxloader').hide();
}else
{
$('div#loadmoreajaxloader').html('No more posts to show.');
}
}
});
}
});
{% endif %}
</script>
It looks like you're using the get() method for both displaying a page and to handle an AJAX request. It's correctly generating a page with an initial cursor, but your $.ajax() method is expecting it to return JSON data.
Split the page request and AJAX request into two methods. Try adding a post() method to your HomeHandler that returns JSON data like this:
import json
def post(self):
q = Allnews.query().order(-Allnews.date_added)
cursor = ndb.Cursor(urlsafe=self.request.get('cursor',default_value=None))
items, next_curs, more = q.fetch_page(30, start_cursor=cursor)
if more:
next_c = next_curs.urlsafe()
else:
next_c = None
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps({'news': items, 'cursor': next_c))
Now you have a method that returns JSON data to the AJAX request:
<script>
var cursor = null;
$(window).scroll(function()
{
if($(window).scrollTop() == $(document).height() - $(window).height())
{
$.ajax({
url:"/home",
type:'POST',
data: {cursor: cursor},
success: function(data)
{
$("#wrapper").append(data['news']);
cursor = data['cursor'];
if ( !cursor )
$('div#loadmoreajaxloader').show().html('No more posts to show.');
}
});
}
});
</script>
Notice how the cursor value is updated on each AJAX request. This is how you will get a new cursor on the next request.
(This code was not tested, you may need to debug it.)

Categories

Resources