ng-admin + Django REST framework ですぐ作れる管理ツール

# Django REST framework と ng-admin で管理ツールを作ってみた

REST API + Static web で管理ツールを実現する方法として [ng-admin](http://ng-admin-book.marmelab.com/) と [Django REST framework](http://www.django-rest-framework.org/) を使ってみました。
本記事は Python と Javascript の使用経験がある読者を対象としています。
本記事のソースコードは以下のリポジトリに公開しています。

## ライブラリ紹介

- Django REST framework: REST API を簡単に作れる Python のウェブフレームワーク。名前のとおり Django がベースになっている。
- ng-admin: 管理ツールに特化した Javascript のクライアントライブラリ。こちらは Angularjs に依存している。

## サーバ構成

図に書くまでもありませんが、以下のようなサーバ構成になります。

```
---------------------------
Static Web (127.0.0.1:8080) // ng-admin
---------------------------
           |
---------------------------
Web API    (127.0.0.1:8000) // REST framework
---------------------------
```

## 環境構築

Pyenv (pyenv-virtualenv) で Python 3.5.2 がインストールされているものとします。
また npm も導入済みであるとします。
ちなみに筆者の環境は macOS El Capitan です。

### Django REST framework

Django REST framework をインストールします。
今回は筆者が用意したリポジトリを使うこととします。

1. リポジトリの取得  
`git clone https://github.com/algas/rest-admin-example.git`
2. virtualenv 環境の作成  
`cd rest-admin-example && pyenv virtualenv 3.5.2 rest-admin-example`
3. virtualenv の activate  
`pyenv activate rest-admin-example`
4. 依存ライブラリのインストール  
`pip install -U pip && pip install -r requirements.txt`
5. database migration  
`cd tutorial && python manage.py migrate`
6. スーパユーザの新規作成  
`python manage.py createsuperuser`  
ガイドにしたがってユーザ名、Eメールアドレス、パスワードを入力します。

環境を自前で1から作成したい方は [quickstart tutorial](http://www.django-rest-framework.org/tutorial/quickstart/) を参考にしてください。
一部の不具合修正を除けばこのチュートリアルと同じ内容を実行しています。

- Django-1.10
- djangorestframework-3.4.4

### ng-admin

1. カレントディレクトリの移動  
`cd ../js`
2. 依存ライブラリのインストール  
`npm install ng-admin --save`

上記の `npm install` は必ず js ディレクトリで実行するようにしてください。
作成される node_modules へのパスに依存しているからです。

- ng-admin-1.0.0-alpha2

## サーバの起動

Django サーバと (Static) Web サーバを別々に立ち上げるために２画面のターミナルを用意します。

### Django REST framework

1. カレントディレクトリの移動  
`cd /path/to/rest-admin-example/tutorial`
2. サーバの起動  
`python manage.py runserver`
3. 動作確認  
下記にブラウザでアクセスします。  
`http://127.0.0.1:8000/`

### ng-admin

ここでは python 3 標準のWebサーバ(http.server)を使います。nginx など他の web サーバでも問題ありません。

1. カレントディレクトリの移動  
`cd /path/to/rest-admin-example/js`
2. サーバの起動  
`python -m http.server 8080`
3. 動作確認  
下記にブラウザでアクセスします。  
`http://127.0.0.1:8000/`

## コード解説

### API

users と groups という２つのAPI群が定義されています。
各モデルの定義は以下のとおりです。

User: url, username, email, groups
Group: url, name

User は任意の数のグループに所属できます。
url は作成された API 要素へのリンクを表します。
http://127.0.0.1:8000/users/1/
http://127.0.0.1:8000/groups/1/

### 管理ページ

管理ページでは User, Group の一覧の表示と新規要素の作成機能を実装しました。
この機能は index.html と admin.js の２つのファイルで実現されています。

```html:index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Rest Admin Page</title>
        <link rel="stylesheet" href="node_modules/ng-admin/build/ng-admin.min.css">
    </head>
    <body ng-app="restAdmin">
        <div ui-view></div>
        <script src="node_modules/ng-admin/build/ng-admin.min.js" type="text/javascript"></script>
        <script src="admin.js" type="text/javascript"></script>
    </body>
</html>
```

上記は node_modules にインストールされた ng-admin の js, css に加えて、下記の admin.js を参照しています。

```js:admin.js
var restAdmin = angular.module('restAdmin', ['ng-admin']);
restAdmin.config(['NgAdminConfigurationProvider', function (nga) {
    var admin = nga.application('Rest Admin')
      .baseApiUrl('http://127.0.0.1:8000/');

    var group = nga.entity('groups').identifier(nga.field('url')).url(function(entityName, viewType, identifierValue, identifierName) {
        return 'groups/';
    });
    var groupFields = [
        nga.field('url'),
        nga.field('name')
    ]
    group.listView().fields(groupFields);
    group.creationView().fields(groupFields);
    admin.addEntity(group);

    var user = nga.entity('users').identifier(nga.field('url')).url(function(entityName, viewType, identifierValue, identifierName) {
        return 'users/';
    });
    var userFields = [
        nga.field('url'),
        nga.field('username'),
        nga.field('email'),
        nga.field('groups', 'json'),
    ]
    user.listView().fields(userFields);
    user.creationView().fields(userFields);
    admin.addEntity(user);

    nga.configure(admin);
}]);
```

まず `http://127.0.0.1:8000/` を baseApiUrl として定義しています。
次は Entity として users, groups を定義です。
ng-admin のデフォルト仕様では URI の末尾にスラッシュ(/)が入らないことを想定しています。
しかし、django framework の仕様で URI の末尾にスラッシュ(/)が必ず入るので、この差異を吸収するために url function を定義しています。
users, groups のそれぞれで listView と creationView を定義しています。
User における groups の要素は、Array で定義されるので、 ng-admin の Field Type として json を指定しています。

## 注意点

- CORS の設定  
通常の設定では `Cross-Origin Resource Sharing` の問題で API から ng-admin に JSON を渡せません。
`django_cors_middleware` を使うことでこの問題を回避しています。
`settings.py` に設定をしているので、筆者のリポジトリを参照する場合には特に対処は不要です。
- 依存性解決  
上記のライブラリの fork 元が更新されておらず Django-1.10 に対応していなかったので、Pull Req. をして来たユーザのブランチを参照することで解決しています。
これも `requirements.txt` に記載しているので問題回避できるはずです。

## 雑感

Django REST framework 自体が Web UI を提供しているので、 ng-admin と組み合わせる利点が見えにくいかもしれません。
Django REST framework は単体で使っても便利です。
ng-admin は当然のことながら、その他の Web API と連携させることもできます。