方式一 通过django的view实现商品列表
创建一个项目drf_demo ,省略模型部分
apps: 存放所有app文件
media:存放所有媒体文件
settings.py文件
# 注册app
INSTALLED_APPS = [
......
'users',
'goods',
'trade',
'user_operation',
]
# 修改用户模型为我们的model
AUTH_USER_MODEL = 'users.UserProfile'
# 设置媒体文件存放路径
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
views_base.py
from django.views.generic.base import View,HttpResponse
from .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")
url.py
from django.views.static import serve
from goods.views import GoodsListView
from .settings import MEDIA_ROOT
urlpatterns = [
re_path(r'^media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}), # 显示静态文件(图片、)
path('goods/', GoodsListView.as_view()),
]
问题
无法序列化 DateTimeField
json_dict["add_time"]=good.add_time
返回json数据时,我们把每个字段都从goods中取出来再放入json_dict中,这样不仅麻烦而且容易出错.
改造1 简化model转dict
# 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_dict["add_time"]=good.add_time
# json_list.append(json_dict)
from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
json_list.append(json_dict)
问题
你引入model_to_dict任然无法解决序列化的问题,因此首当其冲的是要解决序列化问题
改造2 序列化数据
1、使用django的serialize
# from django.forms.models import model_to_dict
# for good in goods:
# json_dict = model_to_dict(good)
# json_list.append(json_dict)
import json
from django.core import serializers
json_data=serializers.serialize("json",goods)
from django.http import HttpResponse
return HttpResponse(json_data,content_type="application/json")
2、使用JsonResponse返回数据
import json
from django.core import serializers
json_data=serializers.serialize("json",goods)
json_data=json.loads(json_data) # 为什么要反序列化
from django.http import HttpResponse,JsonResponse
return JsonResponse(json_data,safe=False)
答疑:
JsonResponse它会将数据序列化,因此上方我们需要先反序列化,再使用JsonResponse
# JsonResponse的源代码
data = json.dumps(data, cls=encoder, **json_dumps_params)
3、启动项目 http://127.0.0.1:8000/goods/
看到这里小伙伴会问,既然django内置的serialize已经可以实现上图效果,为什么还要学习DRF(Django REST framework),加大学习成本。
其一 :因为我们的图片放在media目录下,但是json数据返回的却是相对路径,我们返回给其他客户端就需要手动加域名、包名等
其二:不够灵活
方式二 Django REST framework
DRF的官网:https://www.django-rest-framework.org/tutorial/1-serialization/
DRF的前戏
安装依赖包
安装包:
coreapi(1.32.0+)-模式生成支持。
Markdown(3.0.0+)-对可浏览API的Markdown支持。
Pygments(2.4.0+)-在Markdown处理中添加语法突出显示。
django-filter(1.0.1+)-过滤支持。
django-guardian(1.1.1+)-对象级别权限支持。
使用pip install 包名1 包名2 包名3 ......
修改settings.py文件
INSTALLED_APPS = [
...
'rest_framework',
]
Serializer
在goods下面创建serializers.py文件
from rest_framework import serializers
class GoodsSerializer(serializers.Serializer):
name = serializers.CharField(required=True,max_length=100)
click_num = serializers.IntegerField(default=0)
goods_front_image = serializers.ImageField()
修改views.py文件
from django.shortcuts import render
from .models import Goods
from .serializers import GoodsSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class GoodsListView(APIView):
"""
List all goods
""
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
urlpatterns = [
...
path('goods/', GoodsListView.as_view()),
url(r'^api-auth/', include('rest_framework.urls')), # 实现登录的功能,不设置则不能登录
]
启动项目 http://127.0.0.1:8000/goods/
下图我们已经用admin登录了,不在urls.py文件中配置,则没有login选项
ModelSerializer
修改serializers.py文件
# serializers.py
from rest_framework import serializers
from .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()
class GoodsSerializer(serializers.ModelSerializer):
class Meta:
model = Goods
# fields = ["name","click_num","market_price","add_time"]
fields = "__all__" 显示所有字段
启动项目 http://127.0.0.1:8000/goods/
让外键显示完整信息,修改serializers.py文件
# serializers.py
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 = ["name","click_num","market_price","add_time"]
fields = "__all__"
启动项目 http://127.0.0.1:8000/goods/
使用mixins
修改views.py文件
from rest_framework import mixins
from rest_framework import generics
from .models import Goods
from .serializers import GoodsSerializer
class GoodsListView(mixins.ListModelMixin,
generics.GenericAPIView):
"""
商品列表页
"""
# def get(self, request, format=None):
# goods = Goods.objects.all()[:10]
# goods_serializer = GoodsSerializer(goods,many=True)
# return Response(goods_serializer.data)
queryset = Goods.objects.all()[:10]
serializer_class = GoodsSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
这时有的同学会问,这get就这么一句,能不能连着一句也不用写啊,当然有
使用ListAPIView
generics.py文件
# generics.py
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
views.py文件
from rest_framework import mixins
from rest_framework import generics
from .models import Goods
from .serializers import GoodsSerializer
class GoodsListView(generics.ListAPIView):
"""
商品列表页
"""
queryset = Goods.objects.all()[:10]
serializer_class = GoodsSerializer
# 这里连get方法都不用写了
分页
修改views.py文件
# views.py
"""
商品列表页
"""
queryset = Goods.objects.all() # 此处不要切片
serializer_class = GoodsSerializer
修改settings.py文件
# settings.py
REST_FRAMEWORK={
'PAGE_SIZE': 10,
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
}
修改views.py文件
注意:此时可以删除settings.py中的分页参数
# views.py
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
from .models import Goods
from .serializers import GoodsSerializer
class GoodsResultsSetPagination(PageNumberPagination):
page_size = 10 # 每页显示10条数据
page_size_query_param = 'page_size' # 页大小的参数名称
page_query_param = 'p' # 当前页的参数名称
max_page_size = 1000 # 最大页大小
class GoodsListView(generics.ListAPIView):
"""
商品列表页
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsResultsSetPagination
Viewsets
修改views.py文件
# views.py
class GoodsListViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
"""
商品列表页
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsResultsSetPagination
修改urls.py文件
# urls.py
from goods.views import GoodsListViewSet
goods_list = GoodsListViewSet.as_view({
'get': 'list',
})
urlpatterns = [
. . . . . .
path('goods/', goods_list),
]
Routers
修改views.py文件
# views.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
# 配置goods的url
router.register(r'goods', GoodsListViewSet)
# goods_list = GoodsListViewSet.as_view({
# 'get': 'list',
# })
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^media/(?P<path>.*)$',serve,{"document_root":MEDIA_ROOT}),
re_path(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('', include(router.urls)),
]
DRF的过滤
简单过滤
修改views.py文件
# views.py
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
"""
商品列表页
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsResultsSetPagination
def get_queryset(self):
queryset = Goods.objects.all()
price_min=self.request.query_params.get("price_min",0)
if price_min:
queryset=queryset.filter(shop_price__gt=int(price_min))
return queryset
使用DjangoFilterBackend
该django-filter库包含一个DjangoFilterBackend类,该类支持针对REST框架的高度可自定义的字段筛选。
要使用DjangoFilterBackend,请先安装django-filter
。然后添加django_filters到Django的INSTALLED_APPS
pip install django-filter
现在,您应该将过滤器后端添加到您的settings.py中:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
修改views.py文件
# views.py
...... 省略部分代码
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
"""
商品列表页
"""
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsResultsSetPagination
filterset_fields = ['name', 'shop_price']
使用DjangoFilter
地址:https://django-filter.readthedocs.io/en/master/guide/rest_framework.html#quickstart
过滤
创建filters.py文件
# filters.py
from rest_framework import generics
import django_filters
from .models import Goods
class GoodsFilter(django_filters.rest_framework.FilterSet):
"""
商品的过滤类
"""
price_min = django_filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
class Meta:
model = Goods
fields = ['price_min', 'price_max']
修改views.py文件
# views.py
from goods.filters import GoodsFilter
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
...... 省略部分代码
filterset_class = GoodsFilter
搜索
修改views.py文件
# views.py
from goods.filters import GoodsFilter
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
...... 省略部分代码
filter_backends = [filters.SearchFilter]
search_fields = ['name', 'goods_brief']
排序
修改views.py文件
# views.py
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
......省略部分代码
filter_backends = [filters.SearchFilter,filters.OrderingFilter]
ordering_fields = ['name', 'shop_price']