我给我大姨用Django+Vue开发生鲜电商平台!在乡下也能网购了

一、首页功能完善

首页待完善的功能包括轮播图、新品尝鲜、系列商品等。

1.轮播图实现

轮播图包括3张图片,链接对应3个商品,先在apps/goods/serializers.py中定义序列化如下:

<code>class BannerSerializer(serializers.ModelSerializer): class Meta: model = Banner fields = '__all__'/<code>

再在views.py中定义视图如下:

<code>class BannerViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): ''' list: 轮播图列表 ''' queryset = Banner.objects.filter(is_delete=False).order_by('index') serializer_class = BannerSerializer/<code>

再在urls.py中定义路由如下:

<code># 配置轮播图路由 router.register(r'banners', BannerViewSet, basename='banners')/<code>

测试如下:

可以看到,开始没有轮播图数据,经过在管理后台添加数据后,即同步数据。

前端src/views/index/banners.vue如下:

<code>

methods:{ getBanner(){ bannerGoods() .then((response)=> { console.log(response) //跳转到首页页response.body面 this.banners = response.data }) .catch(function (error) { console.log(error); }); } }, created(){ this.getBanner(); }/<code>

在初始化时调用getBanner()方法,在调用bannerGoods接口请求数据,请求到的数据再通过for循环展示出来。

api.js修改如下:

<code>//获取轮播图 export const bannerGoods = params => { return axios.get(`${local_host}/banners/`) }/<code>

此时再进行测试如下:

显然,请求的图片数据已从本地加载,并且点击轮播图片会调皮转到相应的商品链接。

2.新品功能开发

在定义商品模型时定义了is_new字段表示是否是新品,再实现新品功能时需要用到该字段,直接使用Goods接口并在filters.py过滤器中定义即可,如下:

<code>class GoodsFilter(django_filters.rest_framework.FilterSet): '''商品过滤类''' name = django_filters.CharFilter(field_name="name", lookup_expr='contains') pricemin = django_filters.NumberFilter(field_name="market_price", lookup_expr='gte') pricemax = django_filters.NumberFilter(field_name="market_price", lookup_expr='lte') top_category = django_filters.NumberFilter(method='top_category_filter') def top_category_filter(self, queryset, name, value): '''自定义过滤''' return queryset.filter(Q(category_id=value)|Q(category__parent_category_id=value)|Q(category__parent_category__parent_category_id=value)) class Meta: model = Goods fields = ['name', 'pricemin', 'pricemax', 'is_hot', 'is_new']/<code>

演示如下:

显然,在手动添加新品之后,新品数据即同步,请求的参数中包含is_new=true。

前端src/views/index/news.vue如下:

可以看到,在初始化时,调用getOpro()方法,在调用getGoods接口时传入参数is_new,来获取新品,与之前获取商品调用的接口相同,获取到数据后通过for循环显示出来。

访问示意如下:

3.商品系列分类展示功能

商品系列分类包括左侧的导航栏和右侧的商品列表,大类对应多个品牌、大类对应多个小类、大类对应多个商品,即包含3个一对多关系,在定义序列化时需要嵌套定义

为了实现嵌套,在定义GoodsCategoryBrand模型时需要指定related_name属性,如下:

<code>class GoodsCategoryBrand(models.Model): '''品牌名''' category = models.ForeignKey(GoodsCategory, verbose_name='商品类目', related_name='brands', null=True, on_delete=models.SET_NULL) name = models.CharField(default='', max_length=30, verbose_name='品牌名', help_text='品牌名') desc = models.TextField(default='', max_length=200, verbose_name='品牌描述', help_text='品牌描述') image = models.ImageField(max_length=200, upload_to='brands/') add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加时间') is_delete = models.BooleanField(default=False, verbose_name='是否删除') class Meta: verbose_name = '品牌' verbose_name_plural = verbose_name def __str__(self): return self.name class IndexAd(models.Model): category = models.ForeignKey(GoodsCategory, verbose_name='商品类目', related_name='category', null=True, on_delete=models.SET_NULL) goods = models.ForeignKey(Goods, verbose_name='商品', related_name='goods', null=True, on_delete=models.SET_NULL) class Meta: verbose_name = '首页商品类别广告' verbose_name_plural = verbose_name def __str__(self): return self.goods.name/<code>

完成后需要进行数据映射。

为了在创建brand时只显示一级类别,在adminx.py中定义GoodsBrandAdmin类时重写了get_context()方法,其中获取到category字段只取category_type为1的数据,如下:

<code>定义序列化如下: class BannerSerializer(serializers.ModelSerializer): class Meta: model = Banner fields = '__all__' class BrandSerializer(serializers.ModelSerializer): class Meta: model = GoodsCategoryBrand fields = '__all__' class IndexCategorySerializer(serializers.ModelSerializer): brands = BrandSerializer(many=True) goods = serializers.SerializerMethodField() sub_cat = SecCategorySerializer(many=True) ad_goods = serializers.SerializerMethodField() 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) return goods_serializer.data def get_ad_goods(self, obj): goods_json = {} ad_goods = IndexAd.objects.filter(category_id=obj.id) if ad_goods: good_instance = ad_goods[0].goods goods_json = GoodsSerializer(good_instance, many=False).data return goods_json class Meta: model = GoodsCategory fields = '__all__'class GoodsBrandAdmin(object): list_display = ["category", "image", "name", "desc"] def get_context(self): context = super(GoodsBrandAdmin, self).get_context() if 'form' in context: context['form'].fields['category'].queryset = GoodsCategory.objects.filter(category_type=1) return context/<code>

定义序列化如下:

<code>class BannerSerializer(serializers.ModelSerializer): class Meta: model = Banner fields = '__all__' class BrandSerializer(serializers.ModelSerializer): class Meta: model = GoodsCategoryBrand fields = '__all__' class IndexCategorySerializer(serializers.ModelSerializer): brands = BrandSerializer(many=True) goods = serializers.SerializerMethodField() sub_cat = SecCategorySerializer(many=True) ad_goods = serializers.SerializerMethodField() 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) return goods_serializer.data def get_ad_goods(self, obj): goods_json = {} ad_goods = IndexAd.objects.filter(category_id=obj.id) if ad_goods: good_instance = ad_goods[0].goods goods_json = GoodsSerializer(good_instance, many=False).data return goods_json class Meta: model = GoodsCategory fields = '__all__'/<code>

可以看到,定义了多个一对多的关系和一个一对一的关系,视图如下:

<code>定义路由如下: # 配置首页商品系列路由 router.register(r'indexgoods', IndexCategoryViewSet, basename='indexgoods')class IndexCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): ''' list: 商品分类数据 ''' queryset = GoodsCategory.objects.filter(is_delete=False, is_tab=True, name__in=['生鲜食品', '酒水饮料']) serializer_class = IndexCategorySerializer /<code>

定义路由如下:

<code># 配置首页商品系列路由 router.register(r'indexgoods', IndexCategoryViewSet, basename='indexgoods')/<code>

进行测试:

可以看到,再添加数据brands和ad_goods之前这两类数据为空,添加之后数据同步。

但是同时也可以看到,在商品的image属性的值即图片链接中未自动添加域名,这是因为进行嵌套序列化默认不会添加域名,需要给字段设置context属性,配置如下:

<code>class IndexCategorySerializer(serializers.ModelSerializer): brands = BrandSerializer(many=True) goods = serializers.SerializerMethodField() sub_cat = SecCategorySerializer(many=True) ad_goods = serializers.SerializerMethodField() 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 def get_ad_goods(self, obj): goods_json = {} ad_goods = IndexAd.objects.filter(category_id=obj.id) if ad_goods: good_instance = ad_goods[0].goods goods_json = GoodsSerializer(good_instance, many=False, context={'request': self.context['request']}).data return goods_json class Meta: model = GoodsCategory fields = '__all__'/<code>

此时再查看如下:

显然,已经将域名显示出来。

前端src/views/index/series-list.vue如下: