DRF实操学习——商城


分析:

  1. 商品分类表
  2. 商品表
  3. 商品图片表:一个商品有多张图片

1. 商城模型的设计

  1. 新建一个shopping的app
    在这里插入图片描述

  2. 在settings中注册该app
    在这里插入图片描述

  3. 创建模型,迁移映射

from django.db import models

from utils.modelsMixin import ModelSetMixin


# 商品分类表
class Classification(ModelSetMixin):
    name = models.CharField(max_length=40,verbose_name='分类名')
    # models.SET_NULL如果上级分类被删了,设置为空。外键关联自身
    parent = models.ForeignKey('self',on_delete=models.SET_NULL,null=True,blank=True,verbose_name='上级分类')

    class Meta:
        db_table = 'classification'
        verbose_name = '商品分类'
        verbose_name_plural = verbose_name

	def __str__(self):
        return f'%s.%s' % (self.id,self.name)

# 商品表
class Commodity(ModelSetMixin):
    STATUS_CHOICES = (
        (0,'未发布'),
        (1,'发布'),
    )
    name = models.CharField(max_length=40,verbose_name='商品名')
    caption = models.CharField(max_length=40,verbose_name='副标题')
    brand = models.CharField(max_length=40,verbose_name='品牌',null=True,blank=True)
    # 注:FloatField有计算精度的问题,如果有小数且参与计算,用DecimalField
    # 如果有小数,但是不怎么参与计算,那么可以使用FloatField
    # max_digits最大的位数 decimal_places小数为的最大位数
    price = models.DecimalField(max_digits=10,decimal_places=2,verbose_name='单价')
    stock = models.IntergerField(verbose_name='库存')
    pack = models.TextField(verbose_name='包装信息',null=True,blank=True)
    serviceafter_sale = models.TextField(verbose_name='售后服务',null=True,blank=True)
    sales = models.IntergerField(verbose_name='销量',default=0)
    comments = models.IntergerField(verbose_name='评价数',default=0)
    status = models.IntergerField(verbose_name='状态',default=0,choices=STATUS_CHOICES)
    detail = models.TextField(verbose_name='详情',null=True,blank=True)

    classification1 = models.ForeignKey(Classification,on_delete=models.PROTECT,verbose_name='一级分类',related_name='一级分类')
    classification2 = models.ForeignKey(Classification,on_delete=models.PROTECT,verbose_name='二级分类',related_name='二级分类')
  
    class Meta:
        ordering=['-sales','-comments','-create_time']
        db_table = 'commodity'
        verbose_name = '商品'
        verbose_name_plural = verbose_name


# 商品图片表
class CommodityImg(models.Model):
    src = models.TextField(verbose_name='图片地址')
    priority = models.IntegerField(default=0,verbose_name='优先级')
    commodity = models.ForeignKey(Commodity,on_delete=models.CASCADE,verbose_name='商品')

    class Meta:
        ordering=['-priority','-id']
        db_table = 'commodity_img'
        verbose_name = '商品图'
        verbose_name_plural = verbose_name


  1. 创建序列化器
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import *


class ClassificationSerializer(ModelSerializer):
    class Meta:
        model = Classification
        exclude = ['is_delete']

class CommoditySerializer(ModelSerializer):
    class Meta:
        model = Commodity
        exclude = ['is_delete']

class CommodityImgSerializer(ModelSerializer):
    class Meta:
        model = CommodityImg
        fields = '__all__'

  1. 创建视图
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from .serializers import *

class ClassificationViewSet(ModelViewSet):
    queryset = Classification.objects.filter(is_delete=False)
    serializer_class = ClassificationSerializer

class CommodityViewSet(ModelViewSet):
    queryset = Commodity.objects.filter(is_delete=False)
    serializer_class = CommoditySerializer

class CommodityImgViewSet(ModelViewSet):
    queryset = CommodityImg.objects.filter(is_delete=False)
    serializer_class = CommodityImgSerializer
  1. 创建路由

from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token
from .views import *

urlpatterns = []
router = DefaultRouter()
router.register('classifications', ClassificationViewSet)
router.register('commodity', CommodityViewSet)
router.register('commodity_img', CommodityImgViewSet)
urlpatterns += router.urls
  1. 增加主路由
from django.contrib import admin
from django.urls import path, include
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('drfstudy/', include('drfstudy.urls')),
    path('users/', include('users.urls')),
    path('school/', include('school.urls')),
    path('work/', include('work.urls')),
    path('community/', include('community.urls')),
    path('shopping/', include('shopping.urls')),
    path('docs/', include_docs_urls('DRF6项目接口文档'))  # 配置接口文档路由地址,文档标题
]

2. 优化

商品分类表

1. 序列化器和查询集的优化

  1. 序列化器的优化
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import *


class ClassificationSerializer(ModelSerializer):
    class Meta:
        model = Classification
        exclude = ['is_delete']

class ParentClassificationSerializer(ModelSerializer):
    # 要把子集展示出来,使用管理器。源模型小写_set
    classification_set = ClassificationSerializer(many=True,read_only=True)

    class Meta:
        model = Classification
        exclude = ['is_delete']

  1. 查询集的优化
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from .serializers import *

class ClassificationViewSet(ModelViewSet):
    queryset = Classification.objects.filter(is_delete=False,parent=None)
    serializer_class = ParentClassificationSerializer

  1. 查询结果如下
    在这里插入图片描述
  2. 优化查询结果,继续修改序列化器
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import *


class ClassificationSerializer(ModelSerializer):
    class Meta:
        model = Classification
        fields = ['id','name']

class ParentClassificationSerializer(ModelSerializer):
    # 要把子集展示出来,使用管理器。源模型小写_set
    classification_set = ClassificationSerializer(many=True,read_only=True)

    class Meta:
        model = Classification
        fields = ['id','name','classification_set','parent']

        # 更改属性
        extra_kwargs = {
            # 将parent字段设置为只写,无法查看
            'parent':{'write_only':True} 
        }

优化后查询结果如下:
在这里插入图片描述

  1. 增加权限
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from .serializers import *
from rest_framework.permissions import IsAuthenticated

class ClassificationViewSet(ModelViewSet):
    queryset = Classification.objects.filter(is_delete=False,parent=None)
    serializer_class = ParentClassificationSerializer
    permission_classes = [IsAuthenticated]

    #更改权限装饰器,只有老师才有权限创建
    @wrap_permisssion(TeacherPermission) 
    def create(self, request, *args, **kwargs):
        return ModelViewSet.create(self, request, *args, **kwargs)

    @wrap_permisssion(TeacherPermission)
    def update(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)

    @wrap_permisssion(TeacherPermission)
    def destroy(self, request, *args, **kwargs):
        return ModelViewSet.destroy(self, request, *args, **kwargs)

2. 得到指定类目的所有商品

在ClassificationViewSet中增加以下接口

    @action(methods=['get'],detail=True)
    def commodity(self,request,pk):
        try:
            classification = Classification.objects.filter(is_delete=False).get(id=pk)
        except Classification.DoesNotExist:
            return response(status=HTTP_404_NOT_FOUND)
        # 得到这个类目的所有商品,使用管理器
        # 判断类目是顶层类目还是子类目
        # 默认为顶层类目
        classification_attr = 'classification1'
        # 顶层类目 展示自身类目名
        classification_name = classification.name 
        if classification.parent:
            classification_attr = 'classification2'
            classification_name =  f'{classification.parent.name}---{classification.name}'
        # 使用内置函数getattr获取属性
        data = getattr(classification,classification_attr).filter(is_delete=False,status=1)
        return Response({
            'classification':classification_name,
            'commodity':CommoditySerializer(data,many=True).data
        })

商品表

1. 视图的基础权限等配置

class CommodityViewSet(ModelViewSet):
    queryset = Commodity.objects.filter(is_delete=False)
    serializer_class = CommoditySerializer
    permission_classes = [IsAuthenticated]

    # 重写get_queryset方法的场景:根据不同的操作场景设置不同的查询集
    def get_queryset(self):
        if self.action in ['list','retrieve']: #如果是查询,则只能返回已发布的商品
            return Commodity.objects.filter(is_delete=False,status=1)
        return self.queryset
    
    #更改权限装饰器,只有老师才有权限创建
    @wrap_permisssion(TeacherPermission) 
    def create(self, request, *args, **kwargs):
        return ModelViewSet.create(self, request, *args, **kwargs)
 
    @wrap_permisssion(TeacherPermission)
    def update(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)

    @wrap_permisssion(TeacherPermission)
    def destroy(self, request, *args, **kwargs):
        return ModelViewSet.destroy(self, request, *args, **kwargs)

2. 增加上传商品图片的接口

from config.fastdfsConfig import FASTDFS_SERVER_DOMAIN
from fdfs_client.client import Fdfs_client, get_tracker_conf
import logging
# 导入日志器
logger = logging.getLogger(__name__)

# 创建FastDFS客户端对象
tracker_path = get_tracker_conf('utils/fastdfs/client.conf')
client = Fdfs_client(tracker_path)

class CommodityViewSet(ModelViewSet):
    queryset = Commodity.objects.filter(is_delete=False)
    serializer_class = CommoditySerializer
    permission_classes = [IsAuthenticated]

    # 重写get_queryset方法的场景:根据不同的操作场景设置不同的查询集
    def get_queryset(self):
        if self.action in ['list','retrieve']: #如果是查询,则只能返回已发布的商品
            return Commodity.objects.filter(is_delete=False,status=1)
        return self.queryset
    
    #更改权限装饰器,只有老师才有权限创建
    @wrap_permisssion(TeacherPermission) 
    def create(self, request, *args, **kwargs):
        return ModelViewSet.create(self, request, *args, **kwargs)
 
    @wrap_permisssion(TeacherPermission)
    def update(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)

    @wrap_permisssion(TeacherPermission)
    def destroy(self, request, *args, **kwargs):
        return ModelViewSet.destroy(self, request, *args, **kwargs)

    @action(methods=['post'],detail=True)
    def img(self,request,pk):
        """
        上传图片
        """
        #获取商品
        try:
            commodity = self.get_queryset().get(id=pk)
        except Commodity.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 上传图片
        # 得到上传的文件数据
        file = request.FILES.get('file')
        # 判断是否有图片数据,文件类型是否是图片
        if not file or file.content_type not in ('image/jpeg', 'image/png', 'image/gif'):
            # 不是,返回客户端传入数据错误
            return Response(status=HTTP_400_BAD_REQUEST)

        # 是,交给fastdfs,存储到可用的storage服务上
        # 得到文件后缀名,如果没有后缀名则默认为png类型
        try:
            images_ext_name = file.name.split('.')[-1]
        except Exception as e:
            logger.info('图片拓展名异常:%s' % e)
            images_ext_name = 'png'

        # 上传图片数据,通过字节流
        try:
            upload_res = client.upload_by_buffer(file.read(), file_ext_name=images_ext_name)
        except Exception as e:
            logger.error('图片上传出现异常:%s' % e)
            return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            if upload_res.get('Status') != 'Upload successed.':
                logger.warning('图片上传失败')
                return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)

            # 得到返回的file_id
            image_name = upload_res.get('Remote file_id').decode()
            image_url = FASTDFS_SERVER_DOMAIN + image_name

            #保存图片地址
            CommodityImg.objects.create(src=image_url,commodity = commodity)

            #返回
            return Response({'data':image_url})

修改序列化器,优化查询结果

class CommodityImgSerializer(ModelSerializer):
    class Meta:
        model = CommodityImg
        fields = ['src']

class CommoditySerializer(ModelSerializer):
    # classification1是外键关联的Classification表
    classification1_name = serializers.CharField(source='classification1.name',read_only=True)
    classification2_name = serializers.CharField(source='classification2.name',read_only=True)
    # 添加管理器:类名小写_set
    commodityimg_set = CommodityImgSerializer(many=True,read_only=True)
    class Meta:
        model = Commodity
        exclude = ['is_delete']

优化后的查询结果如下:

在这里插入图片描述

3. 优选商品接口

在CommodityViewSet视图中增加以下接口

    @action(methods=['get'],detail=False)
    def optimization(self,request):
        """
        优选商品接口,返回评论数最多的前五个商品
        """
        # 这里使用order_by会覆盖在模型中使用的order_by
        queryset = self.get_queryset().order_by('-comments').order_by('-create_time')
        # 使用切片返回前五条数据:0,1,2,3,4
        return Response(self.get_serializer(queryset[:5],many=True).data)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值