Django REST frameworkでTodoアプリを作ってみる

Django REST frameworkを使用して、シンプルなTODOアプリを作成します。

**作成するアプリの特徴**

- TODOの作成／完了を管理するシングルページアプリ
- SQLite3にTODOを保管
- Django REST frameworkを使用
- RESTfulなAPIを作成

**環境・バージョン**
Windows 7
Python 3.4.3
Django 1.8.2
Django REST framework 3.1.2

**成果物**
https://github.com/koji-ohki-1974/django-rest-todo

#準備
PythonおよびDjangoは導入済みとします。

###Django REST frameworkの導入
以下のコマンドを実行し、Django REST frameworkを導入します。

```
pip install djangorestframework
```

###プロジェクトの作成
Djangoのプロジェクトを作成します。任意のフォルダで、以下のコマンドを実行します。

```
django-admin.py startproject django_rest_todo
``` 

###データベースのセットアップ
データベースを初期化し、スーパーユーザーを作成します。

```sh
cd django_rest_todo
python manage.py migrate
python manage.py createsuperuser
```

スーパーユーザーのＩＤとメールアドレス、パスワードを設定します。

|項目|値|
|:---------------|:--------------------|
| Username      |admin|
| Email address |admin@any.com|
| Password       |admin|

###サーバーの起動・動作確認
以下のコマンドを実行します。

```
python manage.py runserver
```
 
ブラウザを起動し[http://localhost:8000/](http://localhost:8000/)にアクセスします。
プロジェクトの作成が成功していれば、「It worked!」という画面が表示されます。

「CTRL + C」でサーバーを終了します。
 
###アプリケーションの作成
プロジェクトフォルダで、以下のコマンドを実行し、アプリケーションを作成します。

```sh
cd django_rest_todo
django-admin.py startapp api
```

プロジェクトの設定を変更しておきます。「settings.py」を以下の通り変更します。

```Python:settings.py（抜粋）
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'django_rest_todo.api',
)

MIDDLEWARE_CLASSES = (
#  'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
#  'django.middleware.csrf.CsrfViewMiddleware',
#  'django.contrib.auth.middleware.AuthenticationMiddleware',
#  'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
#  'django.contrib.messages.middleware.MessageMiddleware',
#  'django.middleware.clickjacking.XFrameOptionsMiddleware',
#  'django.middleware.security.SecurityMiddleware',
)
```

# APIの作成
### Todoモデル
Todoのモデルを定義します。「api/models.py」を編集し、Todoクラスを追加します。
 
```Python:api/models.py
from django.db import models

class Todo(models.Model):
    text = models.TextField()
```
 
 モデルの定義が完了したら、データベースに反映します。以下のコマンドを実行します。

```sh
cd ..
python manage.py makemigrations api
python manage.py sqlmigrate api 0001
python manage.py migrate
```

###シリアライザの作成
モデルをJSONにマッピングするシリアライザを定義します。
「api/serializers.py」を作成します。

```Python:api/serializers.py
from .models import Todo
from rest_framework import serializers

class TodoSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'text')
```

###ビューの作成
REST用のビューを作成します。「api/views.py」にTodoViewSetクラスを追加します。

```Python:api/views.py
from rest_framework.viewsets import ModelViewSet
from .models import Todo
from .serializers import TodoSerializer

class TodoViewSet(ModelViewSet):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer
```

###API ルーティング
APIを処理できるようにルーティングの設定をおこないます。
「urls.py」を変更します。

```Python:urls.py
from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter
from .api import views

router = DefaultRouter()
router.register(r'todos', views.TodoViewSet)

urlpatterns = [
    url(r'^api/', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
```

###サーバーの起動・動作確認
再度、サーバーを起動し、APIの動作確認をおこないます。以下のコマンドを実行します。

```
python manage.py runserver
```
 
ブラウザを起動し、以下にアクセスします。

http://localhost:8000/api/
http://localhost:8000/api/todos/

「CTRL + C」でサーバーを終了します。

#フロントエンドの作成
###静的ファイル

静的ファイル（.htmlと.js）の配置場所を指定します。「settings.py」を編集し、以下を追加します。

```Python:settings.py
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)
```

「static」フォルダを作成します。

```sh
mkdir static
```

###HTMLファイル
Todoリストの画面を作成します。「static/index.html」を作成します。

```HTML:static/index.html
<!doctype html>
<html ng-app="djangoTodo">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">

	<title>Django REST framework Todo App</title>

	<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
	<style>
		html                    { overflow-y:scroll; }
		body                    { padding-top:50px; }
		#todo-list              { margin-bottom:30px; }
	</style>

	<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
	<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
	<script src="core.js"></script>

</head>
<body ng-controller="mainController">
	<div class="container">

		<div class="jumbotron text-center">
			<h1>Todoリスト <span class="label label-info">{{ todos.length }}</span></h1>
		</div>

		<div id="todo-list" class="row">
			<div class="col-sm-4 col-sm-offset-4">

				<div class="checkbox" ng-repeat="todo in todos">
					<label>
						<input type="checkbox" ng-click="deleteTodo(todo.id)"> {{ todo.text }}
					</label>
				</div>

			</div>
		</div>

		<div id="todo-form" class="row">
			<div class="col-sm-8 col-sm-offset-2 text-center">
				<form>
					<div class="form-group">

						<input type="text" class="form-control input-lg text-center" placeholder="Todoを入力してください" ng-model="formData.text">
					</div>

					<button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">追加</button>
				</form>
			</div>
		</div>

	</div>

</body>
</html>
```

###JavaScriptファイル
JavaScriptで、APIをコールする処理を記述します。「static/core.js」を作成します。

```Javascript:static/core.js
var djangoTodo = angular.module('djangoTodo', []);

function mainController($scope, $http) {

    $scope.readTodos = function() {
        $http.get('/api/todos/')
            .success(function(data) {
                $scope.formData = {};
                $scope.todos = data;
                console.log(data);
            })
            .error(function(data) {
                console.log('Error: ' + data);
            });
    };

    $scope.createTodo = function() {
        $http.post('/api/todos/', $scope.formData)
            .success(function(data) {
                console.log(data);
                $scope.readTodos();
            })
            .error(function(data) {
                console.log('Error: ' + data);
            });
    };

    $scope.deleteTodo = function(id) {
        $http.delete('/api/todos/' + id + '/')
            .success(function(data) {
                console.log(data);
                $scope.readTodos();
            })
            .error(function(data) {
                console.log('Error: ' + data);
            });
    };

    $scope.readTodos();

}
```

###リダイレクト
トップページ「/」へのリクエストを「static/index.html」にリダイレクトするように設定します。「urls.py」を変更します。

```Python:urls.py
from django.conf.urls import include, url
from django.views.generic import RedirectView
from rest_framework.routers import DefaultRouter
from .api import views

router = DefaultRouter()
router.register(r'todos', views.TodoViewSet)

urlpatterns = [
    url(r'^api/', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    url('', RedirectView.as_view(url='/static/index.html')),
]
```

#動作確認
###サーバーの起動・動作確認
サーバーを起動します。

```
python manage.py runserver
 ```
 
ブラウザを起動し[http://localhost:8000/](http://localhost:8000/)にアクセスします。
Todoリストが表示されれば完成です。