Tutorial: Using AngularJS with Django

31 Jan 2014
AngularJS Django Python

JS

Please Note:
This post appears to be over 6 months old.

I’m hoping to write a quick tutorial to get you started using Angular with Django What it turned into was a tutorial fueled by Red Bull. My apologies if it gets sloppy towards the end! 

Having read posts on the subject of using Django and Angular together, I felt most were reinventing the wheel, so to speak. Although the example code I have given is crude it should highlight how I've been using them on projects.

Models

Lets start with a typical Model.

/jobs/models.py

 
class Job(models.Model):
     name = models.CharField(max_length=50)
     description = models.TextField(null=True, blank=True)
 
 

Alright, nothing special so far. All you have done is create a simple model to contain basic job details.

The REST API (Tastypie)

AngularJS is built to consume webservices, so your gonna need a way to expose the Job Model you just created.

Django has a good set of choices to create RESTful APIs. TastyPie is an awesome webservice framework built for the Django framework. It's incredibly powerful, yet easy to setup and use. However, personal preference aside, the same results could be achieved using Django REST framework, or even constructing your own API responses directly using Django. The choice is entirely yours. For the purposes of this tutorial we'll be using TastyPie.

If you're not familiar with TastyPie head over to the documentation. I won't go into detail regarding installation. One will assume you've setup and added TastyPie to your installed applications and are ready to go.

First, you need to create a resource for your Jobs. TastyPie uses the concept of 'Resources'. It describes them as intermediaries between the end user and objects, in this case thats the Job Model.

Start by creating the appropriate resource for the Job Model:


 class JobResource(ModelResource):
     """
     API Facet
     """
     class Meta:
         queryset = Jobs.objects.all()
         resource_name = 'job'
         allowed_methods = ['post', 'get', 'patch', 'delete']
         authentication = Authentication()
         authorization = Authorization()
         always_return_data = True
 
 

From memory, TastyPies documentation suggests naming the file api.py within your application. This is also my preference, but it's not mandatory.You can name the Python file whatever you like, but it's nice to keep consistency.

There are a few things going on in JobResource which is beyond the scope of this tutorial. But, I would just like to draw attention to how JobResource inherits 'ModelResource'. You want to use Tastypie with Django's ORM (the Job Model). Extending this means that many of the API fundamentals are all handled for you.

TastyPie can handle non-ORM data too. By extending directly from Resource you can also get all the API goodies TastyPie has to offer, but without being tied to the ORM. This is particularly useful when making calls to a non ORM, No SQL database as described in the documentation.

So far you have created the Model (Job) and a way for the end user to interface with it. Next, you need a way to connect the resource to an actual URL that will eventually allow AngularJS to consume it. You do this in Django by hooking it up to the URLconf. Simply instantiate the resource in your Django URLconf then hook up the URL's:


 from tastypie.api import Api
 from .apps.your_app.api import JobResource
 
 v1_api = Api(api_name='v1')
 v1_api.register(JobResource())
 
 urlpatterns = patterns('',
 
      (r'^api/', include(v1_api.urls)),
 )
 

The 'resource_name' attribute specified in the JobResource is the end point of the url. With that you now have a working API with the Resource endpoint Job. Check it’s all working by running your local server, then visiting http://127.0.0.1:8000/api/job/?format=json in your browser.

You now have a working API for your Job model. Easy.

Forms

Before you begin diving into AngularJS we are going to need to create a Job Form using Django's framework. The Job form will later allow you to edit Jobs in the single page application. I know what you're thinking, "why in Django"?

One of Django's design philosophies is "Don’t repeat yourself (DRY)". So it doesn't make sense to build forms using HTML for AngularJS and then in Django too, besides Django does such a good job as this. You may also already have several forms you want to convert, so why repeat the process? Enter, django-angular. This is one cool package you will be glad you came across (I know I was).

Quote: "Django-Angular is a collection of utilities, which aim to ease the integration of Django with AngularJS by providing reusable components."

Again, I'm not going to go into any details regarding the setup and installation here. I suggest you head over and check Django-Angular right away! Suffice to say, one of its many ticks it to allow you to use Django forms thus its form validation within AngularJS. Combine this with a package such as 'crispy forms' and you have a powerful all-in-one solution - "this is why I love the Django framework and its community".


 from .app.your_app.models import Job
 from .apps.djangular.forms import NgFormValidationMixin, NgModelFormMixin, AddPlaceholderFormMixin
 
 class JobForm(NgModelFormMixin, forms.ModelForm):
     """
     Job Form with a little crispy forms added! 
     """
     def __init__(self, *args, **kwargs):
         super(JobForm, self).__init__(*args, **kwargs)
         setup_bootstrap_helpers(self)
 
     class Meta:
         model = Job
         fields = ('name', 'description',)
 
 def setup_bootstrap_helpers(object):
     object.helper = FormHelper()
     object.helper.form_class = 'form-horizontal'
     object.helper.label_class = 'col-lg-3'
     object.helper.field_class = 'col-lg-8'

On to AngularJS

For simplicity you're going to create 3 new templates using the following structure:

 templates
    jobs/index.html
    jobs/new.html
 base.html
 

 This assumes you have a Job app setup and installed. Your base template will look something like this:

/jobs/base.html


 <!DOCTYPE html>
 <html>
 <head>
     <meta charset="utf-8">
     <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.2/css/bootstrap.min.css" rel="stylesheet">
 
     <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.js"></script>
     <script src="/angular-ui-router.min.js"></script>
     <script type="text/javascript" src="http://cdn.jsdelivr.net/restangular/latest/restangular.js"></script>
 
 </head>
 <body>
    {% block content %}{% endblock content %}
    {% block extra_javascript %}{% endblock extra_javascript %}
 </body>
 </html>
 

Django-Angular does offer some nice template tags which will include the necessary javascript for you. I recommend using a content distribution network (CDN) to load the necessary files where possible. Doing so gives obvious geographically and bandwidth advantages.

From here you need to create a signal page template that will be served by our Django project. The index.html will serve as the main page for our single page application and later can be used to serve all your CRUD views for Jobs.

/jobs/index.html


{% extends "base.html" %}
 {% load i18n %}
 {% block content %}
 <div class="container content" ng-app="JobApp">
     <div ui-view >Loading...</div>
 </div>
 {% endblock content %}
 {% block extra_javascript %}
 <script src="{{ STATIC_URL }}/javascript/app.js"></script>
 {% endblock extra_javascript %}
 

/javascript/app.js


var app = angular.module('JobApp', [
     'ui.router',
     'restangular'
 ])
 
 app.config(function ($stateProvider, $urlRouterProvider, RestangularProvider) {
     // For any unmatched url, send to /route1
     $urlRouterProvider.otherwise("/");
     $stateProvider
         .state('index', {
 
             url: "/",
             templateUrl: "/static/html/partials/_job_list.html",
             controller: "JobList"
         })
 
        .state('new', {
 
             url: "/new",
             templateUrl: "/jobs/job-form",
             controller: "JobFormCtrl"
         })
 })
 
 app.controller("JobFormCtrl", ['$scope', 'Restangular', 'CbgenRestangular', '$q',
 function ($scope, Restangular, CbgenRestangular, $q) {
 
 
 }])// end controller
 
 

The template and js above is very simple, inheriting from the base template. There are a few attributes you may-not have seen before and will need to understand.

The first of which is ng-app='JobApp'. Without this tag, the AngularJS process does not start. This directive tells AngularJS which element is the root element of the application. Anything you add inside this element will be part of the template managed by AngularJS.

Next, look at the script you have included in the index.html. This app.js script defines the angular module. An Angular module is a collection of functions that are run when the application is 'booted'.


 var app = angular.module('JobApp', [
 

This above line creates the module called 'JobApp'. In the index.html you already instantiated this using the ng-app='JobApp' attribute. What you have basically done here is tell AngularJS you want app.js to own everything inside.

Infact, you could set ng-app on any element in the DOM. For example, if you didn't want a part of the template controlled by Angular you could do this:


 <h2>I am not inside an AngularJS app</h2>
 <div ng-app="embeddedApp">
   <h3>Inside an AngularJS app</h3>
 </div>

app.config in app.js also shows the beginnings of your URL routing. AngularJS supplies URL routing by default via $route service in Angular core, but it's inadequate, and has some limitations.

One of the modules you have included is AngularUI Router 'ui.router'. AngularUI Router is an another routing framework for AngularJS which is organised around states, which may optionally have routes, as well as other behaviour, attached.

You have provided just one state in this tutorial called 'new', but you could include lots of different states for you application and hopefully you're having a lightbulb moment right now. You can even add a default behaviour for when no state is detected:


  $urlRouterProvider.otherwise("/");
     $stateProvider
         .state('index', {
 
             url: "/",
             templateUrl: "static/html/somepage.html",
             controller: "SomeController"
         })
 
 

If unfamiliar with this then I suggest reading up on AngularUI Router when you have completed this tutorial.

The last element within index.html you should understand is 'ui-view'. This is part of AngularUI Router model too. The ui-view directive tells $state where to place your template ie. templateUrl: "/job/new/".

Final template you will be creating is /jobs/new.html. This will hold the basic form you created earlier using the Django-Angular.


 {% load crispy_forms_tags %}
 {% crispy JobForm %}
 <button type="button" class="btn btn-default"  ng-click="submitJob()">Create</button>
 

Now you just need the view and URL to connect up the form.

/jobs/views.py


 from .forms import JobForm
 
 class JobFormView(TemplateView):
     template_name = "jobs/new.html"
 
     def get_context_data(self, **kwargs):
         context = super(JobFormView, self).get_context_data(**kwargs)
         context.update(JobForm=JobForm())
         return context
 

/jobs/urls.py


 from django.conf.urls import url
 from django.conf.urls import patterns
 
 from .views import JobFormView
 
 urlpatterns = patterns('',
 
                         url(r'^job-form/$',
                            login_required(JobFormView.as_view()),
                            name='job_form'),
 
 )
 
 

Now in your browser navigate to http://127.0.0.1:8000/job/#new and you should see the job form in your new single page application.

Our last step is to post our form data when submitJob is clicked. You are going to change the controller, the example below will use restangular.

Restangular is an AngularJS service that simplifies common GET, DELETE, and UPDATE requests with a minimum of client code. It's a perfect fit for any WebApp that consumes data from a RESTful API.  restangular


app.controller("JobFormCtrl", ['$scope', 'Restangular', 'CbgenRestangular', '$q',
 function ($scope, Restangular, CbgenRestangular, $q) {
 
    $scope.submitJob = function () {
       var post_update_data = create_resource($scope, CbgenRestangular);
       $q.when(post_update_data.then(
                         function (object) {
                             // success!
                         },
 
                         function (object){
                             // error!
                             console.log(object.data)
                         }
                            
                     ))
                 }
 
 }])// end controller
 
 app.factory('CbgenRestangular', function (Restangular) {
         return Restangular.withConfig(function (RestangularConfigurer) {
             RestangularConfigurer.setBaseUrl('/api/v1');
         });
     })
 
 populate_scope_values = function ($scope) {
     return {name: $scope.name, description: $scope.description };
 },
 
 create_resource = function ($scope, CbgenRestangular) {
 var post_data = populate_scope_values($scope)
     return CbgenRestangular.all('job').post(post_data)
 },
 
 
 

Where to go from here

Too much to cover in just one blog post. Best practices should be your next step and head over to egghead, best tutorials on the web in my opinion. 

 

That's all Folks!

Thanks for reading. Let's keep in Touch:
Follow me on GitHub or Twitter @glynjackson


Glyn Jackson is a Python Nerd, consultant and developer.


Find out more