在最近十天我会用Python做一个购物类项目,会用到Django+Mysql+Redis+Vue等。
今天是第五天,主要负责撰写响应具体的商品数据。若是有不懂大家可以先阅读我的前四篇博客以能够顺承。
若是大家基础有不懂的,小编前面已经关于Django博客都更新完了,大家可以自行查看。若是有需要更改的地方欢迎大家指正,同时也欢迎大家关注点赞收藏等待后续,小编会尽力更新优质博客。
在此我会直接继承上一篇博客继续往下写。
关于商品数据,我们在统计时候我们可以按照一个大类再嵌套一个小类再嵌套具体的商品。
而具体的我们按照两大类:
SPU 和 SKU
SPU:标准化产品单位 , SPU 是商品信息聚合的最小单位,比如:Iphone 15就是一个 SPU;体现一件商品的基本特征;
在数据库中 ,SPU 一般包含了商品的通用属性 ,品牌 , 型号 , 主要功能等。
SKU:库存量单位,SKU是库存管理和销售的最小单位 , 在同一个 SPU 下可能会有多个 SKU;SKU是包含商品的具体属性:颜色,大小,配置,产地。
在首页中我们要响应关于商品的一些广告和分类内容。
from django.db import models from utils.model import BaseModel class ContentCategory(BaseModel): """广告内容类别""" name = models.CharField(max_length=50, verbose_name='名称') key = models.CharField(max_length=50, verbose_name='类别键名') class Meta: db_table = 'tb_content_category' verbose_name = '广告内容类别' verbose_name_plural = verbose_name def __str__(self): return self.name class Content(BaseModel): """广告内容""" category = models.ForeignKey(ContentCategory, on_delete=models.PROTECT, verbose_name='类别') title = models.CharField(max_length=100, verbose_name='标题') url = models.CharField(max_length=300, verbose_name='内容链接') image = models.ImageField(null=True, blank=True, verbose_name='图片') text = models.TextField(null=True, blank=True, verbose_name='内容') sequence = models.IntegerField(verbose_name='排序') status = models.BooleanField(default=True, verbose_name='是否展示') class Meta: db_table = 'tb_content' verbose_name = '广告内容' verbose_name_plural = verbose_name def __str__(self): return self.category.name + ': ' + self.title
关于商品的一些模型类我们首先创建一个应用goods,在goods的models文件下要建立多个模型类:
from django.db import models from utils.model import BaseModel class GoodsCategory(BaseModel): """商品类别""" name = models.CharField(max_length=10, verbose_name='名称') parent = models.ForeignKey('self', related_name='subs', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父类别') class Meta: db_table = 'tb_goods_category' verbose_name = '商品类别' verbose_name_plural = verbose_name def __str__(self): return self.name class GoodsChannelGroup(BaseModel): """商品频道组""" name = models.CharField(max_length=20, verbose_name='频道组名') class Meta: db_table = 'tb_channel_group' verbose_name = '商品频道组' verbose_name_plural = verbose_name def __str__(self): return self.name class GoodsChannel(BaseModel): """ 商品频道 对每一类商品进行分组 , 方便构造项目的导航栏 将上面两个模型合在一起 """ group = models.ForeignKey(GoodsChannelGroup, verbose_name='频道组名', on_delete=models.CASCADE) category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='顶级商品类别') url = models.CharField(max_length=50, verbose_name='频道页面链接') sequence = models.IntegerField(verbose_name='组内顺序') class Meta: db_table = 'tb_goods_channel' verbose_name = '商品频道' verbose_name_plural = verbose_name def __str__(self): return self.category.name class Brand(BaseModel): """品牌""" name = models.CharField(max_length=20, verbose_name='名称') logo = models.ImageField(verbose_name='Logo图片') first_letter = models.CharField(max_length=1, verbose_name='品牌首字母') class Meta: db_table = 'tb_brand' verbose_name = '品牌' verbose_name_plural = verbose_name def __str__(self): return self.name class SPU(BaseModel): """商品SPU 连接了第一个模型类 """ name = models.CharField(max_length=50, verbose_name='名称') brand = models.ForeignKey(Brand, on_delete=models.PROTECT, verbose_name='品牌') category1 = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, related_name='cat1_spu', verbose_name='一级类别') category2 = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, related_name='cat2_spu', verbose_name='二级类别') category3 = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, related_name='cat3_spu', verbose_name='三级类别') sales = models.IntegerField(default=0, verbose_name='销量') comments = models.IntegerField(default=0, verbose_name='评价数') desc_detail = models.TextField(default='', verbose_name='详细介绍') desc_pack = models.TextField(default='', verbose_name='包装信息') desc_service = models.TextField(default='', verbose_name='售后服务') class Meta: db_table = 'tb_spu' verbose_name = '商品SPU' verbose_name_plural = verbose_name def __str__(self): return self.name class SKU(BaseModel): """商品SKU""" name = models.CharField(max_length=50, verbose_name='名称') caption = models.CharField(max_length=100, verbose_name='副标题') spu = models.ForeignKey(SPU, on_delete=models.CASCADE, verbose_name='商品') category = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, verbose_name='从属类别') price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='单价') cost_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='进价') market_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='市场价') stock = models.IntegerField(default=0, verbose_name='库存') sales = models.IntegerField(default=0, verbose_name='销量') comments = models.IntegerField(default=0, verbose_name='评价数') is_launched = models.BooleanField(default=True, verbose_name='是否上架销售') default_image = models.ImageField(max_length=200, default='', null=True, blank=True, verbose_name='默认图片') class Meta: db_table = 'tb_sku' verbose_name = '商品SKU' verbose_name_plural = verbose_name def __str__(self): return '%s: %s' % (self.id, self.name) class SKUImage(BaseModel): """SKU图片""" sku = models.ForeignKey(SKU, on_delete=models.CASCADE, verbose_name='sku') image = models.ImageField(verbose_name='图片') class Meta: db_table = 'tb_sku_image' verbose_name = 'SKU图片' verbose_name_plural = verbose_name def __str__(self): return '%s %s' % (self.sku.name, self.id) class SPUSpecification(BaseModel): """商品SPU规格""" spu = models.ForeignKey(SPU, on_delete=models.CASCADE, related_name='specs', verbose_name='商品SPU') name = models.CharField(max_length=20, verbose_name='规格名称') class Meta: db_table = 'tb_spu_specification' verbose_name = '商品SPU规格' verbose_name_plural = verbose_name def __str__(self): return '%s: %s' % (self.spu.name, self.name) class SpecificationOption(BaseModel): """规格选项""" spec = models.ForeignKey(SPUSpecification, related_name='options', on_delete=models.CASCADE, verbose_name='规格') value = models.CharField(max_length=20, verbose_name='选项值') class Meta: db_table = 'tb_specification_option' verbose_name = '规格选项' verbose_name_plural = verbose_name def __str__(self): return '%s - %s' % (self.spec, self.value) class SKUSpecification(BaseModel): """SKU具体规格""" sku = models.ForeignKey(SKU, related_name='specs', on_delete=models.CASCADE, verbose_name='sku') spec = models.ForeignKey(SPUSpecification, on_delete=models.PROTECT, verbose_name='规格名称') option = models.ForeignKey(SpecificationOption, on_delete=models.PROTECT, verbose_name='规格值') class Meta: db_table = 'tb_sku_specification' verbose_name = 'SKU规格' verbose_name_plural = verbose_name def __str__(self): return '%s: %s - %s' % (self.sku, self.spec.name, self.option.value) class GoodsVisitCount(BaseModel): """统计分类商品访问量模型类""" category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='商品分类') count = models.IntegerField(verbose_name='访问量', default=0) date = models.DateField(auto_now_add=True, verbose_name='统计日期') class Meta: db_table = 'tb_goods_visit' verbose_name = '统计分类商品访问量' verbose_name_plural = verbose_name
而我们要传递的数据可以直接传递到数据库当中。
我们不难知道,首页商品的分类我们主要可以分三大类(第一类嵌套第二类,二类嵌套三类)。
当然数据库的内容我们也是按照如此形式,来关联三个大类的表的。
此时,在我们首页的视图当中,我们先对第一大类进行获取,其次二和三。关于具体的操作详细都在我的代码中有详细的介绍。
在下面代码中,获取完事三大类的商品数据后我们开始获取一些广告数据,方法也和上面一致。
from django.shortcuts import render from django.views import View from goods.models import GoodsChannel from contents.models import ContentCategory,Content class IndexView(View): ''' 响应首页 ''' def get(self , request): # 定义一个空的字典 , 存放商品分类的数据 categories = {} # 查询商品分组的所有数据 channels = GoodsChannel.objects.all() # 获取所有商品的分组 for channel in channels: # 获取商品分组的 id , 作为分组字典的 key group_id = channel.group_id # 判断获取到的分组在字段中是否存在 if group_id not in categories: categories[group_id] = {'channels':[] , 'sub_cats':[]} # 查询一级商品的数据信息 # 根据商品的外键数据 , 判断是否为以及商品类别 cat1 = channel.category categories[group_id]['channels'].append( { 'id':cat1.id, 'name':cat1.name, 'url' : channel.url } ) # 获取二级商品类别数据 # 二级的数据根据一级的类别 id 进行获取 for cat2 in cat1.subs.all(): cat2.sub_cat = [] categories[group_id]['sub_cats'].append( { 'id':cat2.id, 'name':cat2.name, 'sub_cat':cat2.sub_cat } ) # 获取三级数据 for cat3 in cat2.subs.all(): cat2.sub_cat.append( { 'id': cat3.id, 'name':cat3.name } ) #首页商品推荐数据信息 #获取到所有推荐商品的广告类别 content_categories = ContentCategory.objects.all() contents = {} for content_category in content_categories: contents[content_category.key] = Content.objects.filter( category_id=content_category.id, status=True#是否展示 ).all().order_by('sequence')#排序 context = {'categories': categories, 'contents': contents}#要返回到前端的内容 return render(request, 'index.html', context=context)
也就是要相应为下图这样的前端页面,根据我们创建的视图来响应。
{% for group in categories.values %} - {% for channel in group.channels %} { channel.url }}">{{ channel.name }} {% endfor %} {% for cat2 in group.sub_cats %} {{ cat2.name }} > {% for cat3 in cat2.sub_cat %} { cat3.id }}/1/">{{ cat3.name }} {% endfor %} {% endfor %}
{% endfor %}
转播图我们响应时候注意拼接地址。
{% for content in contents.index_lbt %} - { content.url }}">{ content.image }}.jpg" alt="幻灯片">
{% endfor %}
第一层:
{ contents.index_1f_logo.0.image.url }}.jpg"> {% for content in contents.index_1f_pd %} { content.url }}">{{ content.title }} {% endfor %} {% for content in contents.index_1f_bq %} { content.url }}">{{ content.title }} {% endfor %} {% for content in contents.index_1f_ssxp %} - { content.url }}" class="goods_pic"> { content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
{% for content in contents.index_1f_cxdj %} - { content.url }}" class="goods_pic"> { content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
{% for content in contents.index_1f_sjpj %} - { content.url }}" class="goods_pic"> { content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
第二层:
{ contents.index_1f_logo.0.image.url }}.jpg"> {% for content in contents.index_2f_pd %} { content.url }}">{{ content.title }} {% endfor %} {% for content in contents.index_2f_bq %} { content.url }}">{{ content.title }} {% endfor %} {% for content in contents.index_2f_cxdj %} - { content.url }}" class="goods_pic">{ content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
{% for content in contents.index_2f_jjhg %} - { content.url }}" class="goods_pic">{ content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
第三层:
{ contents.index_1f_logo.0.image.url }}.jpg"> {% for content in contents.index_3f_pd %} { content.url }}">{{ content.title }} {% endfor %} {% for content in contents.index_3f_bq %} { content.url }}">{{ content.title }} {% endfor %} {% for content in contents.index_3f_shyp %} - { content.url }}" class="goods_pic">{ content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
{% for content in contents.index_3f_cfyp %} - { content.url }}" class="goods_pic">{ content.image.url }}.jpg">
{ content.url }}" title="{{ content.title }}">{{ content.title }}
{{ content.text }} {% endfor %}
快讯
更多 > {% for content in contents.index_kx %} - { content.url }}">{{ content.title }}
{% endfor %}
{% for content in contents.index_ytgg %} { content.url }}" class="advs">{ content.image }}.jpg"> {% endfor %}
如图,所谓商品列表页就是点击三级商品名称能够显示出具体的商品信息。
其中主要分为几大部分:
1、商品分类导航栏(也就是显示商品的三个级别信息)
2、具体商品(要分默认、价格、人气三部分)
因为在前面我们在获取三大级别的商品数据信息的时候我们是直接在响应首页的模型类里面进行的,但获取信息我们不仅是要在首页用到在后面很多地方都会用到比如商品分类导航栏,所以我们将获取商品三个级别信息的代码转移到content应用下的utils文件下:
from goods.models import GoodsChannel def get_categories(): # 定义一个空的字典 , 存放商品分类的数据 categories = {} # 查询商品分组的所有数据 channels = GoodsChannel.objects.all() # 获取所有商品的分组 for channel in channels: # 获取商品分组的 id , 作为分组字典的 key group_id = channel.group_id # 判断获取到的分组在字段中是否存在 if group_id not in categories: categories[group_id] = {'channels': [], 'sub_cats': []} # 查询一级商品的数据信息 # 根据商品的外键数据 , 判断是否为以及商品类别 cat1 = channel.category categories[group_id]['channels'].append( { 'id': cat1.id, 'name': cat1.name, 'url': channel.url } ) # 获取二级商品类别数据 # 二级的数据根据一级的类别 id 进行获取 for cat2 in cat1.subs.all(): cat2.sub_cat = [] categories[group_id]['sub_cats'].append( { 'id': cat2.id, 'name': cat2.name, 'sub_cat': cat2.sub_cat } ) # 获取三级数据 for cat3 in cat2.subs.all(): cat2.sub_cat.append( { 'id': cat3.id, 'name': cat3.name } ) return categories
实现这个页面我们无非就是:响应页面-实现商品分类导航栏-实现商品具体信息的提取-响应到前端。
对于实现商品分类导航栏我们的思路就是,当我们点击某级商品时候,我们会进入这个页面,而我们要响应出对应的三个级别的信息,那么我们同样的在goods应用下面找到utils文件进行获取。
from goods.models import GoodsCategory def get_breadcrumd(category): # category 商品类别对象 # 一级:breadcrumd = {cat1:''} # 二级:breadcrumd = {cat1:'' , cat2:''} # 三级:breadcrumd = {cat1:'' , cat2:'' , cat3:''} breadcrumd = {'cat1': '' , 'cat2': '' , 'cat3': ''} if category.parent == None: # 没有外键数据 , 说明类别为一级 breadcrumd['cat1'] = category elif GoodsCategory.objects.filter(parent_id = category.id).count() == 0: #就是它的外键既不是空,且外键没有能与别的主键关联的,那就只能是第三级 #第二级=第三级.外键 #第一级=第二级.外键 #二级数据的主键是有存在于外键,而三级数据的主键没外键与它关联 # 判断是否有被链接 , 如果没有被链接说明是三级数据 # 三级是通过二级数据连接获取一级的数据 breadcrumd['cat3'] = category cat2 = category.parent breadcrumd['cat2'] = cat2 breadcrumd['cat1'] = cat2.parent else: # 二级 breadcrumd['cat1'] = category.parent breadcrumd['cat2'] = category return breadcrumd
就是它的外键既不是空,且外键没有能与别的主键关联的,那就只能是第三级,此时:第二级=第三级.外键,第一级=第二级.外键。二级数据的主键是有存在于外键,而三级数据的主键没外键与它关联
而此时的模型类:
class GoodsListView(View): ''' 商品列表页 ''' def get(self,request,category_id,page_num): #获取商品分类功能(获取到首页) categories=get_categories() #获取商品类别对象 category=GoodsCategory.objects.get(id=category_id) ''' 这个就是因为响应具体数据时会发出get请求id,然后获取这个id的数据, 为的就是下面能够按照这个数据将它的一级二级三级数据全部响应到页面上 ''' #调用列表导航栏(面包屑) breadcrumd=get_breadcrumd(category)
我们设计的意图就这三类:按照默认(也就是按照时间排序)、价格(按照价格高低排序)、人气(按照购买数量排序)。
同样的是和上面的导航栏在同一模型类下继续写,因为同样的需要获取到这些数据。
class GoodsListView(View): ''' 商品列表页 ''' def get(self,request,category_id,page_num): #获取商品分类功能(获取到首页) categories=get_categories() #获取商品类别对象 category=GoodsCategory.objects.get(id=category_id) ''' 这个就是因为响应具体数据时会发出get请求id,然后获取这个id的数据, 为的就是下面能够按照这个数据将它的一级二级三级数据全部响应到页面上 ''' #调用列表导航栏(面包屑) breadcrumd=get_breadcrumd(category) #商品排序,也就是前端页面的默认价格人气那块(获取请求中的参数sort,获取商品排序的方式,若是没有sort参数,默认按照时间排序) sort=request.GET.get("sort","default") if sort=='price': sort_field='price' elif sort=='hot': sort_field = 'sales' else: sort='default' sort_field='create_time' #获取商品资源 skus=SKU.objects.filter(is_launched=True,category_id=category_id).order_by(sort_field)#判断是否上架 #制作分页器 paginator=Paginator(skus,5) #获取当前页面的数据 page_skus=paginator.page(page_num) #获取分页的总数 total_num=paginator.num_pages context={ 'categories':categories, 'breadcrumd':breadcrumd, 'page_skus':page_skus, 'total_num':total_num, 'category_id':category_id, 'page_num':page_num, 'sort':sort, } return render(request,'list.html',context=context)
注意的一点,制作分页器也要相应到前端页面,具体的代码详细解释代码中都有注释。
相应导航栏,和上面相应的思路代码都一致。
商品分类
{% for group in categories.values %} - {% for channel in group.channels %} { channel.url }}">{{ channel.name }} {% endfor %} {% for cat2 in group.sub_cats %} {{ cat2.name }} > {% for cat3 in cat2.sub_cat %} { cat3.id }}/1/">{{ cat3.name }} {% endfor %} {% endfor %}
{% endfor %}
响应商品数据信息:要注意的是关于照片地址的拼接。
{% for sku in page_skus %} - { sku.default_image.url }}.jpg/">
{{ sku.name }}
{% endfor %}
对默认、价格、销量的响应:
分页器响应:注意分页器的响应要在底下导航信息前面
#总结:
关于此篇博客小编主要介绍了关于在购物类项目关于商品具体信息如何响应的内容,其中最为主要的就是数据库和Django之间的来回调用,以及前端与后端之间的连接。小编后续还会继续更新后续内容,欢迎大家前来阅读!同时也欢迎大家点赞关注,您的支持将是小编变强的最大动力!
相关内容