美文网首页Django+Vue生鲜电商
【Vue+DRF生鲜电商】31.首页商品按分类显示推荐功能

【Vue+DRF生鲜电商】31.首页商品按分类显示推荐功能

作者: 吾星喵 | 来源:发表于2019-08-14 13:57 被阅读0次

    专题:Vue+Django REST framework前后端分离生鲜电商

    Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。
    Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket
    Django版本:2.2、djangorestframework:3.9.2。
    前端Vue模板可以直接联系我拿。

    更多内容请点击 我的博客 查看,欢迎来访。

    首页商品分类显示功能

    这里面有多个一对多的关系:

    • 一级分类---品牌图片Brand
    • 一级分类---二级分类
    • 一级分类---商品

    在 apps/goods/models.py 中GoodsCategoryBrand添加分类的外键关联名

    class GoodsCategoryBrand(models.Model):
        """
        品牌
        """
        category = models.ForeignKey(GoodsCategory, null=True, blank=True, on_delete=models.CASCADE, verbose_name='商品类别', help_text='商品类别', related_name='brands')
        # ......
    

    Goods中也添加一个分类关联名

    class Goods(models.Model):
        """
        商品
        """
        category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='商品类别', help_text='商品类别', related_name='goods')
        # ......
    

    方便之后从分类中通过关联名称直接取到品牌图片和商品。

    IndexCategoryGoodsSerializer序列化首页分类商品

    在 apps/goods/serializers.py 新建一个 apps/goods/serializers.py类,用于序列化一级分类,以及该分类下的品牌图片,二级分类,和子分类下的所有商品。

    from .models import Goods, GoodsCategory, GoodsImage, Banner, GoodsCategoryBrand
    
    
    # 品牌图片
    class BrandsSerializer(serializers.ModelSerializer):
        class Meta:
            model = GoodsCategoryBrand
            fields = "__all__"
    
    
    # 首页分类商品序列化
    class IndexCategoryGoodsSerializer(serializers.ModelSerializer):
        brands = BrandsSerializer(many=True)  # 分类下的品牌图片
        # goods = GoodsSerializer(many=True)  # 不能这样用,因为现在需要的是一级分类,而大多数商品是放在三级分类中的,所以很多商品是取不到的,所以到自己查询一级分类子类别下的所有商品
        goods = serializers.SerializerMethodField()
        sub_category = CategorySerializer2(many=True)  # 序列化二级分类
    
        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)
            # 返回json对象
            return goods_serializer.data
    
        class Meta:
            model = GoodsCategory
            fields = '__all__'
    

    IndexCategoryGoodsViewSet首页分类及商品显示

    apps/goods/views.py 添加IndexCategoryGoodsViewSet类,用于首页显示

    from .serializers import GoodsSerializer, CategorySerializer, ParentCategorySerializer, BannerSerializer, IndexCategoryGoodsSerializer
    
    class IndexCategoryGoodsViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
        """
        list:
            首页分类、商品数据
        """
        queryset = GoodsCategory.objects.filter(category_type=1)
        serializer_class = IndexCategoryGoodsSerializer
    
        def get_queryset(self):
            # 随机取出几个分类
            import random
            category_id_list = self.queryset.values_list('id', flat=True)
            selected_ids = random.sample(list(category_id_list), 3)
            qs = self.queryset.filter(id__in=selected_ids)
            return qs
    

    首先只获取一级分类,然后再随机选择其中3个显示在首页

    添加首页分类商品路由

    修改 DjangoOnlineFreshSupermarket/urls.py 添加

    from goods.views import GoodsListView, GoodsListViewSet, CategoryViewSet, ParentCategoryViewSet, BannerViewSet, IndexCategoryGoodsViewSet
    
    
    router.register(r'indexgoods', IndexCategoryGoodsViewSet, base_name='indexgoods')  # 首页分类及商品
    

    现在访问 http://127.0.0.1:8000/indexgoods/?format=json 就可以看下序列化后的json格式数据

    BLOG_20190814_135530_67

    由于我们没有添加品牌的图片,所以值为空,之后再补充。

    Vue首页分类商品接口联调

    Vue中显示分类商品的组件在 src/views/index/series-list.vue ,组件创建时调用this.getList()方法

                getList() {
                    queryCategorygoods()
                        .then((response) => {
                            //跳转到首页页response.body面
                            console.log('首页获取分类及商品数据');
                            console.log(response);
                            this.list = response.data
                        })
                        .catch(function (error) {
                            console.log(error);
                        });
                }
    

    这时候会请求queryCategorygoods()函数,也就是访问接口

    //获取商品类别信息
    export const queryCategorygoods = params => {
        return axios.get(`${local_host}/indexgoods/`)
    };
    

    得到数据之后赋值给this.list进行遍历

    这我先把 src/views/index/series-list.vue 的广告位注释掉了,否则数据循环可能会会报错

                    <div class="series_pic">
                        广告位
                        <!--router-link :to="'/app/home/productDetail/'+items.ad_goods.id" target=_blank>
                            <img :src="items.ad_goods.goods_front_image" width="340" height="400">
                        </router-link-->
                    </div>
    

    接下来访问 http://127.0.0.1:8080/#/app/home/index 就能看到这些数据了

    BLOG_20190814_135521_91

    按理说这些商品图片是显示不出来的,因为后端在序列化时,并没有加上域名。之后测试。

    BLOG_20190814_135515_34

    首页类别中的广告位和品牌图片

    首页类别广告模型

    在 apps/goods/models.py 创建一个新的模型

    class IndexCategoryAd(models.Model):
        """
        首页广告
        """
        category = models.ForeignKey(GoodsCategory, null=True, blank=True, on_delete=models.CASCADE, verbose_name='商品类别', help_text='商品类别', related_name='ads')
        goods = models.ForeignKey(Goods, verbose_name='商品', help_text='商品', on_delete=models.CASCADE, related_name='ads')
        add_time = models.DateTimeField(auto_now_add=True, verbose_name='添加时间')
    
        class Meta:
            verbose_name_plural = verbose_name = '首页类别广告'
    
        def __str__(self):
            return self.category
    

    添加完成后记得执行makemigrationsmigrate

    后台类别广告添加一些数据

    修改 apps/goods/admin.py 注册广告的models

    from .models import GoodsCategory, Goods, GoodsImage, IndexCategoryAd
    
    @admin.register(IndexCategoryAd)
    class IndexCategoryAdAdmin(admin.ModelAdmin):
        list_display = ['category', 'goods']
    

    访问Django后台 http://127.0.0.1:8000/admin/goods/indexcategoryad/add/ 添加一些广告位商品,一定要添加到一级分类下

    BLOG_20190814_135508_53

    后台类别广告中category过滤

    当选择商品类别时,会显示所有级别的类别,如果想只显示一级分类,修改 apps/goods/admin.py 中的IndexCategoryAdAdmin

    @admin.register(IndexCategoryAd)
    class IndexCategoryAdAdmin(admin.ModelAdmin):
        list_display = ['category', 'goods']
    
        def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if db_field.name == 'category':
                # 外键下拉框添加过滤
                kwargs['queryset'] = GoodsCategory.objects.filter(category_type=1)
            return super(IndexCategoryAdAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
    

    BLOG_20190814_135503_52

    序列化广告位商品

    修改 apps/goods/serializers.py

    # 首页分类商品序列化
    class IndexCategoryGoodsSerializer(serializers.ModelSerializer):
        brands = BrandsSerializer(many=True)  # 分类下的品牌图片
        # goods = GoodsSerializer(many=True)  # 不能这样用,因为现在需要的是一级分类,而大多数商品是放在三级分类中的,所以很多商品是取不到的,所以到自己查询一级分类子类别下的所有商品
        goods = serializers.SerializerMethodField()
        sub_category = CategorySerializer2(many=True)  # 序列化二级分类
        ad_goods = serializers.SerializerMethodField()  # 广告商品可能加了很多,取每个分类第一个
    
        def get_ad_goods(self, obj):
            all_ads = obj.ads.all()
            if all_ads:
                ad = all_ads.first().goods  # 获取到商品分类对应的商品
                ad_serializer = GoodsSerializer(ad)  # 序列化该广告商品
                return ad_serializer.data
            else:
                # 在该分类没有广告商品时,必须要返回空字典,否则Vue中取obj.id会报错
                return {}
    
        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)
            # 返回json对象
            return goods_serializer.data
    
        class Meta:
            model = GoodsCategory
            fields = '__all__'
    

    接下来访问 http://127.0.0.1:8000/indexgoods/?format=json 就可以显示广告商品的数据了

    BLOG_20190814_135456_70

    后台品牌图片增加一些数据

    访问Django后台 http://127.0.0.1:8000/admin/goods/goodscategorybrand/ 随意添加一些数据到一级分类下

    BLOG_20190814_135445_19

    也可以按照首页类别广告的后台注册代码,对商品类别就行过滤。

    Vue和广告商品联调

    修改Vue主页组件 src/views/index/series-list.vue 将广告位取消注释

                    <div class="series_pic">
                        <router-link :to="'/app/home/productDetail/'+items.ad_goods.id" target=_blank>
                            <img :src="items.ad_goods.goods_front_image" width="340" height="400">
                        </router-link>
                    </div>
    

    BLOG_20190814_135436_36

    由于只添加了部分数据,所有很多都没有显示完整。

    序列化嵌套序列化商品图片url加上域名

    示例序列化的结果:商品图片goods_front_image: "/media/upload/goods_init/images/1_P_1449024889889.jpg",没有加上域名

    将 proxy.js 进行修改,改为一个不存在的地址,修改了需要重启服务器,最好清除浏览器缓存货隐身模式下测试

    module.exports = {
        "/": "http://myserver.com:8001"  //如果部署服务器,需修改为服务器的域名
        //"/": "http://127.0.0.1:8000"
    };
    

    现在访问就出现破图了

    BLOG_20190814_135428_21

    当我们的url没有域名,也就是类似"/media/upload/goods_init/images/1_P_1449024889889.jpg"的地址,就会自动加上 proxy.js 中配置的域名,所以之前能够正常显示。

    在 apps/goods/serializers.py 的IndexCategoryGoodsSerializer中,也就是Serializer调用另一个Serializer是不会自动加上域名的,如果要加上域名,需要在嵌套的Serializer中添加一个参数context={'request': self.context['request']}

    # 首页分类商品序列化
    class IndexCategoryGoodsSerializer(serializers.ModelSerializer):
        brands = BrandsSerializer(many=True)  # 分类下的品牌图片
        # goods = GoodsSerializer(many=True)  # 不能这样用,因为现在需要的是一级分类,而大多数商品是放在三级分类中的,所以很多商品是取不到的,所以到自己查询一级分类子类别下的所有商品
        goods = serializers.SerializerMethodField()
        sub_category = CategorySerializer2(many=True)  # 序列化二级分类
        ad_goods = serializers.SerializerMethodField()  # 广告商品可能加了很多,取每个分类第一个
    
        def get_ad_goods(self, obj):
            all_ads = obj.ads.all()
            if all_ads:
                ad = all_ads.first().goods  # 获取到商品分类对应的商品
                ad_serializer = GoodsSerializer(ad, context={'request': self.context['request']})  # 序列化该广告商品,嵌套的序列化类中添加context参数,可在序列化时添加域名
                return ad_serializer.data
            else:
                # 在该分类没有广告商品时,必须要返回空字典,否则Vue中取obj.id会报错
                return {}
    
        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']})
            # 返回json对象
            return goods_serializer.data
    
        class Meta:
            model = GoodsCategory
            fields = '__all__'
    

    现在广告商品和分类商品的图片url已正常添加域名了

    BLOG_20190814_135419_65

    BLOG_20190814_135414_15

    加上这个context参数后,刷新页面,图片就正常显示了

    BLOG_20190814_135409_15

    相关文章

      网友评论

        本文标题:【Vue+DRF生鲜电商】31.首页商品按分类显示推荐功能

        本文链接:https://www.haomeiwen.com/subject/vpegjctx.html