django-angular Documentation Release 0.4.0 ... - Read the Docs

yrekazzyzxamuckInternet και Εφαρμογές Web

4 Δεκ 2013 (πριν από 3 χρόνια και 8 μήνες)

723 εμφανίσεις

django-angular Documentation
Release 0.4.0
Jacob Rief
November 21,2013
Contents
1 Installation and Configuration 3
1.1 Getting the latest release.........................................3
1.2 Dependencies...............................................3
1.3 Configuration...............................................3
2 Integrating AngularJS with Django 5
2.1 XMLHttpRequest............................................5
2.2 Template tags...............................................5
3 Integrate a Django formwith an AngularJS model 7
3.1 Sample code...............................................7
3.2 Working with nested forms........................................9
4 Validate Django forms using AngularJS 11
4.1 Mixing NgFormValidationMixin with NgModelFormMixin......................12
4.2 Customizing validation errors......................................12
4.3 Demo...................................................12
5 Dispatching Ajax requests froman AngularJS controller 13
5.1 Dispatching Ajax requests using method GET.............................14
6 Cross Site Request Forgery protection 15
7 Manage Django URL’s for AngularJS 17
7.1 List all URLs which belong to a namespace...............................18
8 Using attribute placeholder in input field 19
9 Indices and tables 21
i
ii
django-angular Documentation,Release 0.4.0
Django-Angular is a collection of utilities,which aim to ease the integration of Django with AngularJS by providing
reusable components.
Contents:
Contents 1
django-angular Documentation,Release 0.4.0
2 Contents
CHAPTER 1
Installation and Configuration
1.1 Getting the latest release
The easiest way to get django-angular is simply to install it with pip:
$ pip install django-angular
Please also check the latest source code fromgithub.
1.2 Dependencies
• Django >=1.3.1
• AngularJS >=1.0.4
1.3 Configuration
Add"djangular"to your project’s INSTALLED_APPSsetting,and make sure that static files are found in external
Django apps:
INSTALLED_APPS = (
...
’djangular’,
...
)
STATICFILES_FINDERS = (
’django.contrib.staticfiles.finders.FileSystemFinder’,
’django.contrib.staticfiles.finders.AppDirectoriesFinder’,
...
)
Note:django-angular does not define any database models.It can therefore easily be installed without any database
synchronization.
3
django-angular Documentation,Release 0.4.0
4 Chapter 1.Installation and Configuration
CHAPTER 2
Integrating AngularJS with Django
2.1 XMLHttpRequest
As a convention in web applications,Ajax requests shall send the HTTP-Header:
X-Requested-With:XMLHttpRequest
while invoking POST-requests.In AngularJS versions 1.0.x this was the default behavior,but in versions 1.1.x this
support has been dropped.Strictly speaking,Django applications do not require this header,but if it is missing,all
invocations to:
request.is_ajax()
would return False,even for perfectly valid Ajax requests.Thus,if you use AngularJS version 1.1.x,add the
following statement during module instantiation:
angular.module(’MyNgModule’).config(function($httpProvider) {
$httpProvider.defaults.headers.common[’X-Requested-With’] = ’XMLHttpRequest’;
});
2.2 Template tags
Django and AngularJS share the same token for variable substitution in templates,ie.{{ variable_name }}.
This should not be a big problem,since you are discouraged to mix Django template code with AngularJS template
code.However,this recommendation often is not practical in all situations,and there might be a need to mix both
template languages,one which is expanded by Django on the server,and one which is expanded by AngularJS in the
browser.
The cleanest solution to circumvent this,is by using the verbatimtag,which became available in Django 1.5.
A less clean solution,is to change the syntax of the AngularJS template tags.Just add the following statement during
module instantiation:
angular.module(’MyNgModule’).config(function($interpolateProvider) {
$interpolateProvider.startSymbol(’{$’);
$interpolateProvider.endSymbol(’$}’);
});
5
django-angular Documentation,Release 0.4.0
Now,you can easily distinguish a server side variable substitution {{ varname }} from a client side variable
substitution {$ varname $}.
This approach is even less verbose than using the verbatim tag.The problem,however,is that you have to remember
this alternative tag syntax for all of your AngularJS templates.This also makes it difficult to integrate third party
AngularJS directives,which are shipped with their own templates.
2.2.1 Partials
In AngularJS,when used together with external templates,static HTML code often is loaded by a $routeProvider_.
These so named partials can be placed in their own sub-directory belowSTATIC_ROOT.
If for some reason you need mixed template code,ie.one which first is expanded by Django and later is parsed by
AngularJS,then add to your urls.py:
partial_patterns = patterns(’’,
url(r’^partial-template1.html$’,PartialGroupView.as_view(template_name=’partial-template1.html’),name=’partial_template1’),
...more partials...,
)
urlpatterns = patterns(’’,
...
url(r’^partials/’,include(partial_patterns,namespace=’partials’)),
...
)
By using the utility function:
from djangular.core.urlsresolvers import urls_by_namespace
my_partials = urls_by_namespace(’partials’)
the caller obtains a list of all partials defined for the given namespace.This list can be used when creating a Javascript
array of URL’s to be injected into controllers.
2.2.2 Dynamically generated Javascript code
There might be good reasons to mix Django template with AngularJS template code.Consider a multilingual applica-
tion,where text shall be translated,using the superb Django translation engine.
Also,sometimes your application must pass configuration settings,which are created by Django during runtime,
such as reversing a URL.These are the use cases when to mix Django template with AngularJS template code.
Remember,when adding dynamically generated Javascript code,to keep these sections small and mainly for the
purpose of configuring your AngularJS module.All other Javascript code shall go into separate static files!
Warning:Never use Django template code to dynamically generate AngularJS controllers or directives.This will
make it very hard to debug and impossible to add Jasmine unit tests to your code.Always do a clear separation
between the configuration of your AngularJS module,which is part of your application,and the client side logic,
which shall be testable without the need of a running Django server.
6 Chapter 2.Integrating AngularJS with Django
CHAPTER 3
Integrate a Django formwith an
AngularJS model
When you derive from Django’s forms.Form class in an AngularJS environment,it can be useful to augment the
rendered formoutput with the HTML tags:
ng-model="model_name"
where model_name corresponds to the named field fromthe declared formclass.
3.1 Sample code
Assume you have a simple Django form class with a single input field.Augment its functionality by mixing in the
djangular class NgModelFormMixin:
from django import forms
from djangular.forms.angular_model import NgModelFormMixin
class ContactForm(NgModelFormMixin,forms.Form):
subject = forms.CharField()
Now,each rendered form field gets an additional attribute ng-model containing the field’s name.For instance,the
input field named subject would be rendered as:
<input id="id_subject"type="text"name="subject"ng-model="subject"/>
This means,that to a surrounding Angular controller,the field’s value is immediately added to its $scope.
3.1.1 Full working example
This demonstrates how to submit form data using an AngularJS controller.The Django view handling this unbound
contact formclass may look like:
from django.views.generic import TemplateView
class ContactFormView(TemplateView):
template = ’contact.html’
7
django-angular Documentation,Release 0.4.0
def get_context_data(self,
**
kwargs):
context = super(ContactFormView,self).get_context_data(
**
kwargs)
context.update(contact_form=ContactForm())
return context
with a template named contact.html:
<form ng-controller="MyFormCtrl">
{{contact_form}}
<button ng-click="submit()">Submit</button>
</form>
and using some Javascript code to define the AngularJS controller:
function MyFormCtrl($scope,$http) {
$scope.submit = function() {
$http.post(’/url/of/your/contact_form_view’,{
subject:$scope.subject
}).success(function(out_data) {
//do something
});
}
}
Note that your <form> does not require any method or action attribute,since the promise success in your
controller’s submit function will handle any further action,for instance to load a new page or to complain about
missing fields.In fact,you can build forms without even using the <form> tag anymore.All you need fromnow on,
is a working AngularJS controller.
As usual,your form view must handle the post data received through the POST (aka Ajax) request.However,Angu-
larJS does not send post data using multipart/form-data or application/x-www-form-urlencoded
encoding - rather,it uses plain JSON,which avoids an additional decoding step.
Note:In real code you should not hard code the URL into an AngularJS controller as shown in this example.Instead
inject an object containing the URL into your controller as explained in manage Django URL’s for AngularJS
Add these methods to your contact formview:
import json
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseBadRequest
class ContactFormView(TemplateView):
#use ‘get_context_data()’ from above
@csrf_exempt
def dispatch(self,
*
args,
**
kwargs):
return super(ContactFormView,self).dispatch(
*
args,
**
kwargs)
def post(self,request,
*
args,
**
kwargs):
if not request.is_ajax():
return HttpResponseBadRequest(’Expected an XMLHttpRequest’)
in_data = json.loads(request.body)
bound_contact_form = CheckoutForm(data={’subject’:in_data.get(’subject’)})
#now validate ‘bound_contact_form’ and use it as in normal Django
The problem with this implementation,is that one must remember to access each form field three times.Once in the
declaration of the form,once in the Ajax handler of your AngularJS controller,and once in the post handler of the
8 Chapter 3.Integrate a Django formwith an AngularJS model
django-angular Documentation,Release 0.4.0
view.This make maintenance hard and is a violation of the DRYprinciple.Therefore it makes sense to add a prefix to
the model names.One possibility would be to add the argument scope_prefix on each form’s instantiation,ie.:
contact_form = ContactForm(scope_prefix=’my_prefix’)
This,however,has to be done across all instantiations of your form class.The better way is to hard code this prefix
into the constructor of the formclass:
class ContactForm(NgModelFormMixin,forms.Form):
#declare form fields
def __init__(self,
*
args,
**
kwargs):
kwargs.update(scope_prefix=’my_prefix’)
super(ContactForm,self).__init__(
*
args,
**
kwargs)
Now,in your AngularJS controller,the scope for this form starts with an object named my_prefix containing an
entry for each formfield.This means that an input field,for instance,is rendered as:
<input id="id_subject"type="text"name="subject"ng-model="my_prefix.subject"/>
This also simplifies your Ajax submit function,because you just have to pass the Javascript object
$scope.my_prefix as
$http.post(’/url/of/contact_form_view’,$scope.my_prefix)
to your Django view.
3.2 Working with nested forms
NgModelFormMixin is able to handle nested forms as well.Just remember to add the attribute
prefix=’subform_name’ with the name of the sub-form,during the instantiation of your main form.Now
your associated AngularJS controller adds this additional model to the object $scope.my_prefix,keeping the
whole formself-contained and accessible through one Javascript object,aka $scope.my_prefix.
The Django view responsible for handling the post request of this formautomatically handles the parsing of all bound
formfields,even fromthe nested forms.
Note:Django,internally,handles the field names of nested forms by concatenating the prefix with the field name
using a dash ‘-’.This behavior has been overridden in order to use a dot ‘.’,since this is the natural separator between
Javascript objects.
3.2.Working with nested forms 9
django-angular Documentation,Release 0.4.0
10 Chapter 3.Integrate a Django formwith an AngularJS model
CHAPTER 4
Validate Django forms using AngularJS
Django’s forms.Form class offers many possibilities to validate a given form.This for obvious reasons is done on
the server.However,customers may not always accept to submit a form,just to find out that they missed to input some
correct data into a field.Therefore client side form validation is a good idea and very common.But since client side
validation easily can by bypassed,the same validation has to occur a second time,when the server accepts the forms
data for final processing.
This leads to code duplication is generally violates the DRY principle!
Aworkaround to this problemis to use Django’s formdeclaration to automatically generate client side validation code,
suitable for AngularJS.By adding a special mixin class to your form declaration,this can be achieved automatically
and on the fly:
from django import forms
from djangular.forms import NgFormValidationMixin
class MyValidatedForm(NgFormValidationMixin,forms.Form):
surname = forms.CharField(label=’Surname’,min_length=3,max_length=20)
age = forms.DecimalField(min_value=18,max_value=99)
When you initialize this form,give it a name,otherwise the form’s name defaults to “form”.This is required,since
the AngularJS validation code expects a named form.
In the view class,add the created formto the rendering context:
def get_context_data(self,
**
kwargs):
context = super(MyRenderingView,self).get_context_data(
**
kwargs)
context.update(form=MyValidatedForm())
return context
Render this formin a template:
<form name="{{ form.name }}"novalidate>
{{ form }}
<input type="submit"value="Submit"/>
</form>
Remember to add the entry name="{{ form.name }}"to the form element.Use the directive novalidate to
disable the browser’s native form validation.If you just need AngularJS built in form validation mechanisms without
customized checks on the forms data,you are not even required to add an ng-controller onto a wrapping HTML
element.The only measure to take,is to give each form on a unique name,otherwise the AngularJS form validation
code might get confused.
11
django-angular Documentation,Release 0.4.0
4.1 Mixing NgFormValidationMixin with NgModelFormMixin
While it is possible to use NgFormValidationMixin on itself,it is perfectly legal to mix NgModelFormMixin
with NgFormValidationMixin.However,a few precautions have to be taken.
On class declaration inherit first from NgModelFormMixin and afterward from NgFormValidationMixin.
Example:
from django import forms
from djangular.forms import NgFormValidationMixin,NgModelFormMixin
class MyValidatedForm(NgModelFormMixin,NgFormValidationMixin,forms.Form):
pass
Don’t do this:
class MyValidatedForm(NgFormValidationMixin,NgModelFormMixin,forms.Form):
pass
Another precaution to take,is to use different names for the forms name and the scope_prefix.So,this is legal:
form = MyValidatedForm(name=’my_form’,scope_prefix=’my_model’)
but this is not:
form = MyValidatedForm(name=’my_form’,scope_prefix=’my_form’)
AngularJS names each input field to validate,by concatenating its forms name with its fields name.This object
member then contains an error object,named formname.fieldname.$error filled by the AngularJS validation
mechanism.The placeholder for the error object would clash with ng-model,if the forms name is identical to the
model prefix.Therefore,just remember to use different names.
4.2 Customizing validation errors
If a validated Django form is rendered,each input field is prefixed with an unsorted list <ul> of errors,one list item
<li> for each constraint,which might not be satisfied during validation.Now,if a client enters invalid data,these
prepared error messages are made visible using ng-show.The message text is exactly the same as would be shown
if the server side,ie.Django itself,complains about invalid data.These error messages can be customized during the
field initialization.
The default error list is rendered as <ul class="djng-form-errors">...</ul>.If an alternative CSS class
is desired,initialize the formusing the optional argument form_error_class=’my-error-class’.
4.3 Demo
There are two forms using the AngularJS validation mechanisms,one with and one without using the additional
NgModelFormMixin.The former displays the entered model data as a simple code object.
To test this code,a small demo is supplied with this package.With Django >= 1.5 installed,it should run out of
the box.Just change into the directory examples,run./manage.py runserver and point your browser onto
http://localhost:8000/simple_form/or http://localhost:8000/model_form/
Start to fill out the fields.First name requires at least 3 characters,Middle name is optional,Last name must start with
a capital letter and age must be between 18 and 99.
12 Chapter 4.Validate Django forms using AngularJS
CHAPTER 5
Dispatching Ajax requests froman
AngularJS controller
Wouldn’t it be nice to call a Django view method,directly from your AngularJS controller,similar to Remote Proce-
dure Calls?
This can simply be achieved by adding a djangular mixin class to that view:
from django.views.generic import View
from djangular.views.mixins import JSONResponseMixin,allowed_action
class MyJSONView(JSONResponseMixin,View):
#other view methods
@allowed_action
def process_something(self,in_data):
#process input data
out_data = {
’foo’:’bar’,
’success’:True,
}
return out_data
Let’s assume that the URL used for posting this request is attached to this view.Now in your AngularJS controller,
calling the view’s method process_something is as simple as:
function MyFormCtrl($scope,$http) {
$scope.submit = function() {
var in_data = {action:’process_something’};
angular.copy($scope.my_prefix,in_data);
$http.post(’/url/of/my_json_view’,in_data)
.success(function(out_data) {
if (out_data.success) {
//update the controllers scope
} else {
alert(’Something went wrong’);
}
});
}
}
13
django-angular Documentation,Release 0.4.0
Note:In real code you should not hard code the URL into an AngularJS controller as shown in this example.Instead,
inject an object containing the URL into your controller as explained in manage Django URL’s for AngularJS.
The special keyword action,as declared in the post data to be sent,contains the method name of the view to
be called.In MyJSONView.process_something() this action tuple is then already stripped off from the
passed in_data and the method receives a Python dictionary containing an exact copy of the Javascript object
$scope.my_prefix.
Warning:To eschew the possibility for an attacker to call any method of your view by setting the keyword
action to an arbitrary method name,the author of the view must explicitly give permission to call this method.
This is done by adding the decorator @allowed_action in front of the methods to be exposed.Otherwise the
remote caller receives an HttpResponseBadRequest error.
5.1 Dispatching Ajax requests using method GET
Sometimes you only have to fetch some data from the server.If you prefer to fetch this data using the GET method,
you have no way to pass in the action keyword with the remote method you want to execute.But django-angular
lets you hard-code that action inside your URL dispatcher:
urlpatterns = patterns(’’,
...
url(r’^fetch-some-data.json$’,MyResponseView.as_view(),{’action’:’get_data’}),
...
)
By calling the URL fetch-some-data.json,the responding view dispatches incoming requests directly onto
the method get_data.This works with GET requests as well as with POST requests:
class MyResponseView(JSONResponseMixin,View):
def fetch_data(self):
return { ’foo’:’bar’ }
Note:Here for GET requests,the method fetch_data does not require the decorator @allowed_action,since
this method invocation has been determined by programmer,rather than the client.Therefore there is no security issue
here.
14 Chapter 5.Dispatching Ajax requests froman AngularJS controller
CHAPTER 6
Cross Site Request Forgery protection
Ajax requests submitted using method POST are put to a similar risk for Cross Site Request Forgeries as HTTP forms.
This type of attack occurs when a malicious Web site is able to invoke an Ajax request onto your Web site.In Django,
one should always add the template tag csrf_token to render a hidden input containing the token,inside each form
submitted by method POST.
When it comes to making an Ajax request,it normally is not possible to pass that token using a Javascript object,
because scripts usually are static and no secret can be added dynamically.To effectively solve that problemin a DRY
manner,there are two similar possibilities.
When initialising the Angular application,add to following snippet to the run method:
angular.module(’myApp’,[/
*
other dependencies
*
/,’ngCookies’]).run(function($http,$cookies) {
$http.defaults.headers.post[’X-CSRFToken’] = $cookies.csrftoken;
});
The problem with this approach is,that one must ensure that the CSRF cookie is not configured as HTTP_ONLY,
otherwise that value can’t be accessed from Javascript.Therefore djangular offers a templatetag to hard code that
value into a template:
{% load djangular_tags %}
angular.module(’myApp’,[/
*
other dependencies
*
/]).run(function($http) {
$http.defaults.headers.post[’X-CSRFToken’] ="{% csrf_value %}";
});
The latter is my preferred method.
Optionally,if the above methods do not work,add the following method to the view handling the Ajax request:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def dispatch(self,
*
args,
**
kwargs):
return super(MyView,self).dispatch(
*
args,
**
kwargs)
This disables Cross Site Request Forgery protection for Ajax request.Use it at your own risk!
15
django-angular Documentation,Release 0.4.0
16 Chapter 6.Cross Site Request Forgery protection
CHAPTER 7
Manage Django URL’s for AngularJS
You may have noticed,that AngularJS controllers sometimes need a URL pointing to a Django view of your applica-
tion.Don’t fall into the temptation to hard code such an URL into your Javascript controller code.Nor fall into the
temptation to dynamically create Javascript by using a template engine.There is a clean and simple solution to this
problem.
It is good practice to add configuration directives to applications as constants to the AngularJS module definition.This
can safely be done in your template code and belongs in there!
<script>
angular.module(’MyNgModule’).constant(’urls’,{
this_view_url:"{% url this_view %}",
that_view_url:"{% url that_view %}",
...
});
</script>
This newly generated constant object is available through dependency injection to all directives and controllers which
are part of your AngularJS module.Now the remaining task which has to be performed,is to inject this constant
object into the controllers which require a Django URL.The controller examples from JSONResponseMixin and
NgModelFormMixin then can be rewritten as:
function MyFormCtrl($scope,$http,urls) {
$scope.submit = function() {
$http.post(urls.this_view_url,$scope.my_prefix)
.success(function(out_data) {
//do something
});
}
}
Likewise,the urls.that_view_url can be used in an html partial that you may want in an href in place of the
Django url template tag,provided that $scope.urls = urls is set in the controller.
function MyUploadCtrl($scope,$http,urls) {
$scope.urls = urls;
}
}
<a href="{$ urls.that_view_url $}"class="btn btn-primary btn-lg"role="button">Upload File</a>
17
django-angular Documentation,Release 0.4.0
7.1 List all URLs which belong to a namespace
To avoid the repetitive work of adding all the URL’s to this constant,a utility function is available,which returns a
Python dictionary with all URL’s which belong to a certain namespace.This function is available as:
from djangular.core.urlresolvers import urls_by_namespace
The returned dictionary can be used directly to fill the constant with the URLs to be passed into your AngularJS
controller:
<script>
angular.module(’MyNgModule’).constant(’urls’,dict_goes_here);
</script>
Warning:This function is still experimental.Use it at your own risk.
18 Chapter 7.Manage Django URL’s for AngularJS
CHAPTER 8
Using attribute placeholder in input
field
If rendering space is sparse,sometimes it makes sense to place the label of an input field inside itself,such as shown
here:
these labels are rendered in grey,so that they can easily be distinguished from normal user input.If the user starts to
enter text into such a field,then the placeholder vanishes.
To ease the integration with Django,add AddPlaceholderFormMixin while declaring your formclass:
from django import forms
from djangular.forms.mixins import AddPlaceholderFormMixin
class ContactForm(AddPlaceholderFormMixin,forms.Form):
#all form fields as usual
pass
you may of course add other mixins to this formclass,such as NgModelFormMixin.
If you need backward compatibility with older browsers,which do not support the placeholder attribute,use this
AngularJS directive:https://github.com/jrief/angular-shims-placeholder
19
django-angular Documentation,Release 0.4.0
Note:In Django-1.5 the formAPI changed and adding non standard attributes to fields became easier.
20 Chapter 8.Using attribute placeholder in input field
CHAPTER 9
Indices and tables
• genindex
• modindex
• search
21