Vue+Django REST Framework生鲜电商项目学习笔记——商品列表页

分为以下几个环节:

  1. django的view实现商品列表页
  2. django的serializer序列化model
  3. apiview方式实现商品列表页
  4. drf的modelserializer实现商品列表页功能
  5. GenericView方式实现商品列表页和分页功能详解
  6. viewsets和router完成商品列表页
  7. drf的Apiview、GenericView、Viewset和router的原理分析
  8. drf的request和response
  9. drf的过滤
  10. drf的搜索和排序

django的view实现商品列表页

在goods文件夹下新建views_base.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django.views.generic.base import View

from goods.models import Goods


class GoodsListView(View):
def get(self, request):
"""
通过django的view实现商品列表页
:param request:
:return:
"""
json_list = list()
goods = Goods.objects.all()[:10]
for good in goods:
json_dict = dict()
json_dict["name"] = good.name
json_dict["category"] = good.category.name
json_dict["market_price"] = good.market_price
# json_dict["add_time"] = good.add_time
json_list.append(json_dict)

from django.http import HttpResponse
import json
return HttpResponse(json.dumps(json_list), content_type="application/json")

这样就通过django view的方式返回了商品列表数据。

但其中有很多问题,例如在json_dict中加入add_time这个字段,就会出问题:

django的serializer序列化model

上述采用django view实现商品列表页的方式太过繁琐,需要手动获取每一条字段的信息,如下代码可以解决:

1
2
3
4
from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
json_list.append(json_dict)

但还是会遇到某些字段如Image_field不可序列化的问题,其实,django提供了一种serializer可以解决:

1
2
3
4
5
6
7
import json
from django.core import serializers
json_data = serializers.serialize("json", goods)
json_data = json.loads(json_data)

from django.http import JsonResponse
return JsonResponse(json_data, safe=False)

apiview方式实现商品列表页

首先根据drf官方文档将必要的包补全:pip install coreapi django-guardian ,然后将'rest_framework'添加到INSTALLED_APPS设置中“

1
2
3
4
INSTALLED_APPS = (
...
'rest_framework',
)

将drf文档功能和drf登录功能引入根urls.py中:

1
2
3
4
5
6
7
from rest_framework.documentation import include_docs_urls

urlpatterns = [
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
...
url(r'docs/', include_docs_urls(title='慕学生鲜')),
]

首先利用drf的serializers在goods下新建serializers.py:

1
2
3
4
5
6
from rest_framework import serializers


class GoodsSerializer(serializers.Serializer):
name = serializers.CharField(required=True, max_length=100)
click_num = serializers.IntegerField(default=0)

这里只用两个字段简单看一下效果

views.py中,参考官方文档中的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from .serializers import GoodsSerializer
from rest_framework.views import APIView
from rest_framework.response import Response

from .models import Goods


# Create your views here.

class GoodsListView(APIView):
def get(self, request, format=None):
goods = Goods.objects.all()[:10]
goods_serializer = GoodsSerializer(goods, many=True)
return Response(goods_serializer.data)

最后,在urls.py中添加from goods.views import GoodsListView,运行访问localhost:8000/goods/

可以看到完成了商品列表页的功能。

drf的modelserializer实现商品列表页功能

可以利用serializers.ModelSerializer精简我们的代码:

1
2
3
4
5
6
7
8
9
10
from rest_framework import serializers

from goods.models import Goods


class GoodsSerializer(serializers.ModelSerializer):

class Meta:
model = Goods
fields = ('name', 'click_num', 'market_price', 'add_time')

可以将fields改为fields = '__all__'即可序列化所有字段:

为了详细展示出category的信息,可以作如下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from rest_framework import serializers

from goods.models import Goods, GoodsCategory


class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = '__all__'


class GoodsSerializer(serializers.ModelSerializer):
category = CategorySerializer()

class Meta:
model = Goods
fields = '__all__'

结果:

GenericView方式实现商品列表页和分页功能

为了精简GoodsListView中的代码,我们引入drf的mixins和generics:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from .serializers import GoodsSerializer
from rest_framework import mixins
from rest_framework import generics

from .models import Goods


# Create your views here.

class GoodsListView(mixins.ListModelMixin, generics.GenericAPIView):
"""
商品列表页
"""

queryset = Goods.objects.all()[:10]
serializer_class = GoodsSerializer

def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

其实也可以利用generics.ListAPIView来完成,更加简单:

1
2
3
4
5
6
7
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

可以看到它继承了我们之前使用的mixins.ListModelMixin和GenericAPIView。

所以之前的代码就可以精简为:

1
2
3
4
5
6
7
class GoodsListView(generics.ListAPIView):
"""
商品列表页
"""

queryset = Goods.objects.all()[:10]
serializer_class = GoodsSerializer

一般,列表页是需要分页的,利用drf进行分页只要在settings.py中简单设置一下即可:

1
2
3
4
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}

实际上可以通过在Views中通过pagination class来自定义分页效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from .serializers import GoodsSerializer
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
from .models import Goods


# Create your views here.

class GoodsPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
page_query_param = "p"
max_page_size = 100


class GoodsListView(generics.ListAPIView):
"""
商品列表页
"""

queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination

运行之后,自定义效果生效,并且可以动态添加参数:

viewsets和router完成商品列表页

如何使用?:viewsets和router

使用viewsets完成商品列表页:

views.py中修改:

1
2
3
4
5
6
7
8
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""

queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination

urls.py中修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from goods.views import GoodsListViewSet

goods_list = GoodsListViewSet.as_view({
'get': 'list',
})

urlpatterns = [
...

# 商品列表页
url(r'goods/$', goods_list, name="goods-list"),

...
]

使用router完成商品列表页

因为我们使用ViewSet类而不是View类,所以实际上我们不需要自己设计URL。 将资源连接到视图和URL的约定可以使用Router类自动处理。 我们所要做的就是用router注册适当的视图集,然后剩下的就依靠router自动完成。

urls.py中修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 rest_framework.routers import DefaultRouter

from goods.views import GoodsListViewSet

router = DefaultRouter()

# 配置goods的url
router.register(r'goods', GoodsListViewSet)

urlpatterns = [
url(r'^xadmin/', xadmin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}),

url(r'^', include(router.urls)),

url(r'docs/', include_docs_urls(title='慕学生鲜'))
]

drf的APIView、GenericView、Viewsets和router的原理分析

还是老老实实看源码和文档吧!

贴一篇博客:http://yindongliang.com/2017/04/20/talk-about-django-rest-framework

drf的request和response

tutorial:http://www.django-rest-framework.org/tutorial/2-requests-and-responses/

api:http://www.django-rest-framework.org/api-guide/requests/

http://www.django-rest-framework.org/api-guide/responses/

drf的过滤

api:http://www.django-rest-framework.org/api-guide/filtering/

views.py中代码修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from .serializers import GoodsSerializer
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.pagination import PageNumberPagination
from rest_framework import viewsets, mixins
from .models import Goods


# Create your views here.

class GoodsPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
page_query_param = "p"
max_page_size = 100


class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""

queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend,)
filter_fields = ('name', 'shop_price')

可以看到页面中多了一个过滤器。也可以参考django-filter官方文档自定义filter:

新建filters.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django_filters import rest_framework as filters

from .models import Goods


class GoodsFilter(filters.FilterSet):
"""
商品的过滤类
"""
price_min = filters.NumberFilter(name="shop_price", lookup_expr="gte")
price_max = filters.NumberFilter(name="shop_price", lookup_expr="lte")

class Meta:
model = Goods
fields = ['price_min', 'price_max']

views.py中修改:

1
2
3
4
5
6
7
8
9
10
11
from .filters import GoodsFilter

...

class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""

...
filter_class = GoodsFilter

题外话,实现模糊查询效果:name = filters.CharFilter(name="name", lookup_expr="icontains")

drf的搜索和排序

搜索依赖于SearchFilter,排序依赖于OrderingFilter

搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
from rest_framework import filters

...

class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页
"""

...
filter_backends = (DjangoFilterBackend, filters.SearchFilter)
filter_class = GoodsFilter
search_fields = ('name', 'goods_brief', 'goods_desc')

结果:

默认情况下,搜索将使用不区分大小写的部分匹配。搜索参数可能包含多个搜索词,它们应该是空格和/或逗号分隔的。如果使用多个搜索条件,则只有在所有提供的条件匹配的情况下,对象才会返回到列表中。

搜索行为可以通过将各种字符预先添加到search_fields来限制。

1
2
3
4
'^'开始 - 搜索。
'='完全匹配。
'@'全文搜索。 (目前只支持Django的MySQL后端。)
'$'正则表达式搜索。

例如:search_fields =('= username','= email')

默认情况下,搜索参数被命名为“搜索”,但是这可能会被SEARCH_PARAM设置覆盖。

排序

简单地配置即可,在filter_backends中加入filters.OrderingFilter,加上ordering_fields = ('sold_num', 'add_time')

效果:

很多功能配置要多看文档。

🐶 ~怕是要给老板下跪了哦~ 🐶