勉強会
ちょっと業務でRESTfulなAPIを作成する必要が出てきました。
3/27 (水) に再度勉強会開くのでぜひ。
3/14の内容復習編。
Djangoプロジェクトの作成
Python環境にはpipenv
を使用します。
# 仮想環境の作成(python 3.6) $ pipenv install --python 3.6 # 作成した仮想環境の起動 $ pipenv shell
startproject
を使用すると、適切なディレクトリ構成でプロジェクトの鋳型を作成してくれます。
$ django-admin startproject library_project
作成したばかりは以下のような構成になっています。
$ tree library_project/ library_project/ ├── library_project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py
__init__.py
Pythonでは、ディレクトリをパッケージの一部として使用する際にこのファイルが存在する必要があります。settings.py
全ての設定に関する項目を指定します。urls.py
トップレベルのURLに関するルーティングを定義します。wsgi.py
WebサーバーとWebアプリケーションをつなぐ共通のインターフェースに関する設定ファイルです
本番環境にデプロイする際にはいじる必要があります。manage.py
Django関連のコマンドはこのスクリプトを介して実行します。webサーバーの立ち上げなど。
migrate
初期データベースの作成には、migrate
コマンドを使用します。
Djangoはデフォルトでsqlite3を使用します。
$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, 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 admin.0003_logentry_add_action_flag_choices... 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 sessions.0001_initial... OK
http://127.0.0.1:8000 に接続すると。。
Djangoのデフォルト画面が広がっています。
アプリケーションの作成
Djangoプロジェクトは複数のアプリケーションを持つことができます。
startapp
コマンドを使用すると、またしても適切なディレクトリ構成でアプリケーションの鋳型を作成してくれます。
$ django-admin startapp library_project
アプリケーションを1つ持ったプロジェクトは、以下のような構成になっています。
$ tree . ├── books │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── db.sqlite3 ├── library_project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py
admin.py
管理画面に関する設定ファイルです。app.py
アプリケーションに関する設定ファイルです。migrations/
データベースの変更に使用するSQL文はpython manage.py makemigrations
コマンドにより、この中に格納されます。models.py
データベースのモデルを定義するファイルです。tests.py
アプリケーションのテストに使用するモジュールを格納します。views.py
request/responseのロジックに関わる部分について記述します。
これらとは別に、アプリケーションごとにurls.py
を作成し、各アプリでのルーティングを定義することが多いです。
INSTALLED_APPS
での注意点
djangoでは、settings.py
中のINSTALLED_APPS
という環境変数にクラスを追加していくことでアプリケーションを認識させます。
INSTALLED_APPS = [ # local 'books.apps.BookConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
When several applications provide different versions of the same resource (template, static file, management command, translation), the application listed first in INSTALLED_APPS has precedence. https://docs.djangoproject.com/en/2.1/ref/settings/#installed-apps
djangoではINSTALLED_APPS
に指定したアプリケーションは最初にリストされるアプリケーションのリソースが優先されます。
そのため、独自アプリケーションに関する設定は、デフォルトで定義された管理画面に関する設定の上に記述することに注意します。
model名の登録
models.py
で定義したクラスに __str__
メソッドを定義することで、管理サイトで表示するデータベース中のカラム名が指定できます。
class Book(models.Model): title = models.CharField(max_length=250) subtitle = models.CharField(max_length=250) author = models.CharField(max_length=100) isbn = models.CharField(max_length=13) def __str__(self): return self.title
migration
python manage.py makemigrations
でSQL文の発行、python manage.py migrate
で発行したSQL分の適用を行います。
$ python manage.py makemigrations Migrations for 'books': books/migrations/0001_initial.py - Create model Book $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, books, contenttypes, sessions Running migrations: Applying books.0001_initial... OK
Admin
admin.py
にadmin.site.register(Book)
を追記することで、管理画面からBookモデルを管理できるように変更できます。
from django.contrib import admin from.models import Book # Register your models here. admin.site.register(Book)
管理画面では、モデルの編集を行うことができます。
Views
Djangoが提供している汎用Viewを継承するのが楽です。
汎用Viewを使用する場合、以下のようにそのViewを適用するtemplate及びmodelを適切な変数名で定義します。
class BookListView(ListView): model = Book template_name = 'book_list.html'
URLs
ルートからbooks.urls
にルーティングされるように設定します。
urlpatterns = [ path('admin/', admin.site.urls), path('', include('books.urls')), ]
include
関数を使用し、アプリケーションごとの urls.py
を読み込むように設定しています。
books.urls
では、ルートからBookListView
にルーティングされるように設定します。
urlpatterns = [ path('', BookListView.as_view(), name='home'), ]
こうすることで、ルートにアクセスしたリクエストとBookListView
が対応することになります。
template
Djangoのtemplateでは、jinja2テンプレートエンジンを使用することができます。
<h1>All books</h1> {% for book in object_list %} <ul> <li>Title: {{ book.title }}</li> <li>Subtitle: {{ book.subtitle }}</li> <li>Author: {{ book.author }}</li> <li>ISBN: {{ book.isbn }}</li> </ul> {% endfor %}
Django REST frameworkの導入
Django REST frameworkを導入するには、以下の手順を踏む必要があります。
REST framework用に作成するアプリケーションは通常のアプリケーションと異なりデータベースを持たないため、
モデルを定義する必要も、migrationファイル(SQL文が書かれたファイル)を作成する必要も、データベースを更新する必要もありません。
djangorestframework
のインストールINSTALLED_APPS
にrest_framework
を追記- Serializer、View、ルーティングの定義
1. djangorestframework のインストール
pipで入れることができます。
$ pipenv install djangorestframework==3.8.1
2. INSTALLED_APPS
にrest_framework
を追記
INSTALLED_APPS
に'rest_framework'
を追加します。
INSTALLED_APPS = [ 'rest_framework', ]
3. ルーティング、Serializer、View、の定義
APIの場合もstartapp
コマンドを使うと便利です。
$ python manage.py startapp api
URLs
api/
というルーティングを新たに追加します。
urlpatterns = [ path('admin/', admin.site.urls), path('', include('books.urls')), path('api/', include('api.urls')), # new ]
Serializer
APIエンドポイントで扱いやすいJSONやXMLに変換するためのモノです。
最低限のSerializerであれば、以下のように作成することができます。
class BookSerializer(serializer.ModelSerializer): class Meta: model = Book fields = ('title', 'subtitle', 'author', 'isbn')
Views
ViewはAPI用の汎用Viewが提供されているため、それを使うと楽です。
class BookAPIView(generics.ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer
APIを提供しているURL(今回だとhttp://localhost:8000/api/)にアクセスすると、以下のようなそれっぽいAPIコンソールが出現します。
CORS (Cross-Origin Resource Sharing) について
異なるドメイン名(例:mysite.com vs yoursite.com)や
異なるポート(例:localhost:3000 vs localhost:8000)とクライアントがやり取りを行う際、セキュリティー的に問題が生じます。
詳細は以下のサイトがわかりやすかったです。
django-cors-headersを使えば、CORS(Cross-Origin Resource Sharing)ヘッダーをレスポンスに追加することで、クロスサイトHTTPリクエストを行うことが許可されているオリジンホスト名を制限することができます。
django-cors-headersを使用するには、以下の手順を踏む必要があります。
django-cors-headers
のインストールINSTALLED_APPS
及びMIDDLEWARE
に追記CORS_ORIGIN_WHITELIST
にリクエストを制限するホスト名を記述
1. django-cors-headers
のインストール
pipで入れることができます。
$ pip install django-cors-headers
2. INSTALLED_APPS
及びMIDDLEWARE
に追記
以下の設定をsettings.py
に追記します。
INSTALLED_APPS = [ 'corsheaders' ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ]
3. CORS_ORIGIN_WHITELIST
にリクエストを制限するホスト名を記述
例えばクロスサイトHTTPリクエストをlocalhost:3000のみに制限する場合は、以下のようにCORS_ORIGIN_WHITELIST
環境変数に定義します。
# new CORS_ORIGIN_WHITELIST = ( 'localhost:3000' )