こんにちは。KOUKIです。
前回は、UserAPIを作成しました。
本日は、ブログのTag APIを作成します。
<目次>
記事まとめ
TagAPIアプリの作成
以下のコマンドで、blogアプリを作成してください。
1 |
make app app=blog |
blogアプリの作成に成功したら、settings.pyに登録しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
INSTALLED_APPS = [ 'account', # new 'blog', 'rest_framework', 'rest_framework.authtoken', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
続いて、テストコード作成用のフォルダとファイルを作成します。
1 2 3 4 5 |
rm app/blog/tests.py mkdir app/blog/tests touch app/blog/tests/__init__.py touch app/blog/tests/test_models.py touch app/blog/tests/test_tags_api.py |
Tagモデルの作成とテスト
先ほど追加したblogアプリにTagモデルを追加します。
まずは、テストコードからですね。
app/blog/tests/test_models.pyに以下のテストコードを追加してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# app/blog/tests/test_models.py from django.test import TestCase from django.contrib.auth import get_user_model from blog.models import Tag def sample_user(email='test@selfnote.work', password='SELF NOTE'): return get_user_model().objects.create_user(email, password) class ModelTests(TestCase): def test_tag_str(self): """Check if the tag string representation""" tag = Tag.objects.create( user=sample_user(), name='Vegan' ) self.assertEqual(str(tag), tag.name) |
上記のテストコードは、Tag名の文字列チェックです。
次のコマンドでテストを実行できますが、Tag Modelがないためエラーになります。
1 2 3 4 |
make test app=blog E ImportError: cannot import name 'Tag' from 'blog.models' (/app/blog/models.py) |
Tag Modelを追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# app/blog/models.py from django.db import models from django.conf import settings class Tag(models.Model): name = models.CharField(max_length=255) user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, ) def __str__(self): return self.name |
Tag Modelを実装したらマイグレーションをします。
1 |
make migrate |
テストを実行します。
1 2 3 |
make test app=blog ====== 1 passed in 1.29s ======= |
テストがパスしましたね。
Tag Modelをadmin.pyに追加して、管理画面から見えるようにしましょう。
1 2 3 4 5 6 |
# app/blog/admin.py from django.contrib import admin from .models import Tag admin.site.register(Tag) |
Tag APIの作成とテスト
次は、Tag APIの作成です。
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 55 56 57 58 59 60 61 62 63 64 65 66 |
# app/blog/tests/test_tags_api.py from django.contrib.auth import get_user_model from django.urls import reverse from django.test import TestCase from rest_framework import status from rest_framework.test import APIClient from blog.models import Tag from blog.serializers import TagSerializer TAGS_URL = reverse('blog:tag-list') class PublicTagsApiTests(TestCase): def setUp(self): self.client = APIClient() # 1 def test_login_required(self): """Check if it is the access after login""" res = self.client.get(TAGS_URL) self.assertEqual(res.status_code, status.HTTP_401_UNAUTHORIZED) class PrivateTagsApiTests(TestCase): def setUp(self): self.user = get_user_model().objects.create_user( 'test@selfnote.work', 'SELF NOTE' ) self.client = APIClient() self.client.force_authenticate(self.user) # 2 def test_retrieve_tags(self): """Check if the retrieving tags are ok""" Tag.objects.create(user=self.user, name='Harry') Tag.objects.create(user=self.user, name='Ron') res = self.client.get(TAGS_URL) tags = Tag.objects.all().order_by('-name') serializer = TagSerializer(tags, many=True) self.assertEqual(res.status_code, status.HTTP_200_OK) self.assertEqual(res.data, serializer.data) # 1 def test_tags_limited_to_user(self): """"Check if the returned tags are for the authenticated user""" user2 = get_user_model().objects.create_user( 'other@selfnort.work', 'SELF NOTE', ) Tag.objects.create(user=user2, name='Blog') tag = Tag.objects.create(user=self.user, name='Important SEO') res = self.client.get(TAGS_URL) self.assertEqual(res.status_code, status.HTTP_200_OK) self.assertEqual(len(res.data), 1) self.assertEqual(res.data[0]['name'], tag.name) |
このテストは、次の要件をテストします。
- #1 -> 未ログインユーザーのアクセス(401エラーが返される)
- #2 -> Tagの生成
- #3 -> Tagとユーザーの紐づけ
テストを実行します。
1 2 3 |
make test app=blog E ModuleNotFoundError: No module named 'blog.serializers' |
serializers.pyファイルを作成していないので、エラーになりました。
このファイルを作りましょう。
1 |
touch app/blog/serializers.py |
中身を実装します。
1 2 3 4 5 6 7 8 9 10 |
from rest_framework import serializers from blog.models import Tag class TagSerializer(serializers.ModelSerializer): class Meta: model = Tag fields = ('id', 'name') read_only_fields = ('id',) |
テストを実行しましょう。
1 2 3 |
make test app=blog E django.urls.exceptions.NoReverseMatch: 'blog' is not a registered namespace |
「TAGS_URL = reverse(‘blog:tag-list’)」でエラーがでましたね。
blogアプリ配下にurls.pyを作成して、パスを指定しましょう。
1 |
touch app/blog/urls.py |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from django.urls import path, include from rest_framework.routers import DefaultRouter from blog import views router = DefaultRouter() router.register('tags', views.TagViewSet) app_name = 'blog' urlpatterns = [ path('', include(router.urls)), ] |
プロジェクト(app)レベルのurls.pyにもこのurls.pyへのパスを記述します。
1 2 3 4 5 6 7 8 9 |
from django.contrib import admin from django.urls import include, path urlpatterns = [ path('admin/', admin.site.urls), path('api/account/', include('account.urls')),] # new path('api/blog/', include('blog.urls')), ] |
テストを実行しましょう。
1 2 |
make test app=blog E AttributeError: module 'blog.views' has no attribute 'TagViewSet' |
blogアプリのviews.pyにTagViewSet関数を追加しましょう.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# app/blog/views.py from rest_framework import viewsets, mixins from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated from blog.models import Tag from .serializers import TagSerializer class TagViewSet(viewsets.GenericViewSet, mixins.ListModelMixin): authentication_classes = (TokenAuthentication,) permission_classes = (IsAuthenticated,) queryset = Tag.objects.all() serializer_class = TagSerializer def get_queryset(self): return self.queryset.filter(user=self.request.user).order_by('-name') |
テストを実行します。
1 2 3 |
make test app=blog ===== 4 passed in 1.71s ====== |
Create Tag APIの作成とテスト
Tagを作成するAPIを作成します。
まずは、テストコードからです。
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 |
# app/blog/tests/test_tags_api.py from django.contrib.auth import get_user_model from django.urls import reverse from django.test import TestCase from rest_framework import status from rest_framework.test import APIClient from blog.models import Tag from blog.serializers import TagSerializer TAGS_URL = reverse('blog:tag-list') class PublicTagsApiTests(TestCase): ... class PrivateTagsApiTests(TestCase): ... def test_create_tag_successful(self): """Check if creating a new tag is ok""" payload = {'name': 'Test tag'} self.client.post(TAGS_URL, payload) exists = Tag.objects.filter( user=self.user, name=payload['name'] ).exists() self.assertTrue(exists) def test_create_tag_invalid(self): """Check if creating a new tag with invalid payload is ng""" payload = {'name': ''} res = self.client.post(TAGS_URL, payload) self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) |
一つ目のテストは、Tagの作成チェック、もう一つは無効なパラメータ(nameが空)を渡した場合のチェックですね。
テストを実行します。
1 2 3 4 5 6 7 8 9 |
make test app=blog __ PrivateTagsApiTests.test_create_tag_invalid __ > self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) E AssertionError: 405 != 400 __ PrivateTagsApiTests.test_create_tag_successful __ > self.assertTrue(exists) E AssertionError: False is not true |
では、views.pyのTagViewSet関数を修正しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# app/blog/views.py from rest_framework import viewsets, mixins from rest_framework.authentication import TokenAuthentication from rest_framework.permissions import IsAuthenticated from blog.models import Tag from .serializers import TagSerializer class TagViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin): authentication_classes = (TokenAuthentication,) permission_classes = (IsAuthenticated,) queryset = Tag.objects.all() serializer_class = TagSerializer def get_queryset(self): return self.queryset.filter(user=self.request.user).order_by('-name') def perform_create(self, serializer): serializer.save(user=self.request.user) |
テストを実行します。
1 2 3 |
make test app=blog === 6 passed in 1.96s === |
テストがパスしましたね。長くなってきたので、今日はここまでにしておきましょう。
次回
次回は、ブログにコメントをつけるためのComment APIを作成します。
関連記事
基礎編はこちらです。
Djangoおすすめ書籍
Djangoを学ぶなら以下の書籍がオススメです。
緑 -> 赤 -> 紫の順でやればOKです。読みやすい英語で書かれているので、英語力もついでに上がるかもしれません^^
コメントを残す
コメントを投稿するにはログインしてください。