I am making a website using Django and I want to pass a python object from my view (where it is created) through the Django template and to a Dajax call. The problem is that by the time it gets to dajax it has been turned into type unicode.
In my Template
<script>
var emailer = "{{emailer|safe}}"; <---If I omit the quotes here then I get a javascript error.
sessionStorage.setItem('emailer',emailer);
$(document).ready(function(){
$('.send').on('click', function(e){
var emailer = sessionStorage.getItem('emailer');
Dajaxice.InterfaceApp.sendEmail(submitverify,{'emailer':emailer});
});
});
</script>
The dajax function
#dajaxice_register(method='GET')
def sendEmail(emailer):
logger.warning("type: %s, %s" % (type(emailer),emailer))
email_body = "message"
emailer.addToMessage(email_body)
emailer.send()
message = "Email Sent"
return json.dumps({'message':message})
Here the logger statement returns: type: <type 'unicode'>, <Utils.SIMPL_Emailer instance at 0x103142ab8>. Is there any way to fix this so that I get my emailer object instead of a unicode string?
First try to understand what is happening:
On your template you're trying to save a Python object to a Javascript var:
var emailer = "{{emailer|safe}}";`
But it's not possible. When your template is rendered by Django what you really get is a call to object __str__() method and your Javascript will store the <Utils.SIMPL_Emailer instance at 0x103142ab8> value on your emailer var. And remember: this code run in the client browser. That's why you get an error when you remove the quotes.
To solve it you need to first serialize your emailer object (Turn it into something that could be represented as a String, for example, and then turned back to Python Object). But as pointed by Peter DeGlopper it is a very insecure approach. Never, ever deserialize an whole object that was public accessible. Instead send only the email data to your template. You can create a dictionary with this data, turn it into JSON (it's a serialization too, but this time you are serializating only data) and then pass it to your template.
So do not put your emailer on the template context. Instead create a dictonary and pass it to the template.
Then in your Python sendEmail(emailer) method you'll need to instanciate a new Emailer object and feed it with the data, like:
#dajaxice_register(method='GET')
def sendEmail(email_json):
email = json.loads(email_json) # email_json is a json with your email data only
logger.warning("type: %s, %s" % (type(email_json),email_json))
emailer = Emailer("<with all your params...>")
emailer.addToMessage(email.get('body'))
emailer.send()
message = "Email Sent"
return json.dumps({'message':message})
Related
I need to make HTML fill itself with content from JSON file using Mustache or Handlebars.
I created two simple HTML templates for testing (using Handlebars) and filled them with content from an external JavaScript file. http://codepen.io/MaxVelichkin/pen/qNgxpB
Now I need content to lay initially in a JSON file.
I ran into two problems, but they both lie at the heart of solutions of the same main problem - creating a link between the content in the JSON file and HTML, so I decided to ask them in the same question.
How can I connect JSON and HTML? As far as I know there is a way, using AJAX, and there's a way that uses a server. AJAX is a new language for me, so I would be grateful for an explanation of how can I do it, using local HTTP server, that I created using Node.JS.
What should be the syntax in a JSON file? The script in the JSON file must be the same, as a script in JavaScript file, but then it should be processed with the help of JSON.parse function, is that correct? Or syntax in JSON file should be different?
For example, if we consider my example (link above), the code for the first template in the JSON file must be the same as in the JavaScript file, but before the last line document.getElementById('quoteData').innerHTML += quoteData;, I have to write the following line var contentJS = JSON.parse(quoteData);, and then change the name of the variable in the last line, so it will be: document.getElementById('quoteData').innerHTML += contentJS;, Is it right?
Try this:
HTML:
<!-- template-1 -->
<div id="testData"></div>
<script id="date-template" type="text/x-handlebars-template">
Date:<span> <b>{{date}}</b> </span> <br/> Time: <span><b>{{time}}</b></span>
</script>
JS:
function sendGet(callback) {
/* create an AJAX request using XMLHttpRequest*/
var xhr = new XMLHttpRequest();
/*reference json url taken from: http://www.jsontest.com/*/
/* Specify the type of request by using XMLHttpRequest "open",
here 'GET'(argument one) refers to request type
"http://date.jsontest.com/" (argument two) refers to JSON file location*/
xhr.open('GET', "http://date.jsontest.com/");
/*Using onload event handler you can check status of your request*/
xhr.onload = function () {
if (xhr.status === 200) {
callback(JSON.parse(xhr.responseText));
} else {
alert(xhr.statusText);
}
};
/*Using onerror event handler you can check error state, if your request failed to get the data*/
xhr.onerror = function () {
alert("Network Error");
};
/*send the request to server*/
xhr.send();
}
//For template-1
var dateTemplate = document.getElementById("date-template").innerHTML;
var template = Handlebars.compile(dateTemplate);
sendGet(function (response) {
document.getElementById('testData').innerHTML += template(response);
})
JSON:
JSON data format derives from JavaScript, so its more look like JavaScript objects, Douglas Crockford originally specified the JSON format, check here.
JavaScript Object Notation has set of rules.
Starts with open curly braces ( { ) and ends with enclosing curly braces ( } )
ex: {}
Inside baces you can add 'key' and its 'value' like { "title" : "hello json"}
here "title" is key and "hello json" is value of that key.
"key" should be string
"value" can be:
number
string
Boolean
array
object
Can not add JavaScript comments inside JSON (like // or /**/)
there are many online JSON validators, you can check whether your JSON is valid or not, check here.
When comes to linking JSON to js file, its more like provide an interface to get JSON data and use it in your JavaScript.
here XMLHttpRequest our interface. we usually call XMLHttpRequest API.
In the given js code, to get JSON from the server using an REST API (http://date.jsontest.com/)
for more information on REST API you can check here
from the url: http://date.jsontest.com/ you can get JSON object like below.
{
"time": "03:47:36 PM",
"milliseconds_since_epoch": 1471794456318,
"date": "08-21-2016"
}
Note: data is dynamic; values change on each request.
So by using external API you can get JSON, to use it in your JavaScript file/ code base you need to convert JSON to JavaScript object, JSON.parse( /* your JSON object is here */ ) converts JSON to js Object
`var responseObject = JSON.parse(xhr.responseText)`
by using dot(.) or bracket ([]) notation you can access JavaScript Object properties or keys; like below.
console.log(responseObject.time) //"03:47:36 PM"
console.log(responseObject["time"]) //"03:47:36 PM"
console.log(responseObject.milliseconds_since_epoch) //1471794456318
console.log(responseObject["milliseconds_since_epoch"])//1471794456318
console.log(responseObject.date) //"08-21-2016"
console.log(responseObject["date"]) //"08-21-2016"
So to link local JSON file (from your local directory) or an external API in your JavaScript file you can use "XMLHttpRequest".
'sendGet' function updatedin the above js block with comments please check.
In simple way:
create XMLHttpRequest instance
ex: var xhr = new XMLHttpRequest();
open request type
ex: xhr.open('GET', "http://date.jsontest.com/");
send "GET" request to server
ex: xhr.send();
register load event handler to hold JSON object if response has status code 200.
ex: xhr.onload = function () {
for more info check here
Know about these:
Object literal notation
difference between primitive and non-primitive data types
Existing references:
What is JSON and why would I use it?
What are the differences between JSON and JavaScript object?
Basically, JSON is a structured format recently uses which would be preferred due to some advantages via developers, Like simpler and easier structure and etc. Ajax is not a language, It's a technique that you can simply send a request to an API service and update your view partially without reloading the entire page.
So you need to make a server-client architecture. In this case all your server-side responses would be sent in JSON format as RESTful API. Also you can simply use the JSON response without any conversion or something else like an array object in JavaScript.
You can see some examples here to figure out better: JSON example
I'm using YUI io to post data to my server. I have some problems sending foreign characters like æ ø å.
First case: a form is posted to the server
Y.io(url, {
method: 'POST',
form: {
id: 'myform',
useDisabled: true
}
});
This will post the content of the form to the server. If I have a field named "test1" containing "æøå", then on the server I'll see REQUEST_CONTENT="test1=%C3%A6%C3%B8%C3%A5". This can be easily decode with a urldecode function, NO PROBLEM, but...
Second case: data is posted this way:
Y.io(uri, {
data : '{"test1":"æøå"}'),
method : "POST"
});
Now I see this on the server REQUEST_CONTENT="{"test1":"├ª├©├Ñ"}". How can I decode that? And why is it send like that?
I know I can use encodeURIComponent() to encode the string before sending it. But the io request is actually part of a Model Sync operation, so I'm not calling io directly. I'm doing something like this:
Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {....});
var user = new Y.User();
user.set('test1', 'æøå');
user.save();
So it doesn't make sense to encode/decode everytime I set/read the attribute.
Also I have tried to set charset=utf-8 in the request header, but that didn't change anything.
EDIT
I have done some more debugging in chrome and the request is created with this line of code:
transaction.c.send(data);
transaction.c is the xmlhttprequest and (using chrome debugger) I can see the data is "{"test1":"æøå"}"
When the above line of code is executed, a pending network entry is shown (under the network tab in chrome debugger). Request payload displays {"test1":"├ª├©├Ñ"}
Headers are:
Accept:application/json
Content-Type:application/json; charset=UTF-8
ModelSync.REST has a serialize method that decides how the data in the model is turned into a string before passing it to Y.io. By default it uses JSON.stringify() which returns what you're seeing. You can decode it in the server using JSON. By your mention of urldecode I guess you're using PHP in the server. In that case you can use json_decode which will give you an associative array. If I'm not mistaken (I haven't used PHP in a while), it should go something like this:
$data = json_decode($HTTP_RAW_POST_DATA, true);
/*
array(1) {
["test1"] => "æøå"
}
*/
Another option would be for you to override the serialize method in your User model. serialize is a method used by ModelSync.REST to turn the data into a string before sending it through IO. You can replace it with a method that turns the data in the model into a regular query string using the querystring module:
Y.User = Y.Base.create('user', Y.Model, [Y.ModelSync.REST], {
serialize: function () {
return Y.QueryString.stringify(this.toJSON());
}
});
Finally, ModelSync.REST assumes you'll be using JSON so you need to delete the default header so that IO uses plain text. You should add this at some point in your code:
delete Y.ModelSync.REST.HTTP_HEADERS['Content-Type'];
Somewhere in my Django app, I make an Ajax call to my view
$.post("/metrics", {
'program': 'AWebsite',
'marketplace': 'Japan',
'metrics': {'pageLoadTime': '1024'}
});
in my python code, I have got
#require_POST
def metrics(request):
program = request.POST.get('program', '')
marketplace = request.POST.get('marketplace', '')
metrics = request.POST.get('metrics', '')
reportMetrics(metrics, program, marketplace)
the metrics() function in python is supposed to call reportMetrics() with these parameters which are then supposed to go in a log file. But In my log file, I do not see the 'pageLoadTime' value - probably because it is being passed in as a dictionary. In future I need to add more items to this, so it needs to remain a dictionary (and not a string like first two).
Whats the easiest way to convert this incoming javascript dictionary to python dictionary?
Send the javascript dictionary as json and import the python json module to pull it back out. You'll use json.loads(jsonString).
Edit - Added example
$.post("/metrics", {
data: JSON.stringify({
'program': 'AWebsite',
'marketplace': 'Japan',
'metrics': {'pageLoadTime': '1024'}
})
});
Then on the python side
import json
def metrics(request):
data = json.loads(request.POST.get('data'))
program = data.get('program','')
marketplace = data.get('marketplace','')
metrics = data.get('metrics','')
I don't really have a good way of testing this right now, but I believe it should work. You may also have to do some checking if a field is blank, but I believe .get() will handle that for you.
I have a Ajax form in my asp.net mvc application, which has a onSuccess callback as below:
function onSuccessReport(context)
{
$('reportChart').empty();
var line1=#Html.GetStrippedString(context.Expenses);
}
I defined a html helper which accept an string an manipulte it and return a string.
What I pass to onSuccessReport, is a json result which has a structure like this:
But I cant send context.Expenses and the application throws syntax error.
How can I send a javascript variable to my helper?
Thanks
Edited:
The error in my view
****Error 1 The name 'context' does not exist in the current context****
C# method
json = json.Replace("\"Date\":", string.Empty);
json = json.Replace("\"Total\":", string.Empty);
json = json.Replace('}', ']');
json = json.Replace('{', '[');
return MvcHtmlString.Create(json);
You are mixing client side code (javascript) with server side code (HtmlHelper). You cannot pass client side variables to server side helpers. If the context variable is known only on the client then you will have to write a client side javascript function instead of server side helper. So move to logic you wrote in this Html.GetStrippedString helper into a javascript function that you could call from your script.
Actually, you can send javascript values over to the server side if you use Ajax. Instead of using your helper method the way you are, just change it into an action within the Controller to return you some Json (could just be a string, number, object, etc, etc). Here is an example of what you might try out.
View
function onSuccessReport(context)
{
$('reportChart').empty();
var expenses = context.Expenses;
$.getJSON('#Url.Action("GetStrippedString", "ControllerName")', { expenses: expenses }, function (data) {
//pending what you pass back as data, do whatever with it
alert(data);
});
}
Controller
public JsonResult GetStrippedString(string expenses)
{
var result = string.Empty;
//Do something to string
return Json(result, JsonRequestBehavior.AllowGet);
}
I have a project that uses PageMethods to call functions on the server.
The server functions (written in C#) return the values as array of strings, without doing any kind of serialization and in the client side (from Js) the accessing of the return values is by using static variable called arguments.
I found that sometimes for some users (cases are not repro) sometimes an exception occured
"WebServiceFailedException the server method 'Foo' returned invalid data.
the 'd' property is missing from JSON."
Some searching on google I found that people are serializing the return values using DataContractJsonSerializer class and in js accessing the return value using one of the callback function
Example:
function OnRequestComplete(result,
userContext, methodName) {
var Person = eval('(' + result + ')');
alert(Person.Forename);
alert(Person.Surname); }
So is the first technique is correct? or what?
P.S:
the function on the server is defined on the default.aspx.cs file as follows:
[System.Web.Services.WebMethodAttribute(), System.Web.Script.Services.ScriptMethodAttribute()]
public static string[] Foo(string s);
from the client side the calling is as follows
PageMethods.Foo("value",OnSuccess);
Also all the users have the same browser version (IE8)
I don't know if it's the entire problem, but your first issue is manually serializing the return value. PageMethods and ScriptServices automatically JSON serialize their return values. Nesting two levels of JSON could definitely be throwing a wrench in the framework's client-side deserialization process (which is also happening automatically, before your eval() code).
To return an instance of your Person class, this is all you need:
public static Person GetPerson() {
Person p = new Person();
// Populate the Person object here.
return p;
}
Then, on the client-side you can work with the object's properties as expected:
function OnRequestComplete(result, userContext, methodName) {
console.log('Person name: ' + result.Forename + ' ' + result.Surname);
}
Alternatively, if you're using jQuery for other tasks and already have it on the page, you don't even need the ScriptManager and MS AJAX to call page methods. You can directly call page methods with jQuery and skip all that overhead.
Without knowing how the request is made and how the server end is coded, my answer may not be accurate.
Where is the WebMethod decorated method in your server side code? If it is an a separate class with the ScriptService attribute, and if JSON is specified while making the request, then the JSON values should have been automatically serialized and dont need to be serialized manually again. With this set up ASP.NET 3.5 wraps the response in a "d" object
Some users getting the exception might be due to the browser that they are using. If you are using jQuery, I'd specify the content type as so in the ajax request body
contentType: "application/json; charset=utf-8",
Hakeem
This is a bit funky. ASP.NET always adds the "d" to all results. So it either should work or not. Here is some background on the "d" issue:
http://encosia.com/2009/06/29/never-worry-about-asp-net-ajaxs-d-again/