1. 首页轮播图
首先用 xadmin
设置首页轮播图,并设置播放顺序。
1、goods/serializers.py
:
class BannerSerializer(serializers.ModelSerializer):
"""首页轮播图"""
class Meta:
model = Banner
fields = '__all__'
2、goods/views.py
:
class BannerViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""首页轮播图"""
serializer_class = BannerSerializer
queryset = Banner.objects.all().order_by('index')
3、MxShop/urls.py
:
router.register(r'banners', BannerViewSet, basename='banner') # 首页轮播图
2. 首页新品
在 goods/filters.py
中新增 is_new
字段,因为商品模型中 is_new
字段区分商品是否为新品,只需进行过滤即可:
class GoodsFilter(django_filters.rest_framework.FilterSet):
"""
商品过滤
"""
# name 为要过滤的字段,lte 为执行的行为,这里为小于等于本店价格
....
class Meta:
model = Goods
fields = ['pricemin', 'pricemax', 'top_category', 'is_hot', 'is_new']
另外需要在后台中设置新品商品。
3. 首页热搜词
1、goods/serializers.py
:
class HotSearchWordsSerializer(serializers.ModelSerializer):
"""搜索栏下方热搜关键词"""
class Meta:
model = HotSearchWords
fields = '__all__'
2、goods/views.py
:
class HotSearchWordsViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""搜索栏下方热搜关键词"""
serializer_class = HotSearchWordsSerializer
queryset = HotSearchWords.objects.all().order_by('-index')
3、MxShop/urls.py
:
router.register(r'hotsearchs', HotSearchWordsViewSet, basename='hotsearchs') # 搜索栏下方热搜关键词
4. 首页分类显示
1、goods/serializers.py
:
class BrandSerializer(serializers.ModelSerializer):
"""大类下面的宣传商标"""
class Meta:
model = GoodsCategoryBrand
fields = '__all__'
class IndexCategorySerializer(serializers.ModelSerializer):
brands = BrandSerializer(many=True) # 某个大类的商标,可以有多个商标,一对多
# good有一个外键category,但这个外键指向的是三级类,直接反向通过外键category(三级类),取某个大类下面的商品是取不出来的
goods = serializers.SerializerMethodField()
sub_cat = CategorySerializer2(many=True) # 取二级商品分类
ad_goods = serializers.SerializerMethodField() # 广告商品
def get_ad_goods(self, obj):
good_json = {}
ad_goods = IndexAd.objects.filter(category_id=obj.id, )
if ad_goods:
good_ins = ad_goods[0].goods
# 在 serializer 调用 serializer,需要添加上下文 context,嵌套 serializer 也必须加
# serializer 返回的时候一定要加 .data 才是 json 数据
good_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
return good_json
def get_goods(self, obj):
"""将这个商品相关父类子类等都可以进行匹配"""
all_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
))
goods_serializer = GoodsSerializer(all_goods, many=True, context={'request': self.context['request']})
return goods_serializer.data
class Meta:
model = GoodsCategory
fields = '__all__'
2、goods/views.py
:
class IndexCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""首页商品分类数据"""
# 获取 is_tab=True(导航栏)里面的分类下的商品数据
queryset = GoodsCategory.objects.filter(is_tab=True, name__in=['生鲜食品', '酒水饮料'])
serializer_class = IndexCategorySerializer
3、MxShop/urls.py
:
router.register(r'indexgoods', IndexCategoryViewSet, basename='indexgoods') # 首页系列商品展示
广告位商品需要后台设置。
5. 商品点击数和收藏数
两者可以查看 goods
数据表中 click_num、fav_num
数目变化。
5.1 商品点击数
商品点击数与获取商品详情为同一接口,只需重写 retrieve()
即可实现 goods/views.py
:
class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""
....
def retrieve(self, request, *args, **kwargs):
"""商品点击数 +1"""
instance = self.get_object()
instance.click_num += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
5.2 收藏数
5.2.1 重写 perform_create 和 perform_destory
包括收藏和取消收藏,分别对应操作数据库加一、减一操作 user_operation/views.py
:
class UserFavViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin,
mixins.DestroyModelMixin, mixins.RetrieveModelMixin):
"""
用户商品收藏
ListModelMixin:收藏列表
CreateModelMixin:收藏
DestroyModelMixin:取消(删除)收藏,相应地要删除数据库中数据
"""
# serializer_class = UserFavSerializer
queryset = UserFav.objects.all()
# IsAuthenticated:必须登录用户;IsOwnerOrReadOnly:必须是当前登录的用户
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
# 用户认证
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
# 搜索的字段
lookup_field = 'goods_id'
def get_serializer_class(self):
"""
动态设置 serializer,get 时获取用户收藏详情
:return:
"""
if self.action == 'list':
# 收藏列表
return UserFavDetailSerializer
elif self.action == 'create':
return UserFavSerializer
return UserFavSerializer
def get_queryset(self):
# 只能查看当前登录用户的收藏,禁止获取其他用户的收藏
return UserFav.objects.filter(user=self.request.user)
# def perform_create(self, serializer):
# """用户收藏的商品数量 +1"""
# instance = serializer.save() # instance相当于UserFav
# goods = instance.goods
# goods.fav_num += 1
# goods.save()
# def perform_destory(self, instance):
# """用户收藏的商品数量 -1"""
# goods = instance.goods
# goods.fav_num -= 1
# if goods.fav_num < 0:
# goods.fav_num = 0
# goods.save()
注意:注意不能为负数!
5.2.2 信号
使用信号,代码分离性更好,这里只需接收 model
模型 create、delete
发出来的信号即可:
1、新建 user_operation/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 create_UserFav(sender, instance=None, created=False, **kwargs):
"""
用户收藏 +1
post_save:接收信号的方式、sender:接收信号的 model
:param sender:
:param instance:
:param created:
:return:
"""
# 是否新建,因为 update 时也会进行 post_save
if created:
goods = instance.goods
goods.fav_num += 1
goods.save()
@receiver(post_delete, sender=UserFav)
def delete_UserFav(sender, instance=None, created=False, **kwargs):
"""用户收藏 -1"""
goods = instance.goods
goods.fav_num -= 1
if goods.fav_num < 0:
goods.fav_num = 0
goods.save()
2、user_operation/apps.py
:
from django.apps import AppConfig
class UserOperationConfig(AppConfig):
name = 'user_operation'
verbose_name = '操作管理'
def ready(self):
import user_operation.signals
6. 商品库存和销量
6.1 库存数量
商品库存修改会导致的情况:
- 新增商品到购物车
- 修改购物车商品数量
- 删除购物车记录
- 订单取消,库存增加
trade/views.py
:
class ShoppingCartViewSet(viewsets.ModelViewSet):
"""
购物车功能:
list:获取购物车详情
create:加入购物车
delete:删除购物车记录
"""
....
def perform_create(self, serializer):
"""添加到购物车,库存数 -1"""
shop_cart = serializer.save()
goods = shop_cart.goods
goods.goods_num -= shop_cart.nums
goods.save()
def perform_destroy(self, instance):
"""从购物车中删除,库存数 +1"""
goods = instance.goods
goods.goods_num += instance.nums
goods.save()
instance.delete()
def perform_update(self, serializer):
"""更新库存,修改可能是增加也可能是减少"""
# 获取修改之前的库存数量
existed_record = ShoppingCart.objects.get(id=serializer.instance.id)
existed_nums = existed_record.nums
# 保存之前的数据
save_record = serializer.save()
# 变化的数量
nums = save_record.nums - existed_nums
goods = save_record.goods
goods.goods_num -= nums
goods.save()
6.2 商品销量
只有当支付成功后,销量才增加 trade/views.py
:
class AlipayView(APIView):
"""支付相关"""
def post(self, request):
"""
处理支付宝的 notify_url
支付宝服务器主动通知商户服务器里指定的页面http/https路径
:param request:
:return:
"""
....
# 验证成功
if verify_re is True:
....
for existed_order in existed_orders:
order_goods = existed_order.goods.all() # 订单商品项
# 商品销量增加订单中数值(支付成功后,销量才增加)
for order_good in order_goods:
goods = order_good.goods
goods.sold_num += order_good.goods_num
goods.save()
# 更新订单状态
existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.save()
# 返回一个 success 给支付宝,若不返回支付宝会一直发送订单支付成功的消息
return Response("success")
7. 缓存和接口限速
7.1 内存缓存
http://chibisov.github.io/drf-extensions/docs/#caching
1、安装拓展:pip install drf-extensions
2、设置缓存时间 settings.py
:
# drf-extensions配置
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 10 # 缓存全局过期时间(60 * 10 表示10分钟)
}
3、给商品添加缓存 goods/views.py
:
class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""
pass
在 GoodsListViewSet
视图中添加 CacheResponseMixin
,必须放在第一位。
注意:内存缓存重启浏览器失效,因此可以采用 redis 缓存
7.2 配置 redis 缓存后端
1、安装 django-redis
:
pip install django-redis
# hiredis 的作用是提升 redis 解析性能
pip install hiredis
2、配置 settings
:
# redis 缓存相关
# 使用 redis 数据库 1 存储缓存结果
REDIS_URL = 'redis://192.168.131.131:6379/1'
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": REDIS_URL,
'TIMEOUT': 300, # 超时时间
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
'PASSWORD_CLASS': 'redis.connection.HiredisParser',
},
'CONNECTION_POOL_CLASS': 'redis.connection.BlockingConnectionPool',
}
}
3、访问 http://192.168.131.131:8000/goods/
,查看前后两次加载的速度:
服务器上查看 redis
有没有缓存结果:
[root@localhost hj]# redis-cli
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) ":1:1e399649bf49833ffcb5facd7bd7c7d7"
2) ":1:1b4b0a4b5386c3b1e949f67333311750"
3) ":1:7e04a81fa423f272e0115f47a426a125"
4) ":1:47956e9d5293aecd0bd9b696fea21dc7"
也可以使用 redis
客户端工具:https://github.com/qishibo/AnotherRedisDesktopManager/releases
注意:其他需要添加缓存的请自行添加,以上只以缓存商品为例
7.3 接口频率限制
接口访问速度过快,会对服务器造成很大压力,可以对接口访问频率进行限制,分为全局限制所有接口和单独对某个接口进行限制。
全局限制
配置 settings
:
REST_FRAMEWORK = {
# throttle 全局限制接口访问频率
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle', # 未登录用户,限制其 IP
'rest_framework.throttling.UserRateThrottle', # 已登录用户,根据其 token 判断
],
'DEFAULT_THROTTLE_RATES': {
'anon': '3/minute', # 匿名用户,每分钟 3 次
'user': '5/minute', # 登录用户,每分钟 5次
},
}
单个 API 接口限速
1、settings
:
REST_FRAMEWORK = {
# throttle 全局限制接口访问频率
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.ScopedRateThrottle', # 限制用户对于每个视图的访问频次,使用ip或user id。
],
'DEFAULT_THROTTLE_RATES': {
'goods_list': '600/minute'
}
2、在视图函数中添加限制:goods/views.py
:
class GoodsListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""
....
throttle_scope = 'goods_list'