Django REST Framework と Vue を利用したオンラインショッピングモールのホーム画面および商品数量処理、キャッシュとレート制限の実装

スライダーコンポーネントのAPI実装

最初に、PyCharmの開発環境をローカルに設定し、Vueの設定もローカルホストに変更します。

  • goods/serializer.py
class BannerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Banner
        fields = '__all__'
  • goods/views.py
class BannerViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    queryset = Banner.objects.all().order_by('index')
    serializer_class = BannerSerializer
  • urls.py
router.register(r'banners', BannerViewSet, basename='banners')

管理画面からスライダーバナー画像を追加します。

新商品フィルタリング機能の実装

商品モデルにはis_newというフィールドがあります。

is_new = models.BooleanField('新商品かどうか', default=False)

この機能を実装するには、GoodsFilterクラスにこのフィールドを追加するだけです。

class Meta:
    model = Goods
    fields = ['pricemin', 'pricemax', 'is_hot', 'is_new']

管理画面でいくつかの商品に対してis_newをTrueに設定します。

ホーム画面のカテゴリ表示機能

トップカテゴリには以下のような情報を含めます:

  • ブランドロゴ(複数)
  • サブカテゴリ
  • 広告商品
  • 全商品一覧
  • goods/serializers.py
class BrandSerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategoryBrand
        fields = '__all__'

class IndexCategorySerializer(serializers.ModelSerializer):
    brands = BrandSerializer(many=True)
    goods = serializers.SerializerMethodField()
    sub_cat = CategorySerializer2(many=True)
    ad_goods = serializers.SerializerMethodField()

    def get_ad_goods(self, obj):
        try:
            ad_good = IndexAd.objects.get(category_id=obj.id).goods
            return GoodsSerializer(ad_good, context={'request': self.context['request']}).data
        except IndexAd.DoesNotExist:
            return {}

    def get_goods(self, obj):
        goods = Goods.objects.filter(
            Q(category_id=obj.id) |
            Q(category__parent_category_id=obj.id) |
            Q(category__parent_category__parent_category_id=obj.id)
        )
        return GoodsSerializer(goods, many=True, context={'request': self.context['request']}).data

    class Meta:
        model = GoodsCategory
        fields = '__all__'
  • goods/views.py
class IndexCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    queryset = GoodsCategory.objects.filter(is_tab=True, name__in=['食品', '飲料'])
    serializer_class = IndexCategorySerializer
  • urls.py
router.register(r'indexgoods', IndexCategoryViewSet, basename='indexgoods')

商品のクリック数とお気に入り数の管理

  • クリック数の更新
class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    queryset = Goods.objects.all().order_by('id')
    serializer_class = GoodsSerializer

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.click_num += 1
        instance.save()
        return Response(self.get_serializer(instance).data)
  • お気に入り数の更新(シグナルによる実装)
# signals.py
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from user_operation.models import UserFav

@receiver(post_save, sender=UserFav)
def update_fav_count_on_save(sender, instance, created, **kwargs):
    if created:
        instance.goods.fav_num += 1
        instance.goods.save()

@receiver(post_delete, sender=UserFav)
def update_fav_count_on_delete(sender, instance, **kwargs):
    instance.goods.fav_num -= 1
    instance.goods.save()
# apps.py
class UserOperationConfig(AppConfig):
    name = 'user_operation'

    def ready(self):
        import user_operation.signals

在庫と販売数の更新処理

  • 在庫数の更新
class ShoppingCartViewSet(viewsets.ModelViewSet):
    def perform_create(self, serializer):
        cart = serializer.save()
        cart.goods.goods_num -= cart.nums
        cart.goods.save()

    def perform_destroy(self, instance):
        goods = instance.goods
        goods.goods_num += instance.nums
        goods.save()
        instance.delete()

    def perform_update(self, serializer):
        before = ShoppingCart.objects.get(id=serializer.instance.id)
        goods = before.goods
        delta = serializer.validated_data['nums'] - before.nums
        goods.goods_num -= delta
        goods.save()
        serializer.save()
  • 販売数の更新(支払い完了時)
for order_good in order_goods:
    goods = order_good.goods
    goods.sold_num += order_good.goods_num
    goods.save()

DRFでのキャッシュ設定

キャッシュを導入することで、データ取得の高速化を実現します。

  • インストール
pip install drf-extensions
  • goods/views.py
from rest_framework_extensions.cache.mixins import CacheResponseMixin

class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    ...
  • settings.py
REST_FRAMEWORK_EXTENSIONS = {
    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 5
}

Redisを用いたキャッシュ構成

  • インストール
pip install django-redis
  • settings.py
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

APIアクセスレート制限

  • settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/minute',
        'user': '5/minute'
    }
}
  • goods/views.py
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle

class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    throttle_classes = (UserRateThrottle, AnonRateThrottle)

タグ: Django rest-framework drf-extensions redis throttling

5月16日 07:19 投稿