vue+drf打造前后端分离项目(一)之drf的ViewSet、router和跨域

上传的图片资源目录配置:

settings.py:

MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

urls.py:

 # media上传的图片
urlpatterns = [
    re_path(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT})
]
  

 

导入商品种类数据

db_tools/import_category_data.py: 编写脚本导入数据

from db_tools.data.category_data import row_data:db_tools/data/category_data.py 下row_data为数组

__author__ = "westbrook-ding"
__date__ = "2018/10/26 0026 18:53"

# 单独使用django的model
import os
import sys

# os.path.realpath(__file__):获取当前脚本文件的绝对路径
# os.path.dirname(path):文件所在的目录
pwd = os.path.dirname(os.path.realpath(__file__))
# 添加模块搜索目录 djangoShop
sys.path.append(pwd+"../")
# 设置django配置目录  默认会在djangoShop下找
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoShop.settings')


import django
django.setup()

# 引入商品种类class
from goods.models import GoodsCategory
# 引入商品种类数组
from db_tools.data.category_data import row_data

# 保存数据到数据库中
for lev1_cat in row_data:
    lev1_instance = GoodsCategory()
    lev1_instance.code = lev1_cat['code']
    lev1_instance.name = lev1_cat['name']
    lev1_instance.category_type = 1
    lev1_instance.save()

    for lev2_cat in lev1_cat["sub_categorys"]:
        lev2_instance = GoodsCategory()
        lev2_instance.code = lev2_cat['code']
        lev2_instance.name = lev2_cat['name']
        lev2_instance.category_type = 2
        lev2_instance.parent_category = lev1_instance
        lev2_instance.save()

        for lev3_cat in lev2_cat["sub_categorys"]:
            lev3_instance = GoodsCategory()
            lev3_instance.code = lev3_cat['code']
            lev3_instance.name = lev3_cat['name']
            lev3_instance.category_type = 3
            lev3_instance.parent_category = lev2_instance
            lev3_instance.save()

导入商品数据

db_tools/import_good_data.py: 编写脚本导入数据

from db_tools.data.product_data import row_data:db_tools/data/product_data.py 下row_data为数组

__author__ = "westbrook-ding"
__date__ = "2018/10/26 0026 18:53"

# 单独使用django的model
import os
import sys

# os.path.realpath(__file__):获取当前脚本文件的绝对路径
# os.path.dirname(path):文件所在的目录
pwd = os.path.dirname(os.path.realpath(__file__))
# 添加模块搜索目录 djangoShop
sys.path.append(pwd+"../")
# 设置django配置目录  默认会在djangoShop下找
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoShop.settings')


import django
django.setup()

# 引入商品class
from goods.models import Goods, GoodsCategory, GoodsImage
# 引入商品数据数组
from db_tools.data.product_data import row_data

# 保存商品数据
for good_cat in row_data:
    good = Goods()
    good.name = good_cat["name"]
    good.goods_brief = good_cat["desc"] if good_cat["desc"] is not None else ""
    good.goods_desc = good_cat["goods_desc"] if good_cat["goods_desc"] is not None else ""
    good.market_price = float(int(good_cat["market_price"].replace("¥", '').replace("元", "")))
    good.shop_price = float(int(good_cat["sale_price"].replace("¥", '').replace("元", "")))
    good.goods_front_image = good_cat["images"][0]

    category_name = good_cat["categorys"][-1]
    category = GoodsCategory.objects.filter(name=category_name)
    if category is not None:
        good.category = category[0]
    good.save()

    # 商品轮播图
    for good_image in good_cat["images"]:
        good_image_instance = GoodsImage()
        good_image_instance.goods = good
        good_image_instance.image = good_image
        good_image_instance.save()

 

restful api介绍

restful api最佳实践

 

djangorestframework(drf)框架

1.通过django的view实现商品列表页

goods/views_base.py:

__author__ = "westbrook-ding"
__date__ = "2018/10/27 0027 13:51"

from django.views.generic import View
from goods.models import Goods


class GoodslistView(View):
    def get(self, request):
        """
        通过django的view实现商品列表页
        """
        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
        # dumps是将dict转化成str格式,loads是将str转化成dict格式。
        return HttpResponse(json.dumps(json_list), content_type="application/json")

urls.py: 

from django.urls import path, include, re_path
from goods.views_base import GoodslistView
urlpatterns = [
    # 商品列表
    path('goods/', GoodslistView.as_view(), name="goods_list")
]
以上代码可进一步优化如下:
model_to_dict() model转化成字典
 from django.forms.models import model_to_dict
 for good in goods:
     # model_to_dict() model转化成字典
     json_dict = model_to_dict(good)
     json_list.append(json_dict)

model_to_dict() 有些字段无法转换采用model序列化

 from django.core import serializers
 # django models序列化
 json_list = serializers.serialize("json", goods)
 
 import json
 # dumps是将dict转化成str格式,loads是将str转化成dict格式。
 json_list = json.loads(json_list)

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

 return JsonResponse(json_list, safe=False)

 

使用djangorestframework:  

1.安装drf框架

djangorestframework官方文档

pip install djangrestframework

pip install markdown      Markdown对可浏览API的支持。

pip install django-filter    过滤支持。

pip install django-crispy-forms      改进的HTML显示以进行过滤。

pip install coreapi   模式生成支持。

pip install django-guardian      对象级权限支持。

 

2.添加'rest_framework'到您的INSTALLED_APPS设置

3.如果您打算使用可浏览的API,您可能还需要添加REST框架的登录和注销视图。将以下内容添加到根urls.py文件中

re_path(r'^api-auth/', include('rest_framework.urls'))

4.配置自动生成api文档

 urls.py:

# 引入drf自动生成文档的方法
from rest_framework.documentation import include_docs_urls
urlpatterns = [
    path('doc/', include_docs_urls(title="生鲜电商api文档"))
]

 5.drf用apiview来实现商品列表:

  a.  goods应用下 serializers.py:

# 引入 rest_framework的serializers 序列化
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()

b.  goods应用下 views.py:

# 引入自定义的序列化方法
from .serializers import GoodsSerializer

# APIView继承的是django的View
from rest_framework.views import APIView
from rest_framework.response import Response

from goods.models import Goods


# Create your views here.
class GoodslistView(APIView):
    """
    商品列表
    """
    def get(self, requset, format=None):
        goods = Goods.objects.all()[:10]
        # many = True 序列化成数组对象
        goods_json = GoodsSerializer(goods, many=True)
        return Response(goods_json.data)

c.  urls.py:

from django.urls import path, include, re_path

from goods.views import GoodslistView
# 引入drf自动生成文档的方法
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    # 商品列表
    path('goods/', GoodslistView.as_view(), name="goods_list"),

    path('doc/', include_docs_urls(title="生鲜api文档")),

    re_path(r'^api-auth/', include('rest_framework.urls'))
]

通过 http://127.0.0.1:8000/goods 访问goods api 得到如下数据: drf 的序列化帮我们图片目录添加的/media 前缀(django没有做这一步)

6.drf的modelserializer实现商品列表功能:

goods下serializers.py:

# 引入 rest_framework的serializers 序列化
from rest_framework import serializers
from goods.models import Goods, GoodsCategory

# ModelSerializer
class GoodsCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        # __all__ 序列化所有字段
        fields = "__all__"


class GoodsSerializer(serializers.ModelSerializer):
    # 将外键的id也序列化出来 覆盖category字段
    category = GoodsCategorySerializer()

    class Meta:
        model = Goods
        # __all__ 序列化所有字段   ["name","click_num"]:筛选字段   外键会默认序列成id
        fields = "__all__"

views.py、urls.py写法同5.drf用apiview来实现商品列表

 

7.GenericView方式实现商品列表页和分页

goods应用下的views.py:

# 引入自定义的序列化方法
from .serializers import GoodsSerializer

# 引入drf的generics和PageNumberPagination
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination

from goods.models import Goods

# 自定义商品列表分页
class GoodsPagination(PageNumberPagination):
    page_size = 10   # 每页10条
    page_size_query_param = 'page_size'
    page_query_param = 'p'  # 指定页码参数为p 默认为page
    max_page_size = 100


# 查看源码 generics.ListAPIView 继承了 mixins.ListModelMixin,和GenericAPIView,而GenericAPIView继承了views.APIView
class GoodslistView(generics.ListAPIView):
    """
     商品列表页
    """
    queryset = Goods.objects.all()  # 查询商品列表
    serializer_class = GoodsSerializer   # 序列化的类
    pagination_class = GoodsPagination  # 配置分页

goods下serializers.py和  urls.py写法同 6.drf的modelserializer实现商品列表功能:

 

8.viewsets和router实现商品列表页

goods下的views.py:

# 引入自定义的序列化方法
from .serializers import GoodsSerializer
# 引入drf mixins、viewsets 、PageNumberPagination
from rest_framework.pagination import PageNumberPagination
from rest_framework import mixins
from rest_framework import viewsets

from goods.models import Goods

# 自定义商品列表分页
class GoodsPagination(PageNumberPagination):
    page_size = 10   # 每页10条
    page_size_query_param = 'page_size'
    page_query_param = 'p'  # 指定页码参数为p 默认为page
    max_page_size = 100

# viewsets.GenericViewSet 继承了ViewSetMixin, generics.GenericAPIView
# ViewSetMixin 重写了as_view方法 使其可以使用router 
class GoodslistViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all()  # 查询商品列表页
    serializer_class = GoodsSerializer  # 序列化类
    pagination_class = GoodsPagination  # 配置分页



urls.py:

from django.urls import path, include, re_path

# 引入drf自动生成文档的方法
from rest_framework.documentation import include_docs_urls

# 引入drf路由
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

from goods.views import GoodslistViewSet

# 商品列表router
router.register(r'goods', GoodslistViewSet)


urlpatterns = [
    # router
    path('', include(router.urls)),

    path('doc/', include_docs_urls(title="生鲜api文档")),

    re_path(r'^api-auth/', include('rest_framework.urls'))
]

goods下的serializers.py同7.GenericView方式实现商品列表页和分页

 

9.drf的ApiView、GenericView、ViewSet和router分析

GenericViewSet(ViewSet)   -drf  可使用router、动态序列化、minxin
   GennericApiView        -drf  可使用mixin 分页等
      ApiView              -drf  
        View               -django

minxin
    CreateModelMixin
    ListModelMixin
    UpdateModelMixin
    RetrieveModelMixin
    DestoryModelMixin
    

 

10. drf的过滤(django_filter)

goods下的filter.py:

__author__ = "westbrook-ding"

import django_filters
from .models import Goods


# 自定义商品过滤器
class GoodsFilter(django_filters.rest_framework.FilterSet):
    """
    商品本店价格区间
    """
    # gte:>=    lte:<=
    price__min = django_filters.NumberFilter(field_name='shop_price', lookup_expr='gte')
    price__max = django_filters.NumberFilter(field_name='shop_price', lookup_expr='lte')
    # 模糊查询 contains  i:忽略大小写 不加lookup_expr="icontains" 表示全部匹配
    name = django_filters.CharFilter(field_name='name', lookup_expr="icontains")

    class Meta:
        model = Goods
        fields = ['price__min', 'price__max', 'name']

goods下的views.py:

from django_filters.rest_framework import DjangoFilterBackend
from goods.filter import GoodsFilter

class GoodslistViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer  # 序列化类
    pagination_class = GoodsPagination  # 配置分页

    # 过滤
    filter_backends = (DjangoFilterBackend,)
    # filter_fields = ('name', 'shop_price')  # 必需全部匹配
    filter_class = GoodsFilter

 

11.drf的搜索和排序

SearchFilter搜索  OrderingFilter排序

 goods下的views.py:

# 引入drf filters
from rest_framework import filters

from goods.models import Goods

class GoodslistViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer  # 序列化类
    pagination_class = GoodsPagination  # 配置分页
    
    # 搜索: filters.SearchFilter和search_field  
    # 排序: filters.OrderingFilter 和ordering_fields
    filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)

    # 搜索
    # search_fields 满足元组中所选字段的任意条件均会被筛选出来
    # '^'开始 - 搜索。
    # '='完全匹配。
    # '@'全文搜索。(目前只支持Django的MySQL后端。)
    # '$'正则表达式搜索。
    search_fields = ('^name', 'goods_brief', 'goods_desc')
    # 按字段排序
    ordering_fields = ('sold_num', 'add_time')

 

12.商品分类数据接口(列表和详情)

goods下serializers.py:(多级分类序列化)

# 引入 rest_framework的serializers 序列化
from rest_framework import serializers
from goods.models import GoodsCategory

# ModelSerializer
# 三级大类序列化
class GoodsCategorySerializer3(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        # __all__ 序列化所有字段
        fields = "__all__"


# 二级大类序列化
class GoodsCategorySerializer2(serializers.ModelSerializer):
    # many = True: 子类有多个
    sub_cat = GoodsCategorySerializer3(many=True)

    class Meta:
        model = GoodsCategory
        # __all__ 序列化所有字段
        fields = "__all__"


# 一级商品大类序列化
class GoodsCategorySerializer(serializers.ModelSerializer):
    # many = True: 子类有多个 通过models.py 中class GoodsCategory 字段属性 related_name="sub_cat" 关联起来
    sub_cat = GoodsCategorySerializer2(many=True)

    class Meta:
        model = GoodsCategory
        # __all__ 序列化所有字段
        fields = "__all__"


goods下views.py: (mixins.ListModelMixin:列表,mixins.RetrieveModelMixin:单个商品分类详情,通过id查询)

from rest_framework import mixins
from rest_framework import viewsets

from goods.models import GoodsCategory

# 引入自定义的序列化方法
from .serializers import GoodsCategorySerializer

class CategoryListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    """
    商品分类列表 、单个分类详情(mixins.RetrieveModelMixin:请求示例id=1:     
    http://127.0.0.1:8000/category/1/)
    """
    # 获取一级大类
    queryset = GoodsCategory.objects.filter(category_type=1)
    serializer_class = GoodsCategorySerializer

urls.py:

# 商品分类列表router
router.register(r'category', CategoryListViewSet)

13.django解决跨域问题:  django-cors-headers

django-cors-headers github地址

 a.安装 pip install django-cors-headers

 b. settings.py 配置如下

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)
# 置于 django.middleware.csrf.CsrfViewMiddleware 之前
MIDDLEWARE = [  
    ...
    'corsheaders.middleware.CorsMiddleware',  
] 
CORS_ORIGIN_ALLOW_ALL = True

c . 重新运行项目

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章