среда, 26 октября 2016 г.

Создание среды для разработки веб приложения под Django framework и Ember js

В этом howto будет расказано, каким образом создать среду для разработки веб приложения под Django framework и Ember js (DjEmber).
Права суперпользователя linux нужны только для того, чтобы установить Development Tools и node js, остальные процессы установки делаются под простым пользователем (в этом примере пользователь example).

I. Проект DjEmber - example.com
II. Проект Django - back
III. Проект Ember js - front

I. Проект DjEmber - example.com


- Пользователь - example
- Пользовательская директория /home/example/
- Проект DjEmber - example.com - будет размещаться в /home/example/Projects/example.com/
- Проект Django - back (от слова backend) - будет размещаться в /home/example/Projects/example.com/back/
- Проект Ember - front (от слова frontend) - будет размещаться в /home/example/Projects/example.com/front/
- В Django проекте - back - будет использоваться виртуальное окружение с Python версии 3
- Python 3, установленный из исходников, будет в /home/example/python3/
- Виртуальное окружение для Django проекта - back - /home/example/virtualenv/example.com
- Путь для загруженных программ - /home/example/Downloads/

1. Установка дополнительных программ

$ su -c 'yum groupinstall "Development Tools"'
$ sudo yum install wget

2. Создаю корневой каталог для проекта example.com

$ mkdir -p ~/Projects/example.com

Готовые файлы настроек можно посмотреть и скачать с https://github.com/hairetdin/djember

II. Проект Django - back


1. Установка Python 3 в домашнюю директорию пользователя /home/example/python3

- скачиваю исходники стабильной версии python 3, выбираю в https://www.python.org/downloads/source/

$ cd ~/Downloads/
$ wget https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tar.xz

- распаковываю

$ tar xvf Python-3.5.2.tar.xz

- собираю конфигурацию, указываю путь куда будет установен python3 - /home/example/python3/ и устанавливаю

$ cd Python-3.5.2/
$ $ ./configure --prefix=$HOME/python3
$ make
$ make install

- теперь python3 установлен в /home/example/python3/, проверяю

$ ~/python3/bin/python3 -V
Python 3.5.2

2. Создание виртуального окружения для проекта example.com - back (виртуальные окружения у меня находяться в ~/virtualenv)

$ cd ~
$ mkdir -p virtualenv
$ ~/python3/bin/pyvenv ~/virtualenv/example.com

- проверяю работу созданного виртуального окружения - virtualenv/example.com

$ source ~/virtualenv/example.com/bin/activate

в терминале должна появиться строка начинающаяся с - (example.com). Теперь запущу python и посмотрю версию

$ python -V
Python 3.5.2

- деактивация виртуального окружения:

$ deactivate


3. Установка python приложений в виртуальное окружение virtualenv/example.com

- Активирую виртуальное окружение и устанавливаю python приложения

$ source ~/virtualenv/example.com/bin/activate
$ pip install django
$ pip install djangorestframework
$ pip install djangorestframework-jwt
$ pip install django-filter
$ pip install django-cors-headers

Если не будете использовать django-filter, то ставить необязательно.


4. Создаю Django бакэнд проект для example.com

Виртуальное окружение virtualenv/example.com дожно быть активировано.

- перехожу в корень проекта example.com

$ cd ~/Projects/example.com/

- создаю проект back

$ django-admin startproject back

- проверяю созданные Django проект

$ cd ~/Projects/example.com/back/
$ python manage.py runserver

Смотрю в браузере http://127.0.0.1:8000/ - работает, показывает страницу приветствия - "Congratulations on your first Django-powered page."

Хорошо, останавливаю запущенный back проект - Ctrl+C

5. Делаю настройки для ранее установленных python приложений в ~/Projects/example.com/back/back/settings.py

Я для этого я использую редактор Atom, открываю на редактирование settings.py и вношу следующие изменения:

- в INSTALLED_APPS добавляю 'rest_framework' и 'corsheaders'

INSTALLED_APPS = (
   ...
   # external apps
   'rest_framework',
   'corsheaders',
)

- в MIDDLEWARE_CLASSES добавляю 'corsheaders.middleware.CorsMiddleware',

MIDDLEWARE_CLASSES = [
   ...
   'corsheaders.middleware.CorsMiddleware',
   'django.middleware.common.CommonMiddleware',
   ...
]

- добавляю новую конфигурацию для REST_FRAMEWORK

REST_FRAMEWORK = {
   'DEFAULT_PERMISSION_CLASSES': [
# разрешаю подключение к api всем, ограничивать подключения буду во view
'rest_framework.permissions.AllowAny',
   ],
   'DEFAULT_AUTHENTICATION_CLASSES': (
# аутентификацию делаю на уровне сессий (для прямых веб подключений) и с использованием token для фронтэнда
'rest_framework.authentication.SessionAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
   ),
   'PAGE_SIZE': 1000,
}

- для CORS разрешаю все запросы во время разработки (DEBUG = True, а значит получится и CORS_ORIGIN_ALLOW_ALL = True):

CORS_ORIGIN_ALLOW_ALL = DEBUG

- для djangorestframework-jwt (JSON Web Token) добавляю настройки:

JWT_AUTH = {
   'JWT_RESPONSE_PAYLOAD_HANDLER': 'back.views.jwt_response_payload_handler',
   'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}

- настройки settings.py закончены, сохраняю.

6. Создаю ~/Projects/example.com/back/back/views.py, открываю на редактирование и добавляю функцию:

def jwt_response_payload_handler(token, user=None, request=None):
   return {
'access_token': token,
   }

Для чего нужна эта функция? Все дело в том, что djangorestframework-jwt, по умолчанию, при запросе ключа возвращает 'token': длинныйдлинныйключ. А ember-simple-auth хочет получить 'access_token': длинныйдлинныйключ , вот поэтому и необходимо сделать эту функцию.

7. В ~/Projects/example.com/back/back/urls.py добавляю api для аутентификации по ключу:

from rest_framework_jwt.views import obtain_jwt_token
#...

urlpatterns = patterns(
   '',
   # ...

   url(r'^api/auth/login/', obtain_jwt_token),
)

8. Делаю миграцию, создаю суперпользователя - admin, запускаю проект back

$ source ~/virtualenv/example.com/bin/activate
$ cd ~/Projects/example.com/back/
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver

- Теперь через браузер проверяю аутентификацию по адресу http://127.0.0.1:8000/api/auth/login/ , после ввода логина и пароля и нажатии на кнопку "POST", должен появиться ключ - "access_token":"длинныйдлинныйключ".
- Не останавливая проект, запускаю новое терминальное окно и проверяю аутентификацию через утилиту командной строки - curl:

$ curl -X POST -d "username=admin&password=парольсуперпользователя" http://127.0.0.1:8000/api/auth/login/

В ответ должен вернуться - {"access_token":"длинныйдлинныйключ"} . Если все так, значит все настроено правильно.

- На этом настройка бакэнда закончена. Приступаю к запуску и настройке фронтэнда.


III. Проект Ember js - front


1. Создаю ember проект - example.com/front

$ cd ~/Projects/example.com
$ sudo npm install -g ember-cli
$ ember new front

2. Запускаю вновь созданный ember проект - front (команда "ember server" или, сокращенно, "ember s")

$ cd ~/Projects/example.com/front
$ ember s

- При запуске вылетает ошибка "Could not start watchman; falling back to NodeWatcher for file system events.", безопасно игнорируем ее как советует https://ember-cli.com/user-guide/#watchman. Хоть я и установил watchman по инструкции - https://facebook.github.io/watchman/docs/install.html, но при запуске команды "ember -v" под пользователем example получаю ошибку, а при запуске от root вышеприведенной ошибки нет. Если знаете как исправить - подскажите.

- смотрю в браузере http://localhost:4200/ - работает

3. Устанавливаю ember-django-adapter адаптер. front (ember) должен понимать как стучаться в back (django)

$ cd ~/Projects/example.com/front
$ ember install ember-django-adapter

- Добавляю в настройки фронтэнда строку - ENV.APP.API_HOST = 'http://localhost:8000';
Редактирую файл настроек Ember проекта: ~/Projects/example.com/front/config/environment.js

if (environment === 'development') {
 ENV.APP.API_HOST = 'http://localhost:8000';
 ...
}

 Т.е. при разработке, когда django проект back будет запущен командой "python manage.py runserver", ember проект front будет обращаться к http://localhost:8000/. Наименование api можно изменить допустим на my-api, подробности в документации http://dustinfarris.com/ember-django-adapter/ .

4. Устанавливаю аутентификацию для Ember фронэнда

$ cd ~/Projects/example.com/front
$ ember install ember-simple-auth


5. Создаю фронтэнд приложение - application

- Т.к. в Ember js, по умолчанию, роуты начинаются с приложения application, то и аутентификацию прилеплю на странице application (application хоть и не виден в главном router.js, но при запуске ember проекта сначала обрабатываются именно application). Простыми словами, application - это базовые настройки для Ember js проекта.
Я буду использовать привычный для Django порядок - каждое приложение складывается в отдельную папку, для этого используется в команде "ember generate" с дополнительным ключом "--pod". Команду "ember generate" можно сократить до "ember g".

- Создаю template, controller, adapter для application.

$ cd ~/Projects/example.com/front/
$ ember g route application --pod
$ ember g controller application --pod
$ ember g adapter application --pod

После выполнения этих команд во front/app создалась папка приложения - application и внутри нее файлы route.js, controller.js, adapter.js и template.hbs. Таким образом используя ключ --pod создалась отдельная папка apllication в которой разместились роут, контроллер, адаптер и темплейт. Для меня такая структура более приемлема, потому что в дальнейшем можно быстро копировать приложения в другие проекты.


6. Редактирую темплейт app/application/template.hbs, добавляю:

{{!-- app/application/template.hbs --}}
<div class="menu">
 {{#if session.isAuthenticated}}
   <a href {{action 'invalidateSession'}}>Выход</a>
 {{else}}
   {{#link-to 'login'}}Вход{{/link-to}}
 {{/if}}
</div>
<div class="main">
 {{outlet}}
</div>

Таким образом, для "Выход" навешиваю действие - action 'invalidateSession',а для "Вход" ссылку переход на страницу login - #link-to 'login'. Строка {{outlet}} показывает, что весь вышеприведенный код созданного темплейта application будет наследоваться нижестоящими темплейтами, т.е всеми остальными темплейтами, которые будут созданы в проекте.

7. Редактирую контроллер app/application/controller.js, внедряю в проект сессии и создаю action(действие) - invalidateSession():

// app/application/controller.js
import Ember from 'ember';

export default Ember.Controller.extend({
 session: Ember.inject.service('session'),

 actions: {
   invalidateSession() {
     this.get('session').invalidate();
   }
 }
});

8. Добавляю аутентификацию в applicaton route

- Сервис сессий имеет события authenticationSucceeded и invalidationSucceeded, которые показывают прошла аутентификация или нет. Чтобы эти события были доступны в сесии на всех страницах сайта добавляю в app/application/route.js:

// app/application/route.js
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';

export default Ember.Route.extend(ApplicationRouteMixin);

9. Добавляю authorizer(авторизатор) в adapter

- после того как аутентификация будет успешно выполнена, для повторного доступа к веб api бакэнда нужен авторизатор. Т.к. адаптер я использую уже готовый - ember-django-adapter, то в application я его расширю указав на авторизатор 'authorizer:oauth2', который создам далее. А пока редактирую app/application/adapter.js :

// app/application/adapter.js
import DRFAdapter from '../adapters/drf';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';

export default DRFAdapter.extend(DataAdapterMixin , {
 authorizer: 'authorizer:oauth2'
});

10. Создаю аутентификатор и авторизатор oauth2

$ cd ~/Projects/example.com/front/
$ ember g authenticator oauth2
$ ember g authorizer oauth2

- редактирую app/authenticators/oauth2.js (использую стандартную аутентификацию OAuth2PasswordGrant из ember-simple-auth)

// app/authenticators/oauth2.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default OAuth2PasswordGrant.extend({

 serverTokenEndpoint: 'http://127.0.0.1:8000/api/auth/login/',

});


- редактирую app/authorizers/oauth2.js (использую стандартный авторизатор - OAuth2Bearer из ember-simple-auth)

// app/authorizers/oauth2.js
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';

export default OAuth2Bearer.extend();


11. Приложение login

В app/application/template.hbs я указал использовать для "Вход" переход на login страницу.

- Для приложение login создаю route, controller (template создается автоматом при создании route):

$ cd ~/Projects/example.com/front/
$ ember g route login --pod
$ ember g controller login --pod

При создании роута для login в основном роуте проекта - app/router.js - добавились строки для маршрутизации login приложения:

Router.map(function() {
 this.route('login');
});

теперь проект front знает о приложении login.

- Создаю логин форму, редактирую app/login/template.hbs:

{{!-- app/login/template.hbs --}}
<form {{action 'authenticate' on='submit'}}>
 <label for="identification">Login</label>
 {{input id='identification' placeholder='Enter Login' value=identification}}
 <label for="password">Password</label>
 {{input id='password' placeholder='Enter Password' type='password' value=password}}
 <button type="submit">Вход</button>
 {{#if errorMessage}}
   <p>{{errorMessage}}</p>
 {{/if}}
</form>

У этой формы имеется действие (action) - {{action 'authenticate' on='submit'}} - при нажатии на кнопку "Вход".

- Описываю в login контроллере действие authenticate() при нажатии на кнопку "Вход" (так как при аутентификации используются сессия, то надо и ее объявить через переменную session), редактирую app/login/controller.js:

// app/login/controller.js
import Ember from 'ember';

export default Ember.Controller.extend({
 session: Ember.inject.service('session'),

 actions: {
   authenticate() {
     let { identification, password } = this.getProperties('identification', 'password');
     this.get('session').authenticate('authenticator:oauth2', identification, password).
then(() => {
 console.log('Success! authenticated with token: ' + this.get('session.data.authenticated.access_token'));
}, (err) => {
 alert('Error obtaining token: ' + err.responseText);
}).
catch((reason) => {
this.set('errorMessage', reason.error || reason);
     });
   }
 }
});


При выполнении authenticate() получаем логин и пароль из формы, затем для session выполняем метод authenticate, для которого в качестве аргументов передаем ранее созданный authenticator:oauth2, а также логин и пароль.


- На этом настройка ember js фронтэнда - example.com/front завершена.


Проверяю работу среды разработки DjEmber для example.com

- запускаю бакэнд в 1 терминальном окне

$ source ~/virtualenv/example.com/bin/activate
$ cd ~/Projects/example.com/back/
$ python manage.py runserver


- запускаю фронтэнд во 2 терминальном окне

$ cd ~/Projects/example.com/front/
$ ember s

- запускаю Google Chrome, открываю http://localhost:4200/, запускаю консоль - F12 -> Console.
- нажимаю кнопку вход, ввожу login и password (использую пользователя суперюзер созданного в Django back), нажимаю вход и смотрю в консоль, появилось сообщение:

Success! authenticated with token: длинныйдлинныйключ

DjEmber окружение для разработки готово. Теперь можно создавать api на бакэнде под django-rest-framework и подключаться к нему из ember фронтэнда.
Удачных вам проектов!!!

1 комментарий:

  1. На фронтэнде мне понадобился текущий пользователь, т.е. пользователь который прошел аутентификацию. На бакэнде я добавил в файл
    ~/Projects/example.com/back/back/views.py
    следующие строки:

    from rest_framework import serializers

    from django.contrib.auth.models import User


    class UserSerializer(serializers.ModelSerializer):
    class Meta:
    model = User
    fields = ('id', 'username')


    def jwt_response_payload_handler(token, user=None, request=None):
    return {
    'access_token': token,
    'user': UserSerializer(user, context={'request': request}).data,
    }

    Таким образом, после аутентификации на фронтэнде при
    this.get('session.data.authenticated.user')
    я получаю id и username для текущего user.

    ОтветитьУдалить

django-oscar tinymce 4 filebrowser

Задача: в дашборде django-oscar загружать изображения 1. Установка django-filebrowser-no-grappelli - Открываем проект, загружаем виртуа...