专题:Vue+Django REST framework前后端分离生鲜电商
Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。
Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ;
Django版本:2.2、djangorestframework:3.9.2。
前端Vue模板可以直接联系我拿。
更多内容请点击 我的博客 查看,欢迎来访。
这里面有多个一对多的关系:
在 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')
# ......
方便之后从分类中通过关联名称直接取到品牌图片和商品。
在 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__'
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格式数据
由于我们没有添加品牌的图片,所以值为空,之后再补充。
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">
广告位
div>
接下来访问 http://127.0.0.1:8080/#/app/home/index 就能看到这些数据了
[外链图片转存失败(img-Uur9LbNm-1565762239403)(https://blog.starmeow.cn/media/blog/images/2019/08/BLOG_20190814_135521_91.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 “博客图集BLOG_20190814_135521_91.png”)]
按理说这些商品图片是显示不出来的,因为后端在序列化时,并没有加上域名。之后测试。
在 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
添加完成后记得执行makemigrations
和migrate
修改 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/ 添加一些广告位商品,一定要添加到一级分类下
当选择商品类别时,会显示所有级别的类别,如果想只显示一级分类,修改 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)
修改 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 就可以显示广告商品的数据了
访问Django后台 http://127.0.0.1:8000/admin/goods/goodscategorybrand/ 随意添加一些数据到一级分类下
也可以按照首页类别广告的后台注册代码,对商品类别就行过滤。
修改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>
[外链图片转存失败(img-vvErSSVo-1565762239404)(https://blog.starmeow.cn/media/blog/images/2019/08/BLOG_20190814_135436_36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 “博客图集BLOG_20190814_135436_36.png”)]
由于只添加了部分数据,所有很多都没有显示完整。
示例序列化的结果:商品图片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"
};
现在访问就出现破图了
[外链图片转存失败(img-mHvIdwSp-1565762239405)(https://blog.starmeow.cn/media/blog/images/2019/08/BLOG_20190814_135428_21.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 “博客图集BLOG_20190814_135428_21.png”)]
当我们的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已正常添加域名了
[外链图片转存失败(img-iqpYankq-1565762239405)(https://blog.starmeow.cn/media/blog/images/2019/08/BLOG_20190814_135414_15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 “博客图集BLOG_20190814_135414_15.png”)]
加上这个context
参数后,刷新页面,图片就正常显示了
[外链图片转存失败(img-E8nmxpQ8-1565762239405)(https://blog.starmeow.cn/media/blog/images/2019/08/BLOG_20190814_135409_15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 “博客图集BLOG_20190814_135409_15.png”)]