2.使用modelserializer实现商品序列化
从前面的基本使用中可以看到,serializer类似于Django自带的Form,可以对表单进行验证,但是serializer还拥有更多的功能,这里尝试通过serializer将数据保存到数据库中。
在serializers.py中实现用于保存数据的create()
方法如下:
from rest_framework import serializers from .models import Goods class GoodsSerializer(serializers.Serializer): name = serializers.CharField(required=True, max_length=300) click_num = serializers.IntegerField(default=0) goods_front_image = serializers.ImageField() def create(self, validated_data): '''接受前端的数据并保存''' return Goods.objects.create(**validated_data)
views.py中实现用于提交数据的post方法如下:
from rest_framework import status from rest_framework.views import APIView from rest_framework.response import Response from .models import Goods from .serializers import GoodsSerializer # 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) def post(self, request, format=None): serializer = GoodsSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
当然,添加商品数据一般是后台管理员的操作,前台用户是没有这个权限的。
Django中有Form,也有ModelForm,DRF中也有ModelSerializer,相比于Serializer,它省去了模型所有字段的添加和处理数据方法的实现,serializers.py简化如下:
from rest_framework import serializers from .models import Goods class GoodsSerializer(serializers.ModelSerializer): class Meta: model = Goods fields = ['category', 'name', 'sold_num', 'shop_price', 'goods_brief', 'goods_front_image', 'is_hot']
由ModelSerializer自动完成与模型中相应字段的映射,显然,简洁很多,此时再访问http://127.0.0.1:8000/goods/如下:
显然,此时将指定字段返回前端,还可以指定fields = '__all__'
来序列化所有字段,如下:
from rest_framework import serializers from .models import Goods class GoodsSerializer(serializers.ModelSerializer): class Meta: model = Goods fields = '__all__'
再次访问,显示:
显然,序列化了所有字段,并且没有出错。
此时对于商品信息,category显示的时对应GoodsCategory模型的主键,当然还可以显示Category的具体信息,此时需要使用嵌套序列化,如下:
from rest_framework import serializers from .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__'
即用自定义字段覆盖原有的category字段,显示如下:
此时已经显示出category的具体信息。
3.GenericView方式实现商品列表页和分页
现在进一步使用mixins和GenericView让代码更加简洁。
views.py如下:
from rest_framework import mixins, generics from rest_framework.response import Response from .models import Goods from .serializers import GoodsSerializer # 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)
访问http://127.0.0.1:8000/goods/,具有与之前一样的效果,代码还可以进一步简化:
from rest_framework import mixins, generics from rest_framework.response import Response from .models import Goods from .serializers import GoodsSerializer # Create your views here. class GoodsListView(generics.ListAPIView): '''商品列表页''' queryset = Goods.objects.all()[:10] serializer_class = GoodsSerializer
还可以进行分页,只需要在settings.py中进行配置即可:
# DRF配置 REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, }
显示:
显然,此时已经实现分页的效果,并且比之前现实的信息更多,包括了总数、当前页的上一页、下一页等信息,并且图片地址也添加了域名、成为可访问的完整路径。
除了使用默认分页,还可以进行个性化定制,views.py如下
from rest_framework import generics from rest_framework.pagination import PageNumberPagination from .models import Goods from .serializers import GoodsSerializer # 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
此时可以注释掉settings.py中关于REST_FRAMEWORK的配置,再次访问如下:
此时表示页数的参数变为了指定的p,并且可以指定page_size参数,超过范围会提示页面无效。
4.viewsets和router实现商品列表页
viewsets中含有很多常见的视图,可以让代码变得更加简洁高效。
使用GenericViewSet如下:
from rest_framework import mixins, viewsets from rest_framework.pagination import PageNumberPagination from .models import Goods from .serializers import GoodsSerializer # 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
urls.py进行路由配置如下:
from django.conf.urls import url, include from django.views.static import serve from rest_framework.documentation import include_docs_urls import xadmin from .settings import MEDIA_ROOT from goods.views import GoodsListViewSet goods_list = GoodsListViewSet.as_view({ 'get': 'list', }) urlpatterns = [ url(r'^xadmin/', xadmin.site.urls), url(r'^media/(?P<path>.*)$', serve, {'document_root':MEDIA_ROOT}), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), # 商品列表页 url(r'goods/$', goods_list, name='goods-list'), # 文档路由 url(r'docs/', include_docs_urls(title='生鲜电商')) ]
使get()
请求绑定到list()
方法,此时访问http://127.0.0.1:8000/goods/效果与之前相同,还可以使用router自动完成配置进一步简化,urls.py如下:
from django.conf.urls import url, include from django.views.static import serve from rest_framework.documentation import include_docs_urls from rest_framework.routers import DefaultRouter import xadmin from .settings import MEDIA_ROOT from goods.views import GoodsListViewSet # Create a router and register our viewsets with it. router = DefaultRouter() # 配置goods的路由 router.register(r'goods', GoodsListViewSet) urlpatterns = [ url(r'^xadmin/', xadmin.site.urls), url(r'^media/(?P<path>.*)$', serve, {'document_root':MEDIA_ROOT}), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), # 商品列表页 url(r'^', include(router.urls)), # 文档路由 url(r'docs/', include_docs_urls(title='生鲜电商')) ]
router执行rigister()方法会自动进行配置,将get()请求转到list()方法、post()请求转到create()方法,还执行一些其他默认操作,以后添加其他模型的视图也直接添加一行代码router.register(r'xxx', XxxListViewSet)即可。
访问效果与之前相同。
5.各种View的分析
前面用到过不同的View来实现商品列表页,现在结合源码进一步分析。
GenericViewSet(viewsets)是最高一层的View,继承自GenericAPIView,而GenericAPIView又继承自APIView,APIView又继承自View,前3个均属于DRF,View属于Django。
它们的主要差异在于mixins,包括CreateModelMixin、ListModelMixin、RetrieveModelMixin、UpdateModelMixin和DestroyModelMixin等,ListModelMixin中含有list()方法。
GenericAPIView继承自APIView,在APIView的基础上实现过滤、分页等功能;
GenericAPIView结合mixins形成各种APIView,如CreateAPIView、ListAPIView、ListCreateAPIView、RetrieveUpdateAPIView等;
GenericViewSet继承自ViewSetMixin和GenericAPIView,ViewSetMixin允许且要求在urls.py中通过router进行方法的绑定或者自定义绑定,viewsets还实现了initialize_request(request, *args, **kwargs)方法,绑定了很多action,有很多好处,同时还实现了一些组合,包括ReadOnlyModelViewSet、ModelViewSet等。