Toly blog

Python and Django notes

Руководство: Используем AngularJS с Django

Перевод статьи Глена Джексона Tutorial: Using AngularJS with Django

~~Я надеялся написать быстрое руководство, что бы вы начали использовать Angular с Django~~ которое становилось руководством по поглощению Red Bull. Мои извинения, если оно получится небрежным к концу!

Почитав посты на тему совместного использования Django и AngularJS, я чувтсвовал, что большинство из них были “велосипедными”. Хотя пример кода, который здесь приводится, сырой, он должен показать, как я использую их в проектах.

Модели

Давайте начнем с типичной модели

/jobs/models.py
1
2
3
class Job(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(null=True, blank=True)

Ок, пока ничего особенного. Все что вы сделали - это создали простую модель, которая содержит основные сведения о работе.

REST API (Tastypie)

AngularJS построен так, что бы использовать веб-сервисы, поэтому вам нужен способ плучать данные только что созданной модели Job.

Для Django есть хороший выбор вариантов для создания RESTful API. TastyPie является отличным REST фреймворком для Django. Он невероятно мощный, но простой в установке и использовании. Однако, по-моему, таких же результатов можно добиться используя Django REST framework, или даже самостоятельно создав ответы на запросы API средствами Django. Выбор остается за вами. В этом уроке мы будем использовать TastyPie.

Если вам не знаком TastyPie, ознакомьтесь с документацией. Я не буду вдаваться в подробности относительно установки. И предполагаю, он уже установлен, добавлен в INSTALLED_APPS и вы готовы продолжить.

Сперва, вам нужно создать ресурс для модели Job. TastyPie использует понятие ресурс. Это описывается как посредник между конечным пользователем и объектом (в данном случае это будет модель Job).

Начните с создания соответствующего ресурса для модели Job:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from tastypie.resources import ModelResource
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from .models import Job


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

Насколько я помню, документация TastyPie предлагает назвать файл ресурсов как api.py. Так же предлагаю и я, но это не обязательно. Вы можете назвать этот файл как угодно, но это хорошо для сохранения постоянства.

Есть несколько вещей, происходящих в JobResource, которые выходят за рамки данного руковосдства. Но, я просто хотел бы обратить внимание на то, как JobResource наследует ModelResource. Вы используете TastyPie с ORM Django (модель Job). Другими словами, многие из основных API ORM вым доступны.

TastyPie так же может обрабатывать данные и без ORM. Расширяя и наследуя ресурс, вы так же можете получить все вкусности TastyPie для API которое захотите предложить, но без привязки к ORM. Это особенно полезно при совершении запросов, не поддерживающих ORM, NoSQL баз как описано в документации.

Пока что вы создали модель данных (Job) и способ взаимодействия с нею (JobResource). Далее, вам нужен способ подключения к ресурсу через определеный URL, что в конечном итоге позволит AnagularJS его использовать. В Django вы можете сделать это используя URL роутер. Просто подключите экземпляр ресурса к определенному URL:

1
2
3
4
5
6
7
8
9
10
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)),
)

Аттрибут resource_name, определенный в JobResource - это название ресурса в URL. По нему вы знаете где находится работающее API для ресурса JobResource. Проверьте что все это работает, запустив локальный сервер и посетив адрес http://127.0.0.1:8000/api/v1/job/?format=json

Теперь у вас есть рабочее API для вашей модели Job. Просто.

Формы

Перед тем как начать использовать AngularJS нам нужно сделать форму для Job с использованием Django. Позже, эта форма позволит вам редактировать данные модели Job в одностраничном приложении. Я знаю о чем вы думаете: “Почему в Django”?

Одним из приципов в Django является прицип “на повторяйся” (DRY). Так что не имеет смысла создавать формы с помощью HTML для AngularJS, а затем делать то же для Django. К тому же Django хорошо справляется с соданием форм. Возможно у вас есть несколько форм, которые нужно преобразовать, так почему бы не сделать это автоматическим? Посмотрите в сторону django-angular. Это один классный пакет, познакомившись с которым вы будете рады (я знаю о чем говорю).

django-angular - это набор утилит, которые помогают упростить интеграцию Django и AngularJS с помощью повторно используемых компонентов

Опять же, здесь я не буду вдаваться в подробности относительно установки и настройки. Я предлагаю вам ознакомиться с документацией и начать использовать django-angular прямо сейчас! Достаточно сказать, что один из его многочисленных компонентов позволит вам использовать формы Django для валидации в AngularJS. Объединив его с пакетом crispy forms вы получите мощное решение “все-в-одном” - вот за что я люблю Django и его сообщество.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django import forms
from djangular.forms import NgFormValidationMixin, NgModelFormMixin, AddPlaceholderFormMixin
from crispy_forms.helper import FormHelper

from .models import Job


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'

AngularJS

Для простоты сделайте 3 новых шаблона, расположенных так:

1
2
3
4
templates
    jobs/index.html
    jobs/new.html
base.html

Это предполагает, что приложение jobs установлено и настроено. Базовый шаблон будет выглядеть примерно так:

/jobs/base.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!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 предлагает полезные шаблонные теги, которые включат необходимый для вас JavaScript. Я рекомендую использовать CDN и загрузить туда файлы, какие можно. Это дает географические и пропускные преимущества.

Теперь создадим шаблон страницы, который будет рендерится нашим проектом. index.html будет служить главной страницей нашего одностраничного приложения, и позже может быть использован для всех CRUD (Create Read Update Delete) представлений.

/jobs/index.html
1
2
3
4
5
6
7
8
9
10
{% 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

Шаблон и JS код выше очень простые, наследуется от базового шаблона. Есть несколько аттрибутов, которые вы возможно не видели прежде и в которых нужно будет разобраться.

Первый из них: ng-app="JobApp". Без этого тега процесс AngularJS не запустится. Эта директива говорит фреймворку AngularJS какой элемент является корневым в приложении. Все, что вы добавите внутрь этого элемента будет частью шаблона под управлением AngularJS.

Далее, посмотрите на скрипт, который вы включили в index.html. Это скрипт app.js, который является модулем AngularJS. Модуль AngularJS представляет собой набор функций, которые выполняются когда приложение загружено.

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

Строка, расположенная выше, создает модуль JobApp. В index.html вы уже создали его экземпляр с помощью директивы ng-app="JobApp". Основное, что здесь делается: сообщается AngularJS, что модуль app.js управляет содержимым этого тега.

Фактически, вы можете установить аттрибут ng-app для любого элемента в DOM. Например, если вы хотите, что бы часть шаблона не управлялась через Angular, вы можете сделать так:

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

app.config в app.js также показывает начало вашей URL-маршрутизации. AngularJS поддерживает URL-маршрутизацию по умолчанию через сервис $route в ядре Angular, но этого недостаточно и это имеет некоторые ограничения.

Один из модулей, которые вы включили, это AngularUI роутер ui.route. AngularUI роутер - это дополнительный модуль URL-маршрутизации для Angular, организованный в контексте состояний, которые опционально могуть иметь прикрепленные реакции на определенные URL’ы.

В этом руководстве предусмотрено одно состояние, которое назыаается new, но вы можете определять много различных состояний для вашего приложения. Вы даже можете добавить поведение для того случая, когда ни одного состояния не обнаружено:

1
2
3
4
5
6
7
8
$urlRouterProvider.otherwise("/");
$stateProvider
   .state('index', {

       url: "/",
       templateUrl: "static/html/somepage.html",
       controller: "SomeController"
   })

Если вы не знакомы с этим, то я предлагаю почитать о AngularUI Router после того как закончите с этим руководством.

Последний элемент в index.html, с которым вам нужно разобраться - это ui-view. Это тоже относится к AngularUI Router. Директива ui-view определяет куда разместь ваш шаблон.

Последний шаблон, который нужно создать - /jobs/new.html. Он будет содержать простую форму, которую вы сделали ранее с использованием django-angular.

/jobs/new.html
1
2
3
{% load crispy_forms_tags %}
{% crispy JobForm %}
<button type="button" class="btn btn-default"  ng-click="submitJob()">Create</button>

Теперь вам нужно всего лишь представление и URL для подключения формы.

/jobs/views.py
1
2
3
4
5
6
7
8
9
10
11
12
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

class IndexView(TemplateView):
    template_name = 'jobs/index.html'
/jobs/urls.py
1
2
3
4
5
6
7
8
9
10
11
from django.conf.urls import url
from django.conf.urls import patterns

from .views import JobFormView, IndexView

urlpatterns = patterns('',

    url(r'^job-form/$', login_required(JobFormView.as_view()), name='job_form'),
    url(r'^/$', IndexView.as_view(), name='index_job'),

)

Теперь отправьте ваш браузер по адресу http://127.0.0.1:8000/job/#new и вы должны увидеть форму для добавления работы в вашем новом одностраничном приложении.

Последний шаг - сделать возможным отправку POST-данных при нажатии на кнопку с ng-click="submitJob()". Измените контроллер как показано ниже, используя restangular.

RestAngular - это модуль для AngularJS упрощает работу с GET, DELETE и UPDATE запросами, используя минимум клентского кода. Он идеально подходит для любого веб приложения, которое получает данные через REST API.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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)
}

Что дальше

Материала слишком много для одного поста в блоге. Дальше - практиковаться и изучать видео уроки по AngularJS.

Comments