AJAX Routing to Django URLs (Using Django 2.0 "path" urlpatterns) - ajax

This used to work before Django 2.0 changed url patterns from "url" to "path":
index.html
<!DOCTYPE html>
{% load static %}
<head>
<script type="text/javascript" src="{% static 'main/js/jquery-3.3.1.js' %}">
</head>
<body>
<div id='test'>
<p><button class="btn">Click Here!</button></p>
</div>
<script>
$('.btn').click(function(){
console.log('button is clicked!')
$.ajax({
url: 'main/all_json',
sucess: function(serverResponse){
console.log('success.serverResponse', serverResponse)
}
})
});
APP LEVEL urls.py
urlpatterns = [
url(r'^all_json$',views.all_json, name="all_json")
]
Project Level urls.py
app_name= "main"
urlpatterns = [
path('', include ('apps.main.urls', namespace='main')),
path('admin/', admin.site.urls),
]
views.py
def all_json(request):
return HttpResponse ('hello world!')
But now, Django 2.0 uses "path" instead of the url regex pattern. When I use path:
app_name= "name"
urlpatterns = [
path('all_json',views.all_json, name="all_json"),
]
I get the:
GET http://127.0.0.1:8000/main/all_json 404 (Not Found)
I looked in the new documentation and release notes and there are some SO answers that explain how to use it SO post 1 & SO post 2. That has been useful up to this point, where I'm unable to pass the url from the AJAX function to the "path".
I'm new to AJAX and I'm used to using the {% url main:all_json %} in Django for my actions. But with AJAX I believe I can't use this notation. Is that right?
And for some reason, the examples that I have that used url(r'^$') urlpatterns before Django 2.0 worked, but now I get a code 404 when using 'path'. Most of the questions and tutorials available are pre Django 2.0 and use url(r'^$') urlpatterns. Release notes and documentation do not mention anything about differences in working with AJAX.
My questions is the following:
Is there something else that I need to add in my template and/or urls.py to help it find the urls(get rid of the 404)?

First, url is still perfectly valid in Django 2.0. In later versions exactly the same functionality is available as re_path.
However, the problem is not there. It is that you have added a final slash in the new version where you didn't have one before. Remove it:
path('all_json', ...)
or, preferably, add it to the Ajax call:
url: 'main/all_json/',
Note finally that since the Ajax script is directly in the template file, it's absolutely possible to use the {% url %} tag there.

Try to build API's that clear and useful. Using main, all_json names are unclear. Nevertheless, let's try on your examples:
In your urls.py use main/all_json/ and name="all_json". Accoding to documentation:
There’s no need to add a leading slash, because every URL has that. For example, it’s articles, not /articles. link
...each pattern requires that the URL end with a slash. link
In your HTML template (by the way, it's maybe mistake, but you named it html.py. I advise to refactor this to somename.html), in the js block use template tag {% url "all_json"" %} like:
$.ajax({
url: '{% url "all_json" %}',
sucess: function(serverResponse){
console.log('success.serverResponse', serverResponse)
}
})
By using url template tag you can avoid many mistakes when changing urls.

Related

Mixed Content on Vue-select Component

this is a bit of a weird one.
On my local development environment, this works perfectly, but once I go on a staging or live server - things don't go as smooth.
I am using the https://vue-select.org/ component for Vue.js to pull through options based on user input. When the select textarea is changed, I debounce the method and fire the string off to an API and pull back any results that are relative to the user's input. This then gets populated into an 'options' array which the select dynamically updates and uses.
The API link is specified using a variable from my .env file. On my local environment using Laravel Valet, this works fine.
When the site gets switched over to a live server, things get interesting. When I try and input a value into the select field, I get this result:
Mixed Content: The page at 'https://example.com/cv/1fa2383/edit' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example.com/api/job_title_names'. This request has been blocked; the content must be served over HTTPS.
Now, you would assume that this was because the request is being loaded over HTTP, not HTTPS. This is the weird part. Nowhere hardcoded, or defined as a variable is there HTTP requests, only HTTPS. I've quadruple tested this and re-written and even hard-coded HTTPS links but for some reason, the component keeps trying to load the request over HTTP.
It's really odd because it tries to fire two requests. One is a GET and the other is a POST. The POST is what my Axios script should be doing, but the GET request gets added for some unknown reason. There is no code built for a get request on this component as you will see below.
I'm at my witts end with this. Here's some information regarding my components and element references:
My env variables: (.test is my dev tld).
test is my dev tld
/api/ is what most of my axios requests go through.
APP_URL=http://example.test
APP_API_URL=http://example.test/api/
My input:
<label>
<span class="block font-bold mb-2 text-grey-500 text-sm">Job Title <i class="fad fa-question-circle leading-none text-blue-400" v-tooltip.top-center="'What was your role in this company?'"></i></span>
<v-select taggable :options="jobTitleOptions" #search="fetchJobTitleOptions" v-model="newEmployment.jobTitle" :filterable="false" class="dynamic-select" placeholder="Type a Job Title">
<template slot="no-options">
Type to find your job title...
</template>
</v-select>
</label>
My methods:
fetchJobTitleOptions (search, loading) {
loading(true);
this.searchJobTitles(loading, search, this);
},
searchJobTitles: _.debounce((loading, search, vm) => {
console.log('Job Title Url: ' + vm.jobTitleOptionsUrl);
if(search != '') {
axios.post(vm.jobTitleOptionsUrl, {
name: escape(search)
})
.then(response => {
let data = response.data;
vm.jobTitleOptions = data;
loading(false);
});
} else {
loading(false);
}
}, 500),
Computed:
jobTitleOptionsUrl: function() {
return this.url + 'job_title_names/'
},
My component reference:
apiurl is the value of APP_API_URL assigned to a different vue variable.
<edit-employment :apiurl="apiurl" :cvid="this.cv_id"></edit-employment>
Blade template:
#extends('layouts/app')
#section('content')
<edit-cv url="{{ env('APP_URL') }}" api="{{ env('APP_API_URL') }}" cvid="{{ $cv_id }}" urlslug="{{ $url_slug }}"></edit-cv>
#endsection
Using Vue tools, I can see that all the links are being referenced correctly, not one has HTTP prefixed before them.
I am running Nginx on my local environment, but Apache is running on my server. I'm not sure if that could help with some diagnosis?
Steps I've taken to try and solve this:
I have flushed all cache from the Laravel side of things successfully
I have returned the variables in Laravel and can confirm they return correctly.
I have re-written most variables to ensure that they're 100% correct
I have checked package versions to see if there are conflicts, of which there are not.
Any help would be greatly appreciated.
Thank you
I fixed this by double-checking my routes. When working with APIs and specifically Axios in Laravel, if you have a trailing / at the end of the route, this causes the request to 301. After removing the trailing slash, everything worked as it should.
So this route:
return this.url + 'job_title_names/'
Becomes this:
return this.url + 'job_title_names'

Django template reload div with javascript - url tag

Hi i just wanted to reload part of data on the website using ajax.
Website is created using template language from Django.
I have problem with using url tag inside javascript tags, code below.
urls.py
urlpatterns = [
url(r'^(?P<place_id>[\w]+)/$', place_views.place_website,
name='room_view'),
]
Website
<script type="text/javascript">
setInterval(function () {
$("#displayMoment").load("{% url room_view room_id %}");
}, 60000);
</script>
Legend
room_view is the name of the view inside views.py
room_id is the room id which is captured by regex in url pattern. But it should be in double bracket right ? {{ room_id }} ?
displayMoment is the id of the DIV.
Problem
With this i get error
NoReverseMatch at /places/2/
Reverse for '' not found. '' is not a valid view function or pattern name.
Ho does the website part should look like ?

Blade templating with angular2 templating?

I have problem because im using angular with laravel.I added this in routes.php
Blade::setContentTags('<%', '%>'); // for variables and all things Blade
Blade::setEscapedContentTags('<%%', '%%>'); // for escaped data
But its not working because im getting an error when i display data from angular.
Any suggestion how can i fix this?
For example if i say:
{{'test'}} it works but if i say {{response.test}} where response is from angular i get an error because laravel thinks that is his.
Blade will ignore anything preceded by the # character. Try that.
first confing laravel blade and angular js
<script type="text/javascript">
var app = angular.module('myApp', [])
.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('<%=');
$interpolateProvider.endSymbol('%>');
});
</script>
write your angular code like this
<%= cs.Category_name %>

django + angularjs resource + ajax POST = 500 (Internal Server Error)

I can do GET requests, but when I do POST, in Chrome developer tools I see: "Failed to load resource: the server responded with a status of 500 (INTERNAL SERVER ERROR)"
I thought the problem is in Django's csrf_token, so I found this solution:
.config(function($httpProvider){
$httpProvider.defaults.headers.common['X-CSRFToken'] = CSRF_TOKEN;
});
In my index.html, in <head> I have:
<script>
CSRF_TOKEN = '{{ csrf_token }}';
</script>
But it still raises 500 error. Am I doing something wrong or the problem is not in csrf?
P.S. CSRF_TOKEN is declared before
<script src="{{ STATIC_URL }}lib/angular/angular.js"></script>
and other scripts.
I've figured out the problem.
Django by default appends slash to your URL. If you enter:
http://mydjangosite.com/page Django will redirect you to: http://mydjangosite.com/page/
Angular's $resource removes trailing slash (you can read about it on github: https://github.com/angular/angular.js/issues/992).
Django has APPEND_SLASH setting which uses HTTP 302 redirection to
append slash to urls without slash. This works with GET method but not
with others (POST,PUT,DELETE) because redirection cannot and will not
pass the data to the new URL
So, there are two options:
1) Use $http insread of $resource
or
2) In Django's settings.py add this line:
APPEND_SLASH = False
and in your urls.py remove all trailing slashes
simply escape the backslash like: /custom_api/get_nearest_hotels/:eventId\/
(From: http://pragmaticstartup.wordpress.com/2013/04/27/some-lessons-learnt-from-messing-with-django-and-angularjs/)
As you all know you need to dump dictionary in your HTTPRESPONCE object.
sometimes what happens, in your view; you try to dump something in to your dict that can not be serialized. that is python/django can not serialize that object.
the examples can be (FORM OBJECT), (MODEL OBJECT), etc
so you need to get those away.
context = {}
context['office_form'] = OfficeCompleteForm(request.POST)
this can not be serialized and you will get 500 error.
be free to add following data.
context['success'] = {"msg": "successfully updated. "}
context['error'] = {"msg": "error can not update. "}
and at last do not forget to call you response method like this.
return HttpResponse(json.dumps(context), content_type="application/json")

How do I render a view after POSTing data via AJAX?

I've built an app that works, and uses forms to submit data. Once submitted, the view then redirects back to display the change. Cool. Django 101. Now, instead of using forms, I'm using Ajax to submit the data via a POST call. This successfully saves the data to the database.
Now, the difficult (or maybe not, just hard to find) part is whether or not it's possible to tell Django to add the new item that has been submitted (via Ajax) to the current page, without a page refresh. At the moment, my app saves the data, and the item shows up on the page after a refresh, but this obviously isn't the required result.
If possible, I'd like to use exactly the same view and templates I'm using at the moment - essentially I'd like to know if there's a way to replace a normal HTTP request (which causes page refresh) with an Ajax call, and get the same result (using jQuery). I've hacked away at this for most of today, so any help would be appreciated, before I pull all of my hair out.
I had a very similar issue and this is how I got it working...
in views.py
from django.utils import simplejson
...
ctx = {some data to be returned to the page}
if ajax == True:
return HttpResponse(simplejson.dumps(ctx), mimetype='json')
then in the javascript
jQuery.ajax({
target: '#id_to_be_updated',
type: "POST",
url: "/",
dataType: 'json',
contentType: "text/javascript; charset=\"utf-8\"",
data: {
'foo':foo,
'bar':bar,
},
success: function(data){
$("#id_to_be_updated").append(data.foo);
}
});
Here's how I did it:
The page that has the form includes the form like so
contact.html
{% include "contact_form.html" %}
This way it's reusable.
Next I setup my view code (this view code assumes the contact form needs to be save to the db, hence the CreateView):
class ContactView(CreateView):
http_method_names = ['post']
template_name = "contact_form.html"
form_class = ContactForm
success_url = "contact_form_succes.html"
There are a few things to note here,
This view only accepts pots methods, because the form will be received through the contact.html page. For this view I've setup another template which is what we included in contact.html, the bare form.
contact_form.html
<form method="POST" action="/contact">{% crsf_token %}
{{ form.as_p }}
</form>
Now add the javascript to the contact.html page:
$("body").on("submit", 'form', function(event) {
event.preventDefault();
$("#contact").load($(this).attr("action"),
$(this).serializeArray(),
function(responseText, responseStatus) {
// response callback
});
});
This POSTS the form to the ContactView and replaces whatever is in between #contact, which is our form. You could not use jquery's .load function to achieve some what more fancy replacement of the html.
This code is based on an existing working project, but slightly modified to make explaining what happens easier.

Resources