第四课 Vue+Django rest framework生鲜项目资源商品列表页
第一节 django view实现商品列表页
1.1 实现商品列表页
- 进阶请参考django官方文档:https://docs.djangoproject.com/zh-hans/3.1/
- 谷歌插件JSONView用来解析json数据: 按下F5
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 = []
goods = Goods.objects.all()[:10]
for good in goods:
json_dict = {}
json_dict["name"] = good.name
json_dict["category"] = good.category.name
json_dict["market_price"] = good.market_price
json_list.append(json_dict)
from django.http import HttpResponse
import json
return HttpResponse(json.dumps(json_list), content_type="application/json")
- 路由配置
from django.urls import path, re_path
import xadmin
from MxShop.settings import MEDIA_ROOT
from django.views.static import serve
from goods.views_base import GoodsListView
urlpatterns = [
path('xadmin/', xadmin.site.urls),
re_path('media/(?P<path>.*)', serve, {'document_root': MEDIA_ROOT}, name='media'),
path(r'goods/', GoodsListView.as_view(), name="goods-list")
]
- 访问: http://127.0.0.1:8000/goods/
- 这样我们就通过重载django的View方式完成了列表的返回。那为什么还要使用rest framework框架呢?
- 第一:一个个字段填充,工作量大,易出错。
- 第二: 序列化不能序列datetime类型的字段。
- 当然还有很多其他问题。。。
1.2 实现商品列表页优化
- 针对上面第一个问题优化:一个个字段填充,工作量大,易出错。
- from django.forms.models import model_to_dict
- 这里依然解决不了,datetime和ImageFieldFile字段序列化的问题。
from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
json_list.append(json_dict)
- 同时解决上面第一个和第二个问题。
- 使用from django.core import serializers 去系列化
from django.core import serializers
json_data = serializers.serialize("json", goods)
from django.http import JsonResponse
import json
json_data = json.loads(json_data)
return JsonResponse(json_data, safe=False)
- 那为什么还要使用rest framework框架呢?
- 返回给手机端会在图片路径前加很多东西,rest framework会帮我们加好
- 返回字段的排列顺序定死了,想修改的话需要后台修改结果。还有很多不方便地方rest framework会帮我们做好
- rest framework会帮我们自动生成文档
- rest framework会帮我们输入检测,比如form表单还是请求体中的数据。
第二节 django-rest-framework实现商品列表页
2.1 Serializer初体验
- django-rest-framework简称drf
- django-rest-framework官方文档:https://www.django-rest-framework.org/
- 根据文档先安装依赖包:
- pip install PyYAML -i https://pypi.douban.com/simple
- pip install uritemplate -i https://pypi.douban.com/simple
- pip install Pygments -i https://pypi.douban.com/simple
- pip install django-guardian -i https://pypi.douban.com/simple
- django-rest-framework导入, 这里和课程中不同。按照官方文档来:
from django.urls import path, re_path, include
import xadmin
import rest_framework
from MxShop.settings import MEDIA_ROOT
from django.views.static import serve
from goods.views import GoodsListView
from rest_framework.schemas import get_schema_view
urlpatterns = [
path('xadmin/', xadmin.site.urls),
path('api-auth/', include("rest_framework.urls")),
re_path('media/(?P<path>.*)', serve, {'document_root': MEDIA_ROOT}, name='media'),
path(r'goods/', GoodsListView.as_view(), name="goods-list"),
path('openapi', get_schema_view(title="生鲜电商"), name='openapi-schema'),
]
- 不能忘记 配置到 INSTALLED_APPS 中。 ‘rest_framework’
- 我们尝试写一个class-based-views: https://www.django-rest-framework.org/tutorial/3-class-based-views/
from goods.serializer import GoodsSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Goods
from rest_framework import status
class GoodsListView(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
goods = Goods.objects.all()[:10]
goods_json = GoodsSerializer(goods, many=True)
return Response(goods_json.data)
def post(self, request, format=None):
serializer = GoodsSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- goods应用下新建serializer.py文件, 只选两个字段进行测试。
from rest_framework import serializers
from goods.models import Goods
class GoodsSerializer(serializers.Serializer):
name = serializers.CharField(required=True, max_length=100)
click_num = serializers.IntegerField(default=0)
goods_front_image = serializers.ImageField()
def create(self, validated_data):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
return Goods.objects.create(**validated_data)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2f8f7bc961b190e7611dd82a97905308.png#pic_center)
2.2 ModelSerializer简化serializers
- drf中的ModelSerializer和之前的Modelform一样,可以简化字段的书写。
- 外键中的数据也可以直接覆盖读取。
from rest_framework import serializers
from goods.models import Goods, GoodsCategory
class GoodsCategorySerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = "__all__"
class GoodsSerializer(serializers.ModelSerializer):
category = GoodsCategorySerializer()
class Meta:
model = Goods
fields = "__all__"
2.3 mixins简化views 并分页
- 先导入from rest_framework import mixins 和 from rest_framework import generics
- 方法一:可以继承mixins.ListModelMixin, generics.GenericAPIView,重写get函数
- 方法二:直接继承generics.ListAPIView
from goods.serializer import GoodsSerializer
from rest_framework import mixins
from rest_framework import generics
from rest_framework.response import Response
from .models import Goods
class GoodsListView(generics.ListAPIView):
"""
商品列表页展示
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
- 通过settings.py 配置就可以完成分页。这里查看数据有一点改变,多了俩个链接,之前的数据放到results字段下。, 这些事是generics.GenericAPIView类帮我们完成的。
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 10,
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/73da8d6aebfb24c213b410b6e86618d9.png#pic_center)
- 定制分页的样式。 这些可以看官方文档说明。这里如果定义了,settings.py中的分页设置会覆盖掉。
from rest_framework.pagination import PageNumberPagination
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
2.4 viewsets和router配合
- 后续课程很多地方都是使用这种方法。
- 它可以帮我们做很多事情。
from goods.serializer import GoodsSerializer
from rest_framework import generics
from .models import Goods
from rest_framework.pagination import PageNumberPagination
from rest_framework import viewsets, mixins
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
- router配置。
from goods.views import GoodsListViewset
goods_list = GoodsListViewset.as_view({
'get': 'list'
})
path(r'goods/', goods_list, name="goods-list"),
- drf 自动router 配置。
- https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#using-routers
from goods.views import GoodsListViewset
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'goods', GoodsListViewset)
path('', include(router.urls)),
第三节 drf细节探讨
2.1 drf的Apiview、GenericView、Viewset和router的原理分析
- GenericviewSet (viewset) 属于 drf
- 同时继承ViewSetMixin, 允许不需要通过函数绑定 可以通过url配置时进行绑定
- 里面有很多action。动态serializer有很多用处。
- GenericAPIView 属于 drf
- 不同过滤,分页,serializer序列化的逻辑
- 这一层级有很多混合的View。定制组合通过函数进行绑定。
- APIView 属于 drf
- view 属于django
- 上面的差异几乎都是通过mixin区别的。
- CreateModelMixin
- ListModelMixin
- UpdateModelMixin
- RetrieveModelMixin
- DestoryModelMixin
2.2 drf的过滤功能
- 直接调用get_queryset,加入自己逻辑过滤
class GoodsListViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页展示
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
def get_queryset(self):
queryset = Goods.objects.all()
price_min = self.request.query_params.get("price_min", 0)
if price_min:
queryset = Goods.objects.filter(shop_price__gt=int(price_min))
return queryset
- 通过django_filters过滤。:https://www.django-rest-framework.org/api-guide/filtering/
- 先在settings.py中注册进来 ‘django_filters’,
- 这里是准确查询,name值要相等才可以,shop_price也要相等。
from django_filters.rest_framework import DjangoFilterBackend
class GoodsListViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页展示
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'shop_price']
2.3 django-filter的范围查询和模糊查询
- 这里我们可以了解下django-filter的官网。https://django-filter.readthedocs.io/en/stable/
- 自己写一个filiters.py。我们也可以自定自己过滤方法。。。看文档
from django_filters import rest_framework as filters
from .models import Goods
class GoodsFilter(filters.FilterSet):
"""
商品的过滤类
"""
min_price = filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
max_price = filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
name = filters.CharFilter(field_name="name", lookup_expr='icontains')
class Meta:
model = Goods
fields = ['min_price', 'max_price', 'name']
- views.py修改。这里注意 filterset_class = GoodsFilter
from .filters import GoodsFilter
class GoodsListViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页展示
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = [DjangoFilterBackend]
filterset_class = GoodsFilter
2.4 drf 的SearchFilter搜索和OrderingFilter排序
- 这里注意下这个SearchFilter是drf的,上面的filiter都是django的。
- OrderingFilter排序,也是drf框架中的。
from rest_framework import filters
class GoodsListViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
商品列表页,分页,搜索,过滤,排序
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = GoodsFilter
search_fields = ['^name', 'goods_brief', 'goods_desc']
ordering_fields = ['sold_num', 'add_time']