勉強会用の資料です.
以前本家チュートリアルを辿った資料を作っていましたが,
しばらく放置している間にdjangoのバージョンが2になったのと,
シングルページアプリケーション化を目標にするため,一度仕切り直します.
フロントエンドはvue-cli,バックエンドはdjango(rest-frameworks)を利用して,
本家チュートリアルをSPA化していきます.
例のごとくgitに作業開始,途中のソースコードを置くので,途中から始めるかたはcloneしてください.
ソースコードはgithubに置いてます → https://github.com/usa-mimi/tutorial-spa
環境構築
仮想環境を作成してから作業することを推奨します.
仮想環境の作成方法は過去記事等を参考にしてください.
私はvirtualenv(virtualenvwrapper)を使用していますが,
python3.3からはvenvが標準でついているので好きなものを使用してください.
この記事を書いている時点でのpython, pip, Djangoのバージョンは以下の通りです.
python 3.6.4
pip 9.0.3
Django 2.0.3
バージョンが多少前後しても動くと思いますが,python3.7でdjango1.11を動かそうとするとエラーになります.
仮想環境の作成
まず仮想環境を作成します.
virtualenvwrapperを使用している場合は作り方が異なりますのでこの節はスキップして大丈夫です.
仮想環境の名前は tutorial-spa
にしました.
また,pythonは2系ではなく3系を使用したいので -p python3
オプションでpython3を指定しています.
$ mkvirtualenv -p python3 tutorial-spa
Running virtualenv with interpreter /usr/local/bin/python3
Using base prefix '/usr/local/Cellar/python/3.6.4_3/Frameworks/Python.framework/Versions/3.6'
New python executable in /Users/shimomura/.virtualenvs/tutorial-spa/bin/python3.6
Also creating executable in /Users/shimomura/.virtualenvs/tutorial-spa/bin/python
Installing setuptools, pip, wheel...done.
virtualenvwrapper.user_scripts creating /Users/shimomura/.virtualenvs/tutorial-spa/bin/predeactivate
virtualenvwrapper.user_scripts creating /Users/shimomura/.virtualenvs/tutorial-spa/bin/postdeactivate
virtualenvwrapper.user_scripts creating /Users/shimomura/.virtualenvs/tutorial-spa/bin/preactivate
virtualenvwrapper.user_scripts creating /Users/shimomura/.virtualenvs/tutorial-spa/bin/postactivate
virtualenvwrapper.user_scripts creating /Users/shimomura/.virtualenvs/tutorial-spa/bin/get_env_details
作業ディレクトリの用意
tutorialアプリのためのディレクトリを作成し,
その中にdjango用のディレクトリとフロント(js)用のディレクトリを配置するようにします.
チュートリアルのためディレクト階層を深くし,バックエンドとフロントエンドをまとめてますが,
実際に作成する場合はそれぞれ別リポジトリで管理するほうがいいです.
同じリポジトリにまとめるとフロントとバックエンドのバージョンの差異はなくなりますが,
フロントとバックエンドの作業分担がしづらくなります.
また,circleCIなどでpush時に自動テストを実行するCIアプリを利用している場合
フロントの修正だけでバックエンドのテストが実行されてしまいます.
$ mkdir tutorial-spa
$ cd tutorial-spa
まずはディレクトリを作って移動するだけです.
ここにバックエンドとフロントエンドのプログラムを作っていきます.
djangoプロジェクトの作成(バックエンドの設置)
ソース: → f34594e
仮想環境にworkonした上でまずはdjangoをinstallし,新規プロジェクトを作成します.
(tutorial-spa) $ pip install django
Collecting django
Using cached Django-2.0.3-py3-none-any.whl
Collecting pytz (from django)
Using cached pytz-2018.3-py2.py3-none-any.whl
Installing collected packages: pytz, django
Successfully installed django-2.0.3 pytz-2018.3
pip install django 3.39s user 2.91s system 93% cpu 6.737 total
(tutorial-spa) $ django-admin startproject tutorial
ここまででこういうファイル構成になっているはずです.
tutorial-spa/
└── tutorial/
├── manage.py
└── tutorial/
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
pollsアプリケーションの追加
本家: https://docs.djangoproject.com/ja/2.0/intro/tutorial01/
pollsアプリを追加します.
本家ではviews.pyを編集してますが,API化する場合は通常のレスポンスは必要ないの記述は不要です.
追加
ソース: f34594e
→ ac92ba1
startapp
でpollsアプリを追加して, settings.py
のINSTALL_APPSに追加します.
- プロジェクトのルートディレクトリに移動
manage.pyがある,プロジェクトの(tutorial)ルートディレクトリに移動します.
manage.pyを利用して様々なコマンド処理(アプリの新規作成など)をすることができます.
(tutorial-spa) $ python manage.py startapp polls
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'polls',
]
polls/models.py の編集
ソース: ac92ba1
→ f3c16d6
本家: https://docs.djangoproject.com/ja/2.0/intro/tutorial02/
polls/models.py
にpollsで使用するモデルを記述します.
この辺は手抜きで本家からのコピペです.
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
DBへの適用
ソース: f3c16d6
→ f6cc667
makemigrations
コマンドとmigrate
コマンドを実行してDBを作成します.
実行すると db.sqlite3
ファイルが作成されます.
makemigrations
はmodels.py(と存在する場合は前回のmigrateファイル)から,
DBを作成(もしくは変更/削除)するためのmigrateファイルを作成するコマンドです.
models.pyを変更した場合はmakemigrations
でmigrateファイルを作成し,
migrate
コマンドでDBへ変更を適用する,という流れになります.
初めて実行する場合はdjangoが使用するテーブルも作成されます.
(tutorial-spa)$ python manage.py makemigrations polls
Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Choice
- Create model Question
- Add field question to choice
(tutorial-spa) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying polls.0001_initial... OK
Applying sessions.0001_initial... OK
管理サイトに追加
ソース: f6cc667
→ 7945f45
管理サイトからDBの中身を確認できるように, polls/admin.py
を編集して
Question
モデルと Choice
モデルを追加します.
from .models import Question, Choice
admin.site.register(Question)
admin.site.register(Choice)
確認
ソース: 7945f45
(このセクションでの変更なし)
管理画面から見えることを確認します.
- 管理画面に入れるようにsuperuserを作成します.
(tutorial-spa) $ python manage.py createsuperuser
Username (leave blank to use 'shimomura'): admin
Email address:
Password:
Password (again):
Superuser created successfully.
- 開発サーバを立ち上げ
(tutorial-spa) $ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
March 31, 2018 - 07:26:23
Django version 2.0.3, using settings 'tutorial.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
この状態でブラウザを開き,以下のURLを叩くと管理ページへのログイン画面が表示されるので,
そこで先ほど作成した username, passwordを入力します.
ログイン後,pollsの下に2つのモデルがあることを確認します.
API化
DjangoRESTframeworkを使用してAPI化を行います.
ライブラリのインストール/設定
ソース: 7945f45
→ 2d3388c
pipで djangorestframework
をインストールします.
(tutorial-spa) $ pip install djangorestframework
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # <-------- 追加
'polls',
]
ついでなのでこのタイミングでtimezoneと言語を日本語に変更します.
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ja'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Tokyo'
これで管理サイトが日本語化されます.
APIレスポンス(serializerクラス)の定義
ソース: 2d3388c
→ d802bad
APIの戻り値を設定するためにserializerを定義していきます.
serializerはdjangoのForm
のようなものですが,表示のためのwidget
を持っていません.
その代わり,データを階層的に表現できます.
また,新規作成時の処理,更新時の処理をそれぞれ独立メソッドで定義します.
ModelFormの場合はsaveメソッド内でinstanceがあるかどうかを自分で記述する必要があります
serializerクラスの定義は慣例的に serializers.py
ファイルを作り,その中に記述していきます.
from rest_framework import serializers
from .models import Question, Choice
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = (
'id',
'question_text',
'pub_date',
)
class ChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = Choice
fields = (
'id',
'question',
'choice_text',
'votes',
)
fields
は羅列せずに,fields = '__all__'
と書くこともできます.
__all__
と書くとModelの全フィールドが対象となります(ModelFormの__all__と同じです)
Viewクラスの定義
ソース: d802bad
→ 9aff5a9
次にAPIのためのViewクラスを定義します.
DjangoRESTFrameworkではAPIに特化してViewクラスを提供しています.
RESTではあるリソースとURIを紐付け,さらに HTTPメソッド を CRUD操作 に紐付ける,ということがよく行われます.
CRUDは,Create, Read, Update, Deleteの略です.
厳密には異なる部分があり,よく議論の対象となっていますが,大抵の場合は
POST→Create,GET→Read,PUT(PATCH)→Update,DELETE→Deleteと割り当てることができます.
そのため,DRFではそれぞれのメソッドを定義したViewSet
を提供しています.
今回はその派生クラスであるReadOnlyModelViewSet
を使ってみます.
なお,ソースコードは通常のviewと区別するため,api_views.py
というファイルを作成し,そこに記述していきます.
from rest_framework.viewsets import ReadOnlyModelViewSet
from .models import Question
from .serializers import QuestionSerializer
class QuestionViewSet(ReadOnlyModelViewSet):
queryset = Question.objects.all() # ここが対象となるレコードの指定.今回は全部
serializer_class = QuestionSerializer # 戻り値を定義したSerializer
url設定
ソース: 9aff5a9
→ aac490f
通常のdjango同様,作成したviewとurlを紐付けます.
今回はViewSet
を使用したため,通常のdjangoと若干書き方がことなります.
ViewSetではメソッドをHTTPメソッドと紐付けるために,as_view()
の呼び出し時に引数に辞書を与え,
ViewSet.as_view({'get': 'list', 'post': 'create'})
のように書きます.
この操作もほぼ決まりきったルールがあるため,DRFではRouterクラスを提供しています.
RouterはViewSetのメソッドを自動的にURLに展開してくれます.
このソースコードも,通常のurls.pyと区別するため, api_urls.py
に書くことにします.
from rest_framework.routers import DefaultRouter
from . import api_views
question_router = DefaultRouter()
question_router.register(r'', api_views.QuestionViewSet)
さらにRootのurls.pyを編集し,polls/api_urls.py
で定義したrouterを紐付けます.
from django.contrib import admin
from django.urls import path, include # includeを追加
from polls.api_urls import question_router # 定義したquestion_routerをimport
api_urlpatterns = [ # apiのURL一覧 (まだquestionだけ)
path('questions/', include(question_router.urls)), # 慣例として複数形にする
]
urlpatterns = [
path('admin/', admin.site.urls),
path('api/1.0/', include(api_urlpatterns)), # api/1.0/としてapi一覧を登録
]
apiはurlの一部にv1
や1.0
などのようにバージョン番号を付けておくと後々の改修時に役立ちます.
api用のURLは一箇所でまとめて定義したほうが,フロントエンド開発時見直す時に見やすいです.
今回はしてませんが,tutorial/api_urls.py
として別に書き出してもいいと思います.
確認
admin画面 ( http://localhost:8000/admin/polls/question/ ) から適当なテストデータを登録し,
apiの戻り値を確認してみましょう.
DRFではブラウザからアクセスするとapiの戻り値をキレイに整形して表示してくれます.
http://localhost:8000/api/1.0/questions/
にアクセスすると以下のように表示してくれるはずです.
また,ReadOnlyModelViewSet
ではリストの他に,詳細表示用のメソッド (retrieve) が定義されており,
DefaultRouter
によって /pk/
として自動でURLが登録されます.
http://localhost:8000/api/1.0/questions/1/
にアクセスすると以下のように表示してくれるはずです.
関連ライブラリの吐き出し
ソース: aac490f
→ 8b39332
djangoやdjangorestframeworkなど,pipでインストールしたライブラリを書き出しておきましょう.
pip7.1からconstraintsが使えるようになったので,本チュートリアルではそれを活用していきます.
インストール済みのライブラリはシェルで pip freeze
と打つことで確認できます.
従来のやり方ではこれを requirments.txt
に書き出してましたが,今回は constraints.txt
として書き出します.
$ pip freeze > constraints.txt
また,手動で requirements.txt
を編集し, pip install
コマンドで指定したライブラリを書きます.
Django
djangorestframework
ライブラリを復元するときは
$ pip install -r requirements.txt -c constraints.txt
とすればOKです.
$ pip freeze > requiremnts.txt
で書き出し,
$ pip install -r requirements.txt
で復元しても結果としては同じですが,
pip install
コマンドではライブラリが必要としている関連ライブラリも自動でインストールされます.
そのため,自分が必要としていないライブラリまでrequirements.txtに含まれてしまいます.先ほど
pip freeze
で吐き出したconstraints.txt
の中身を確認すると以下のようになっているはずです.constraints.txt
Django==2.0.3
djangorestframework==3.7.7
pytz==2018.3
>
>pytzはインストールした覚えがないと思いますが,こいつはDjangoの関連ライブラリとして自動でインストールされます.
>このように,自分が本当に必要でインストールしたものと,結果としてインストールされたライブラリ群を分けて管理することで,
>後で必要のないライブラリを削除したり,アップデートを行うことが容易になります.
---
次はフロント側を作っていきます.
→ [チュートリアル2へ](https://qiita.com/maisuto/items/2bf1b072f189a65d6f6e)
→ [チュートリアルまとめ](http://qiita.com/maisuto/items/17653f344d1f64afa019)