Remote Method Invocation¶
Wouldn’t it be nice to call a Django view method, directly from an AngularJS controller, similar to a Remote Procedure Call or say better Remote Method Invocation?
Single Page Applications¶
By nature, Single Page Web Applications implemented in Django, require one single View. These kind
of applications can however not always be build around the four possible request methods GET
,
PUT
, POST
and DELETE
. They rather require many different entry points to fulfill the
communication between the client and the server.
Normally, this is done by adding a key to the request data, which upon evaluation calls the appropriate method. However, such an approach is cumbersome and error-prone.
Django-Angular offers some helper functions, which allows the client to call a Django’s View method, just as if it would be a normal asynchronous JavaScript function. To achieve this, let the View’s class additionally inherit from JSONResponseMixin:
from django.views.generic import View
from djng.views.mixins import JSONResponseMixin, allow_remote_invocation
class MyJSONView(JSONResponseMixin, View):
# other view methods
@allow_remote_invocation
def process_something(self, in_data):
# process in_data
out_data = {
'foo': 'bar',
'success': True,
}
return out_data
In this Django View, the method process_something
is decorated with @allow_remote_invocation
.
It now can be invoked directly from an AngularJS controller or directive. To handle this in an
ubiquitous manner, Django-Angular implements two special template tags, which exports all
methods allowed for remote invocation to the provided AngularJS service djangoRMI
.
Template Tag djng_all_rmi
¶
The AngularJS Provider djangoRMIProvider
shall be configured during the initialization of the
client side, such as:
{% load djng_tags %}
…
<script type="text/javascript">
var tags = {% djng_all_rmi %};
my_app.config(function(djangoRMIProvider) {
djangoRMIProvider.configure(tags);
});
</script>
This makes available all methods allowed for remote invocation, from all View classes of your Django project.
Note
In order to have your methods working, the associated urls need to be named.
Template Tag djng_current_rmi
¶
Alternatively, the AngularJS Provider djangoRMIProvider
can be configured during the
initialization of the client side, such as:
{% load djng_tags %}
…
<script type="text/javascript">
var tags = {% djng_current_rmi %};
my_app.config(function(djangoRMIProvider) {
djangoRMIProvider.configure(tags);
});
</script>
This makes available all methods allowed for remote invocation, from the current View class, ie. the one rendering the current page.
Note
In order to have your methods working, the associated urls need to be named.
Let the client invoke an allowed method from a Django View¶
By injecting the service djangoRMI
into an AngularJS controller, allowed methods from the
Django View which renders the current page, can be invoked directly from JavaScript. This example
shows how to call the above Python method process_something
, when configured using the template
tag djng_current_rmi
:
my_app.controller("SinglePageCtlr", function($scope, djangoRMI) {
$scope.invoke = function() {
var in_data = { some: 'data' };
djangoRMI.process_something(in_data)
.success(function(out_data) {
// do something with out_data
});
};
});
If djangoRMIProvider
is configured using the template tag djng_all_rmi
, the allowed
methods are grouped into objects named by their url_name. If these URL patterns are part of a
namespace, the above objects furthermore are grouped into objects named by their namespace.
Note
djangoRMI is a simple wrapper around AngularJS’s built in $http service. However, it
automatically determines the correct URL and embeds the method name into the special
HTTP-header DjNg-Remote-Method
. In all other aspects, it behaves like the
$http service.
Dispatching Ajax requests using method GET¶
Sometimes you only have to retrieve some data from the server. If you prefer to fetch this data
using an ordinary GET request, ie. one without the special AngularJS provider djangoRMI
, then
it is possible to hard-code the method for invocation into the urlpatterns inside the URL
dispatcher.
class MyResponseView(JSONResponseMixin, View):
def get_some_data(self):
return {'foo': 'bar'}
def get_other_data(self):
return ['baz', 'cap']
urlpatterns = [
# …
url(r'^fetch-some-data.json$', MyResponseView.as_view(), {'invoke_method': 'get_some_data'}),
url(r'^fetch-other-data.json$', MyResponseView.as_view(), {'invoke_method': 'get_other_data'}),
# …
]
If a client calls the URL /fetch-some-data.json
, the responding view dispatches incoming
requests directly onto the method get_some_data
. This kind of invocation only works for GET
requests. Here these methods do not require the decorator @allow_remote_invocation
,
since now the server-side programmer is responsible for choosing the correct method and thus a
malicious client cannot bypass the intended behavior.