LoginSignup
24
20

More than 3 years have passed since last update.

SEIYU風のECサイトを作りましょう(3)Django REST frameworkで爆速APIを作りましょう

Posted at

前回までの記事

1.Django REST framework + Vue.js「SEIYU風のECサイトを作りましょう」全6回(予定)
2.SEIYU風のECサイトを作りましょう(1)要求分析とプロジェクト初期化
3.SEIYU風のECサイトを作りましょう(2)Xadminを使って管理画面を一新します

前書き

logo.png
今回の記事はDjango REST frameworkを使用して、APIを作っていきます。
テスト用のデータが必要なため、商品(goods)カテゴリ(GoodsCategory)のデータを予め用意します。
管理画面から手入力でもできますが、本記事のリポジトリにあるdb_toolsフォルダをコピーして使用することをオススメします。
本記事リポジトリ

使い方

qiita-Django-supermarket/api/db_tools/配下にある、以下の二つのファイルを実行して下さい。
そうすればテストデータがDBに追加されます。
import_category_data.py
import_goods_data.py

商品(goods)API

まずはSerializersを作ります。
Djangoのformsとして考えていいです。

cd apps/goods
vim serializer.py
apps/goods/serializer.py
from rest_framework import serializers
from .models import Goods


class GoodsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Goods
        fields = "__all__"

次はgoodsviews.pyを編集します。

apps/goods/views.py
from rest_framework import mixins
from rest_framework import viewsets

from .models import Goods
from .serializer import GoodsSerializer


class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
     queryset = Goods.objects.all()
     serializer_class = GoodsSerializer

最後はurls.pyに以下を追加します。

api/urls.py
...
from django.urls import path, re_path, include
from rest_framework.routers import DefaultRouter
from goods.views import GoodsListViewSet
...
router = DefaultRouter()
router.register(r'goods', GoodsListViewSet)
...

urlpatterns = [
    ...
    re_path('^', include(router.urls)),
    ...
]

以上の三点を追加すれば、商品リストのAPIは出来上がりです。
以下コマンドを実行し、サーバーを再起動します。

python manage.py runserver

http://127.0.0.1:8000/にアクセスし、正しくデータが取得できたことを確認します。
キャプチャ.PNG
キャプチャ.PNG

データあるだけではAPIとして不親切なので、オプションをつけていきます。

ページング

Django REST frameworkにページングを追加する方法は以下の二つがあります。

  • setting.pyにグローバル配置する
  • views.pyにローカルなページングを追加する

今回はローカルに追加する方法を使用します。
goodsのviews.pyに以下を追加します。

apps/goods/views.py

...
from rest_framework.pagination import PageNumberPagination
...
class GoodsSetPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100
...
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
     ....
     pagination_class = GoodsSetPagination

以上の内容追加後、再度http://127.0.0.1:8000/goods/にアクセスします。
ページングが確認できたらオーケーです。
キャプチャ.PNG

公式ドキュメント-ページング

フィルタ

商品に関するフィルタ、よくあるのは以下の3つです。

  • 値段順に並び替え
  • 値段区間内検索
  • キーワード検索

それら全部フロントでやれますが、勉強のため、バックエンドで実装します。
先ずはsettings.pyに以下を追加します。

api/settings.py
INSTALLED_APPS = [
    ...
    'django_filters'
]

次はviews.pyに以下を追加します。

apps/goods/views.py
...
from django_filters.rest_framework import DjangoFilterBackend
...
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
     ...
     filter_backends = (DjangoFilterBackend,)
     filterset_fields = ('name', 'shop_price')
     ... 

以上の二点完成後、http://127.0.0.1:8000/goods/にアクセスします。
フィルタという項目が追加されたことが確認できます。
キャプチャ.PNG
商品名を入力すれば、フィルタが機能してることが確認できます
キャプチャ.PNG
キャプチャ.PNG
今のフィルタは商品名が完全一致でなければ表示されません。
親切とは言えませんので、もう少し工夫して部分一致を可能にします。

goodsの配下にfilters.pyファイルを作ります。

apps/goods/filters.py
import django_filters
from .models import Goods


class GoodsFilter(django_filters.rest_framework.FilterSet):
    """
    goodsのfilter
    """
    price_min = django_filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
    price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte')

    class Meta:
        model = Goods
        fields = ['price_min', 'price_max']

views.pyに追加とコメントアウトを行います。

apps/goods/views.py
...
from rest_framework import filters
from .filters import GoodsFilter
...
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
     ...
     # filterset_fields = ('name', 'shop_price')
     filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
     filterset_class = GoodsFilter
     search_fields = ['name', 'goods_brief']
     ordering_fields = ['shop_price']

http://127.0.0.1:8000/goods/にアクセスします。

キャプチャ.PNG

  • 値段順に並び替え
  • 値段区間内検索
  • キーワード検索

以上3つの商品に関するフィルタが完成しました。

外部キーの処理

Goodsモデルにはcategoryという外部キーが存在します。
今のAPIでは数字のIDとして表示してますが、参照先の詳細を表示できるようにしましょう。
キャプチャ.PNG

goodsのserializer.pyを修正します。

apps/goods/serializer.py
...
from .models import GoodsCategory
...
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = "__all__"
...
class GoodsSerializer(serializers.ModelSerializer):
    category = CategorySerializer()
    ...

修正後、カテゴリの詳細も表示できるようになりました。

キャプチャ.PNG

カテゴリ(category )API

カテゴリの内容は商品ほど多くはないですが、自身を外部キーとして扱うカテゴリの階級が存在します。
それを実装しましょう。

apps/goods/serializer.py
class CategorySerializer3(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = "__all__"


class CategorySerializer2(serializers.ModelSerializer):
    sub_cat = CategorySerializer3(many=True)

    class Meta:
        model = GoodsCategory
        fields = "__all__"

class CategorySerializer(serializers.ModelSerializer):
    sub_cat = CategorySerializer2(many=True)
    ...

次はviews.pyに以下を追加します。

apps/goods/views.py
...
from .models import GoodsCategory
from .serializer import CategorySerializer
...
class CategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    queryset = GoodsCategory.objects.filter(category_type=1)
    serializer_class = CategorySerializer

urls.pyに以下を追加します。

api/urls.py
...
from goods.views import CategoryViewSet
...
router.register(r'categorys', CategoryViewSet, base_name="categorys")
...

以上の修正できたら、http://127.0.0.1:8000/にアクセスして確認します。
階層分けされたデータが確認できるはずです。
キャプチャ.PNG

キャプチャ.PNG

以上でカテゴリAPIが完成です。

APIドキュメント

もしバックエンドとフロントエンドを二人で担当する場合。
フロントエンドエンジニアが作業をしやすくするために、
バックエンドエンジニアはAPIのドキュメントを作る必要があります。
Django REST frameworkにはドキュメント自動生成する機能は備えてあります。
それを機能を有効化しましょう。

setting.pyに以下を追加します。

api/settings.py

...
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'
}
...

ドキュメント用のルートを追加します。
urls.pyに以下を追加します。

api/urls.py

...
from rest_framework.documentation import include_docs_urls
...

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title="shop")),
]

http://127.0.0.1:8000/docs/にアクセスし、ドキュメントの画面を確認します。
キャプチャ.PNG
以下のように空白のカラムがある場合には、ヘルプテキストを追加する必要があります。
キャプチャ.PNG
空白のカラムにhelp_textを追加してください。

apps/goods/filters.py

price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte', help_text="最大額")

キャプチャ.PNG

以上、APIの制作は一旦完了です。
新規登録、ログインなどのAPIはVue.jsプロジェクトの制作と共に作っていきます。
最後まで読んで頂きありがとうございます。

次回予告

Vue.jsプロジェクトの初期化

24
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
20