03 | Django REST framework+Vue 生鲜超市 ——商品类别数据展示
商品类别数据接口
(1)商品分类有两个接口:
一种是全部分类:一级二级三级
一种是某一类的分类以及商品详细信息:
开始写商品分类的接口
序列化
给分类添加三级分类的serializer
from rest_framework import serializers from .models import Goods,GoodsCategory class GoodsSerializer(serializers.ModelSerializer): class Meta: model = Goods fields = "__all__" class CategorySerializer3(serializers.ModelSerializer): class Meta: model = GoodsCategory fields = "__all__" class CategorySerializer2(serializers.ModelSerializer): sub_cat = CategorySerializer3(many=True) class Meta: model = GoodsCategory fields = "__all__" class CategorySerializer(serializers.ModelSerializer): sub_cat = CategorySerializer2(many=True) class Meta: model = GoodsCategory fields = "__all__"
编写视图函数
from goods.serializers import CategorySerializer from .models import Goods,GoodsCategory class CategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): ''' list: 商品分类列表数据 ''' queryset = GoodsCategory.objects.filter(category_type=1) serializer_class = CategorySerializer
说明:
- 注释的内容,在后面生成drf文档的时候会显示出来,所有要写清楚
url配置
from goods.views import CategoryViewSet router.register(r'categorys', CategoryViewSet, base_name="categorys")
访问
http://127.0.0.1:8000/categorys/
访问
http://127.0.0.1:8000/categorys/1/
vue展示商品分类数据
接口相关代码都放在src/api/api.js里面,调试接口的时候我们首先需要新建一个自己的host,然后替换要调试的host
(1)新建local_host
let local_host = 'http://127.0.0.1:8000'
(2)替换商品类别默认的host
开启前端Vue项目
cnpm run dev
访问
http://127.0.0.1:8080/#/app/home/index
发现不显示商品分类了,是因为这涉及到了跨域问题,接下来就解决跨域的问题
drf跨域问题
后端服务器解决跨域问题的方法
(1)安装模块
pip install django-cors-headers
django-cors-headers 使用说明:https://github.com/ottoyiu/django-cors-headers
(2)添加到INSTALL_APPS中
INSTALLED_APPS = ( ...
'coreschema',
... )
(3)添加中间件
下面添加中间件的说明:
CorsMiddleware
should be placed as high as possible, especially before any middleware that can generate responses such as Django's CommonMiddleware
or Whitenoise's WhiteNoiseMiddleware
. If it is not before, it will not be able to add the CORS headers to these responses.
Also if you are using CORS_REPLACE_HTTPS_REFERER
it should be placed before Django's CsrfViewMiddleware
(see more below).
意思就是 要放的尽可能靠前,必须在CsrfViewMiddleware之前。我们直接放在第一个位置就好了
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
(4)设置为True
CORS_ORIGIN_ALLOW_ALL = True
现在再访问
http://127.0.0.1:8080/#/app/home/index
在一级分类中设置为True
vue展示商品列表页数据
商品列表页会判断我们是serach还是getGoods
getListData() { if(this.pageType=='search'){ getGoods({ search: this.searchWord, //搜索关键词 }).then((response)=> { this.listData = response.data.results; this.proNum = response.data.count; }).catch(function (error) { console.log(error); }); }else { getGoods({ page: this.curPage, //当前页码 top_category: this.top_category, //商品类型 ordering: this.ordering, //排序类型 pricemin: this.pricemin, //价格最低 默认为‘’ 即为不选价格区间 pricemax: this.pricemax // 价格最高 默认为‘’ }).then((response)=> { this.listData = response.data.results; this.proNum = response.data.count; }).catch(function (error) { console.log(error); }); } },
说明:
(1)page分页
page_size数量与前端一致
页码参数与起前端一致"page"
goods/views.py
class GoodsPagination(PageNumberPagination): ''' 商品列表自定义分页 ''' #默认每页显示的个数 page_size = 12 #可以动态改变每页显示的个数 page_size_query_param = 'page_size' #页码参数 page_query_param = 'page' #最多能显示多少页 max_page_size = 100
(2)过滤
top_category是商品的一级分类,需要传入参数:一级分类的id
pricemin和pricemax与前端保持一致
获取一级分类下的所有商品
goods/filters.py
import django_filters from .models import Goods from django.db.models import Q class GoodsFilter(django_filters.rest_framework.FilterSet): ''' 商品过滤的类 ''' #两个参数,name是要过滤的字段,lookup是执行的行为,‘小与等于本店价格’ pricemin = django_filters.NumberFilter("shop_price", lookup_expr='gte') pricemax = django_filters.NumberFilter("shop_price", lookup_expr='lte') top_category = django_filters.NumberFilter("category", 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 = ['pricemin', 'pricemax']
(3)排序
GoodsListViewSet中ording与前端要一致
在 goods/views.py中的 GoodsListViewSet 类中配置排序字段
ordering_fields = ('sold_num', 'shop_price')
(5)搜索
在 goods/views.py中的 GoodsListViewSet 类中配置搜索字段
search_fields = ('name', 'goods_brief', 'goods_desc')
(6)替换为local_host
//获取商品列表 export const getGoods = params => { return axios.get(`${local_host}/goods/`, { params: params }) }
现在就可以从后台获取商品的数据了,主要功能
- 分类过滤
- 价格区间过滤
- 显示商品数量
- 分页
- 搜索
完整代码
from django.conf.urls import url,include import xadmin from MxShop.settings import MEDIA_ROOT from django.views.static import serve from rest_framework.documentation import include_docs_urls from goods.views import GoodsListViewSet,CategoryViewSet from rest_framework.routers import DefaultRouter router = DefaultRouter() #配置goods的url router.register(r'goods', GoodsListViewSet) router.register(r'categorys', CategoryViewSet, base_name="categorys") urlpatterns = [ url(r'^admin/', xadmin.site.urls), url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}), url(r'docs/', include_docs_urls(title="慕学生鲜")), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), url('^', include(router.urls)), ]
import django_filters from .models import Goods from django.db.models import Q class GoodsFilter(django_filters.rest_framework.FilterSet): ''' 商品过滤的类 ''' #两个参数,name是要过滤的字段,lookup是执行的行为,‘小与等于本店价格’ pricemin = django_filters.NumberFilter("shop_price", lookup_expr='gte') pricemax = django_filters.NumberFilter("shop_price", lookup_expr='lte') top_category = django_filters.NumberFilter("category", 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 = ['pricemin', 'pricemax']
from rest_framework import serializers from .models import Goods,GoodsCategory class GoodsSerializer(serializers.ModelSerializer): class Meta: model = Goods fields = "__all__" class CategorySerializer3(serializers.ModelSerializer): class Meta: model = GoodsCategory fields = "__all__" class CategorySerializer2(serializers.ModelSerializer): sub_cat = CategorySerializer3(many=True) class Meta: model = GoodsCategory fields = "__all__" class CategorySerializer(serializers.ModelSerializer): sub_cat = CategorySerializer2(many=True) class Meta: model = GoodsCategory fields = "__all__"
from goods.serializers import GoodsSerializer from .models import Goods from rest_framework.response import Response from rest_framework import mixins,viewsets from rest_framework import generics from rest_framework.pagination import PageNumberPagination from .filters import GoodsFilter from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters class GoodsPagination(PageNumberPagination): ''' 商品列表自定义分页 ''' #默认每页显示的个数 page_size = 12 #可以动态改变每页显示的个数 page_size_query_param = 'page_size' #页码参数 page_query_param = 'page' #最多能显示多少页 max_page_size = 100 class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): '商品列表页' #这里必须要定义一个默认的排序,否则会报错 queryset = Goods.objects.all().order_by('id') # 分页 pagination_class = GoodsPagination serializer_class = GoodsSerializer # filter_backends = (DjangoFilterBackend,filters.SearchFilter) filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 设置filter的类为我们自定义的类 filter_class = GoodsFilter #搜索,=name表示精确搜索,也可以使用各种正则表达式 search_fields = ('name', 'goods_brief', 'goods_desc') # 排序 ordering_fields = ('sold_num', 'shop_price') from goods.serializers import CategorySerializer from .models import Goods,GoodsCategory class CategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): ''' list: 商品分类列表数据 ''' queryset = GoodsCategory.objects.filter(category_type=1) serializer_class = CategorySerializer
说明:
这里有两个接口 catogry 和goods,catogry中有用的数据只有各个分类的名字和各个分类的id
goods接口 可以根据过滤器中的top_category = id,在所有的good中检索 ,满足自己父类category_id = id(goods的父类,通过点击导航或商品分类,可能为二级也可能为三级 ) 或者 categroy__parent_category_id = id (goods的父类,通过点击导航或商品分类,可能为二级也可能为三级)
categroy__parent_category__parent_category_id = id ,
只有可能是导航。来多虑数据