【DRF】venv仮想環境を用いて、DRFシステムで書籍管理システムを作成する(2)Djangoアプリケーションの作成
DRFを利用して書籍管理システムを作成するの2回目です。前回はDjangoが稼働するところまでをまとめましたが、今回はアプリケーションを作成するところまでを追っていきたいと思います。
試行錯誤しながらなので、なかなか進まないのが悩みどころ(苦笑)
3.「アプリケーション」の作成
プロジェクト以下に、アプリケーションを作成します。
今回は書籍管理APIを構築するので、名前は book
とします。
アプリケーションを作成するコマンドは、 django/apps ディレクトリ内で行います。
1 2 3 4 5 |
(venv) ~$ cd django/apps (venv) ~$ mkfir book # アプリ作成 (venv) ~$ python3 manage.py startapp book apps/book |
出来上がったファイルは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
(venv) ~/source/drf3/django/apps$ cd book (venv) ~/source/drf3/django/apps/book$ ls -al total 32 drwxr-xr-x 3 t******** t******** 4096 Sep 5 23:20 . drwxr-xr-x 4 t******** t******** 4096 Sep 5 23:20 .. -rw-r--r-- 1 t******** t******** 0 Sep 5 23:20 __init__.py -rw-r--r-- 1 t******** t******** 63 Sep 5 23:20 admin.py -rw-r--r-- 1 t******** t******** 140 Sep 5 23:20 apps.py drwxr-xr-x 2 t******** t******** 4096 Sep 5 23:20 migrations -rw-r--r-- 1 t******** t******** 57 Sep 5 23:20 models.py -rw-r--r-- 1 t******** t******** 60 Sep 5 23:20 tests.py -rw-r--r-- 1 t******** t******** 63 Sep 5 23:20 views.py |
3-1.管理ユーザー作成
アプリの管理ユーザを作成します。
コマンドの実行は manage.py のある django ディレクトリで行います。
1 |
(venv) ~$ python3 manage.py createsuperuser |
ユーザー名、メールアドレス、パスワードを入力します。
パスワードは最低8文字が必要です。
また入力時にパスワードは表示されない(見えない)ので、間違えずに入力して下さい。コピペ推奨。
3-2.アプリケーションを使えるように設定
アプリケーションを使えるようにするには、プロジェクト設定に「アプリケーション」を追加する必要があります。
API を構築するにはDjango REST Framework
を使用するので、同時に追加します。
job_board/settings.py ファイルにある INSTALLED_APPS に追加します。
apps/settings.py
1 2 3 4 5 6 7 8 9 10 |
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', # (3-2)追加 'apps.book', # (3-2)追加 ] |
3-3.Pagination追加
ページネーションを追加するために下記の設定をsettings.py
に追加します。
以下の設定で4データごとにページ送りするようになります。
apps/settings.py
1 2 3 4 |
REST_FRAMEWORK = { 'PAGE_SIZE': 4, 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination' } |
3-4.apps.pyファイルの確認
apps/book/apps.py
の name
フィールドが正しく設定されていることを確認します。
次のように name
を apps.book
に変更します。
apps/book/apps.py
1 2 3 4 5 |
from django.apps import AppConfig class BookConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'apps.book' # (3-3)パスに合わせて修正 |
4.モデルの作成
4-1.モデルファイルの作成
アプリ以下のディレクトリにモデルファイルを作成します。(jobs/models.py)
モデルファイルは、テーブル設計を行うファイルで、カラム名やカラムの形式などを書き込んでいきます。
モデルの書き方については、下記記事を参考にしてください。
apps/book/models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=255) address = models.CharField(max_length=255, blank=True, null=True) website = models.URLField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name class Genre(models.Model): name = models.CharField(max_length=100) description = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=255) birthdate = models.DateField(blank=True, null=True) country = models.CharField(max_length=100, blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name class Book(models.Model): title = models.CharField(max_length=255) publisher = models.ForeignKey(Publisher, on_delete=models.SET_NULL, null=True) genre = models.ForeignKey(Genre, on_delete=models.SET_NULL, null=True) authors = models.ManyToManyField(Author, through='BookAuthor') publication_year = models.PositiveIntegerField(blank=True, null=True) isbn = models.CharField(max_length=13, unique=True, blank=True, null=True) type = models.CharField(max_length=20, choices=[(1, '文庫'), (2, '漫画')], blank=True, null=True) price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.title class BookAuthor(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) author = models.ForeignKey(Author, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"{self.author.name} - {self.book.title}" |
あまり深く考えずにテーブルを作成してしまい、少し後悔していますが、あとで修正して再マイグレーションすればいいやと思い、そのまま進めることとします。
とはいいながら、一応、著者は複数登録できるように、中間テーブルを使用する形にするなどしている設定です。後々細かい技を駆使する予定。
4-2.データベース構築
モデルファイルが作成できたらデータベースの構築が必要になりますので、下記コマンド(makemigrations)を実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
(venv) ~$ python3 manage.py makemigrations # 結果 (venv) :~/source/drf3/django$ python3 manage.py makemigrations Migrations for 'book': apps/book/migrations/0001_initial.py - Create model Author - Create model Book - Create model Genre - Create model Publisher - Create model BookAuthor - Add field authors to book - Add field genre to book - Add field publisher to book |
続けて下記コマンド(migrate)を実行します。
1 2 3 4 5 6 |
(venv) ~$ python3 manage.py migrate # 結果 Operations to perform: Apply all migrations: admin, auth, book, contenttypes, sessions Running migrations: Applying book.0001_initial... OK |
マイグレーションした結果のファイルができが上がっています。
drf3/django/apps/book/migrations ディレクトリ内を確認。
1 2 3 4 5 6 7 |
(venv) $:~ /source/drf3/django/apps/book/migrations$ ls -al total 16 drwxr-xr-x 3 t******** t******** 4096 Sep 6 00:18 . drwxr-xr-x 4 t******** t******** 4096 Sep 6 00:18 .. -rw-r--r-- 1 t******** t******** 3849 Sep 6 00:18 0001_initial.py ←★ -rw-r--r-- 1 t******** t******** 0 Sep 5 23:20 __init__.py drwxr-xr-x 2 t******** t******** 4096 Sep 6 00:19 __pycache__ |
「0001_initial.py」が、マイグレーション実行時に作成されるファイルです。
テーブル構造を変更するなどしてマイグレーションを実行するたびに、連番のファイルが次々と出来上がる仕組みとなっています。
5.URL追加
5-1.プロジェクト用ルーティングを作成
ルーティングの設定を行います。
ルーティングとは、リクエストに応じて、Djangoプロジェクト内でどのような処理経路を辿らせるかを指定するものです。
簡単に言うとネットワーク上で行われる道案内です。
プロジェクト用のルーティング用ファイル(django/apps/urls.py)に、アプリケーション用のルーティングを指定します。
apps/api へのアクセスルートを設定するため、include には appsからのパスを「.」で接続して設定します。
apps/urls.py
1 2 3 4 5 6 7 |
from django.contrib import admin from django.urls import path, include # includeを追加 urlpatterns = [ path('admin/', admin.site.urls), path('book/api/', include('apps.book.api.urls')), # (5-1)この一行を追加 ] |
5-2.アプリケーション用ルーティングを作成
次に、アプリケーションのルーティングを作成します。
今回はDRFなので、APIを作成するためAPI専用のディレクトリを作成し、管理しやすくします。
book ディレクトリの下に api フォルダを作成し、その中に urls.py ファイルを作成します。
1 2 3 4 5 |
django ├── apps │ ├── book │ │ └── api ←★ディレクトリを作成する │ │ └── urls.py ←★ファイルを作成する |
book/api/urls.py
1 2 3 4 5 6 7 8 9 10 11 12 |
from django.urls import path from . import views urlpatterns = [ # 書籍 path("books/", views.BookListView.as_view(), name="book_list"), path("books/<int:pk>/", views.BookDetailView.as_view(), name="book_detail"), # 出版社 path("publishers/", views.PublisherListView.as_view(), name="publisher_list"), path("publishers/<int:pk>/", views.PublisherDetailView.as_view(), name="publisher_detail"), ] |
上記設定で、書籍と出版社へのルーティングが出来上がります。
5-3.__init__.py
ファイルの作成
apiディレクトリ以下に必要とのことで __init__.py
ファイルを作成します。
内容はなく、空ファイルで良いとのことです。
1 2 3 4 5 6 |
django ├── apps │ ├── book │ │ └── api │ │ ├── urls.py │ │ └── __init__.py ←★このファイルを作成する |
ここでは内容は空のファイルを作成するだけでOKです。
6.Serializer (シリアライザー) 追加
Serializer とは?
Serializer とは、クエリセットやモデルインスタンスのような複雑なデータを Json形式のフォーマットに変換することを役割としています。
先ほど作成した api ディレクトリ以下に serializers.py
ファイルを作成します。
serializers の ModelSerializer クラスを継承することにより、簡単に書くことができます。
1 2 3 4 5 6 7 |
django ├── apps │ ├── book │ │ └── api │ │ ├── __init__.py │ │ ├── urls.py │ │ └── serializers.py ←★このファイルを作成する |
book/api/serializers.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
from rest_framework import serializers from apps.book.models import Publisher, Genre, Author, Book, BookAuthor class PublisherSerializer(serializers.ModelSerializer): class Meta: model = Publisher fields = "__all__" class GenreSerializer(serializers.ModelSerializer): class Meta: model = Genre fields = "__all__" class AuthorSerializer(serializers.ModelSerializer): class Meta: model = Author fields = "__all__" class BookSerializer(serializers.ModelSerializer): publisher = PublisherSerializer(read_only=True) genres = GenreSerializer(many=True, read_only=True) class Meta: model = Book fields = "__all__" class BookAuthorSerializer(serializers.ModelSerializer): book = BookSerializer(read_only=True) author = AuthorSerializer(read_only=True) class Meta: model = BookAuthor fields = "__all__" |
詳細な解説
- PublisherSerializer, GenreSerializer, AuthorSerializer:
- これらは非常にシンプルで、
ModelSerializer
を使用し、全てのフィールドをシリアライズします。 - BookSerializer:
publisher
フィールドに対してPublisherSerializer
を使用し、genres
フィールドに対してGenreSerializer
を使用しています。このようにすることで、関連するモデルのデータも含めてシリアライズできます。read_only=True
を指定することで、関連するオブジェクトが読み取り専用になります。これにより、書き込み操作時にこれらのフィールドを無視します。- BookAuthorSerializer:
book
フィールドに対してBookSerializer
を使用し、author
フィールドに対してAuthorSerializer
を使用しています。これにより、BookAuthor
モデルに関連するBook
とAuthor
のデータもシリアライズできます。
このように定義することで、各モデルとその関連データをシリアライズしやすくなります。シリアライザを適切に定義することで、API の使いやすさが向上し、フロントエンドや他のサービスと円滑にデータをやり取りできるようになります。
7.Permissions追加
Django REST Framework が提供するパーミッションシステムを使用し、API を安全にします。
主な機能
・認証されたユーザーのみに API へのアクセスを許可する
・許可されたユーザーのみに書き込み権限を与える
permissions.py ファイルを作成します。
1 2 3 4 5 6 7 8 |
django ├── apps │ ├── book │ │ └── api │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── serializers.py │ │ └── permissions.py ←★このファイルを作成する |
jobs/api/permissions.py
1 2 3 4 5 6 7 8 |
from rest_framework import permissions class IsAdminUserOrReadOnly(permissions.IsAdminUser): def has_permission(self, request, view): is_admin = super().has_permission(request, view) return request.method in permissions.SAFE_METHODS or is_admin |
8.Views追加
Viewsと聞くと、私が長年使用してきたCakePHPやZendFrameworkでおける、フロントサイドのHTMLを作成するファイルだと思えそうですが、Djangoでは、いわゆるControllerの役割を担うファイルで、がっつりバックエンドの処理を書いていくファイルとなります。
そして Django REST Framework では GenericAPIView の中に一般的に必要な開発のコードをすでに用意してくれています。
それを継承することで、簡単に開発をすることができます。
API の CRUD 操作はどの場合においても必要なため、すでに用意されています。
CRUD とは今更書かなくてもわかると思いますが、以下の機能の総称となります。
- Create(生成)
- Read(読み取り)
- Update(更新)
- Delete(削除)
8-1.views.py ファイルの作成
では views.py ファイルを作成します。
1 2 3 4 5 6 7 8 9 |
django ├── apps │ ├── book │ │ └── api │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── serializers.py │ │ ├── permissions.py │ │ └── views.py ←★このファイルを作成する |
apps/book/api/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
from rest_framework import generics from apps.book.models import Publisher, Genre, Author, Book, BookAuthor from apps.book.api.permissions import IsAdminUserOrReadOnly from apps.book.api.serializers import PublisherSerializer, GenreSerializer, AuthorSerializer, BookSerializer, BookAuthorSerializer class BookListView(generics.ListCreateAPIView): queryset = Book.objects.all().order_by("-id") serializer_class = BookSerializer permission_classes = [IsAdminUserOrReadOnly] class BookDetailView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer permission_classes = [IsAdminUserOrReadOnly] class PublisherListView(generics.ListCreateAPIView): queryset = Publisher.objects.all().order_by("-id") serializer_class = PublisherSerializer permission_classes = [IsAdminUserOrReadOnly] class PublisherDetailView(generics.RetrieveUpdateDestroyAPIView): queryset = Publisher.objects.all() serializer_class = PublisherSerializer permission_classes = [IsAdminUserOrReadOnly] |
駆け足できましたが、今回はここまで。
ひとまずこれでDjangoの設定は終了しました。
次回は API 関連に取り掛かります。