一、知足多端适配前端
随着移动端的兴起,如今公司产品不仅限于pc端的,包括Android,IOS。python
按照之前的方式,咱们后端其实就要有多套,pc一套,APP端两套。开发成本以及开发效率会很低,若是先后端分离,咱们后端只须要有一套就能够了~数据库
后端只提供接口,前端不论是pc仍是APP均可以去调用数据。django
二、先后端职责划分编程
之前的编程方式,先后端职责不清晰,模板语言前端后端均可以写。后端
三、开发效率api
先后端互相等待。跨域
四、解放前端能力session
前端配合后端,只写模板语言,能力受限。cors
五、后端语言和模板语言解耦
后端开发语言与模板语言耦合度较高,依赖开发语言,更换后端语言的成本很高。
(1)项目urls.py修改以下:
from django.conf.urls import url, include urlpatterns = [ # path('admin/', admin.site.urls), url(r'^api/', include('api.urls')), ]
(2)应用目录下建立urls.py文件,配置以下:
from django.conf.urls import url, include from api.views import course urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view()), ]
(3)修改/api/views/course.py类视图文件以下所示:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning class CourseView(APIView): versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): print(request.version) return Response('...')
(4)访问显示效果
建立/api/cors.py,代码以下所示:
from django.middleware.common import CommonMiddleware # 经过它找到要引入的模块 from django.utils.deprecation import MiddlewareMixin class CORSMiddleware(MiddlewareMixin): """自定义中间件""" def process_response(self, request, response): # 添加响应头 # 容许你的域名来获取个人数据 response['Access-Control-Allow-Origin'] = "*" # 容许你携带Content-Type请求头,这里不能写* response['Access-Control-Allow-Headers'] = "Content-Type" # 容许你发送GET/POST/DELETE/PUT response['Access-Control-Allow-Methods'] = "GET, POST" return response
修改settings.py,添加中间件:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'api.cors.CORSMiddleware' ]
from django.db import models # Create your models here. class Course(models.Model): """课程表""" title = models.CharField(verbose_name='课程名称', max_length=32) course_img = models.CharField(verbose_name="课程图片", max_length=64) level_choices = ( (1, "初级"), (2, "中级"), (3, "高级"), ) level = models.IntegerField(verbose_name="课程难易程度", choices=level_choices, default=1) def __str__(self): return self.title class CourseDetail(models.Model): """课程详细表""" course = models.OneToOneField(to='Course', on_delete=models.CASCADE) slogon = models.CharField(verbose_name="口号", max_length=255) why = models.CharField(verbose_name="为何要学习?", max_length=255) recommend_courses = models.ManyToManyField(verbose_name="推荐课程", to="Course", related_name="rc") def __str__(self): return "课程详细:" + self.course.title class Chapter(models.Model): """章节""" num = models.IntegerField(verbose_name="章节") name = models.CharField(verbose_name="章节名称", max_length=32) course = models.ForeignKey(verbose_name="所属课程", to="Course", on_delete=models.CASCADE) def __str__(self): return "课程章节" + self.name
执行数据迁移操做。
from django.contrib import admin from api import models # Register your models here. """ root/root!2345 """ admin.site.register(models.Course) admin.site.register(models.CourseDetail) admin.site.register(models.Chapter)
基于rest-framework实现查询课程查询接口。
api/urls.py:
from django.conf.urls import url, include from api.views import course urlpatterns = [ # 方式一: url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view()), url(r'^(?P<version>[v1|v2]+)/course/(?P<pk>\d+)/$', course.CourseView.as_view()), ]
api/views/course.py:
from rest_framework.views import APIView from rest_framework.response import Response from api import models from rest_framework import serializers class CourseSerializer(serializers.ModelSerializer): """对django model 的实例进行序列化""" class Meta: # 帮忙转换没有本身写的字段 model = models.Course fields = "__all__" class CourseView(APIView): def get(self, request, *args, **kwargs): """ 查看全部的课程:http://127.0.0.1:8000/api/v1/course/ 查看某一课程:http://127.0.0.1:8000/api/v1/course/1 """ ret = {'code': 1000, 'data': None} try: pk = kwargs.get('pk') if pk: # 若是pk有值 obj = models.Course.objects.filter(id=pk).first() ser = CourseSerializer(instance=obj, many=False) else: queryset = models.Course.objects.all() # QuerySet里面是一个个对象 ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret)
显示效果:
这种方法虽然能够实现可是若是代码不少时,就看起来很不简洁了。若是能交给不一样的方法来执行就比较好了。
api/urls.py:
from django.conf.urls import url, include from api.views import course urlpatterns = [ # 方式二:前提是要重写as_view url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view({'get': 'list'})), url(r'^(?P<version>[v1|v2]+)/course/(?P<pk>\d+)/$', course.CourseView.as_view({'get': 'retrieve'})), ]
api/views/course.py改写以下:
from rest_framework.viewsets import ViewSetMixin class CourseView(ViewSetMixin, APIView): def list(self, request, *args, **kwargs): """ 课程列表接口 :param request: :param args: :param kwargs: :return: """ ret = {'code': 1000, 'data': None} try: queryset = models.Course.objects.all() # QuerySet里面是一个个对象 ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 True:queryset ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret) def retrieve(self, request, *args, **kwargs): """ 课程详细接口 :param request: :param args: :param kwargs: :return: """ ret = {'code': 1000, 'data': None} try: pk = kwargs.get('pk') obj = models.Course.objects.filter(id=pk).first() ser = CourseSerializer(instance=obj, many=False) # many描述是model对象仍是QuerySet False:对象 ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret)
注意:many描述是model对象仍是QuerySet,当many=True时,描述是QuerySet;当many=False时,描述是model对象。
course.py调整以下:
from rest_framework.views import APIView from rest_framework.response import Response from api import models from rest_framework import serializers class CourseSerializer(serializers.ModelSerializer): """对django model 的实例进行序列化""" class Meta: # 帮忙转换没有本身写的字段 model = models.Course fields = "__all__" class CourseDetailSerializer(serializers.ModelSerializer): """课程详细序列化""" class Meta: model = models.CourseDetail fields = "__all__" depth = 1 # 0-10之间,0是帮忙找一层(当前表关联的表)的数据,1是找两层(再往下找一层关联表)的数据 from rest_framework.viewsets import ViewSetMixin class CourseView(ViewSetMixin, APIView): def list(self, request, *args, **kwargs): """ 课程列表接口 :param request: :param args: :param kwargs: :return: """ ret = {'code': 1000, 'data': None} try: queryset = models.Course.objects.all() # QuerySet里面是一个个对象 ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 True:queryset ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret) def retrieve(self, request, *args, **kwargs): """ 课程详细接口 :param request: :param args: :param kwargs: :return: """ ret = {'code': 1000, 'data': None} try: pk = kwargs.get('pk') # 课程id # 课程详细对象 obj = models.CourseDetail.objects.filter(course_id=pk).first() ser = CourseDetailSerializer(instance=obj, many=False) ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret)
主要是调整了retrieve方法,增长了CourseDetailSerializer来处理详细信息序列化。
注意:这里配置depth = 1 官方推荐是:配置0-10之间,0是帮忙找一层(当前表关联的表)的数据,1是找两层(再往下找一层关联表)的数据。
显示效果:
虽然这种方法能够实现效果,可是它每每给的数据过多了。
对CourseDetailSerializer作了以下修改:
class CourseDetailSerializer(serializers.ModelSerializer): """课程详细序列化""" # 自定义字段 serializers默认对model对象作序列化 title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询 img = serializers.CharField(source="course.course_img") # level = serializers.CharField(source="course.level") # 这个只是拿到了数字 level = serializers.CharField(source="course.get_level_display") class Meta: model = models.CourseDetail fields = ["course", "title", "img", "level", "slogon", "why"]
注意source的用法,且get_字段名_display()能够用来获取对应字段的值
显示效果:
source用来解决一对1、外键、choice的跨表查询。可是遇到多对多就很差用了。
class CourseDetailSerializer(serializers.ModelSerializer): """课程详细序列化""" # 自定义字段 serializers默认对model对象作序列化 title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询 img = serializers.CharField(source="course.course_img") # level = serializers.CharField(source="course.level") # 这个只是拿到了数字 level = serializers.CharField(source="course.get_level_display") # 针对多对多字段使用SerializerMethodField recommends = serializers.SerializerMethodField() # 取get_recommends(obj)的返回值 class Meta: model = models.CourseDetail fields = ["course", "title", "img", "level", "slogon", "why", "recommends"] def get_recommends(self, obj): # 注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。 # 获取推荐的全部课程 queryset = obj.recommend_courses.all() return [{'id': row.id, 'title': row.title} for row in queryset]
注意多对多的字段使用SerializerMethodField,recommends取的是get_recommends函数的返回值。
注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。显示效果以下:
修改CourseSerializer实现对课程列表序列化修改:
class CourseSerializer(serializers.ModelSerializer): """对django model 的实例进行序列化""" # 自定义字段 level = serializers.CharField(source="get_level_display") class Meta: # 帮忙转换没有本身写的字段 model = models.Course fields = ["id", "title", "course_img", "level"]
显示效果:
修改CourseDetailSerializer以下所示:
class CourseDetailSerializer(serializers.ModelSerializer): """课程详细序列化""" # 自定义字段 serializers默认对model对象作序列化 title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询 img = serializers.CharField(source="course.course_img") # level = serializers.CharField(source="course.level") # 这个只是拿到了数字 level = serializers.CharField(source="course.get_level_display") # 针对多对多字段使用SerializerMethodField recommends = serializers.SerializerMethodField() # 取get_recommends(obj)的返回值 chapter = serializers.SerializerMethodField() class Meta: model = models.CourseDetail fields = ["course", "title", "img", "level", "slogon", "why", "recommends", "chapter"] def get_recommends(self, obj): # 注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。 # 获取推荐的全部课程 queryset = obj.recommend_courses.all() return [{'id': row.id, 'title': row.title} for row in queryset] def get_chapter(self, obj): # obj是课程详细的对象 queryset = obj.course.chapter_set.all() # course.chapter_set反向查找,取到全部的章节 return [{'id': row.id, 'name': row.name} for row in queryset]
Django 中的一对多关系用 ForeignKey 来实现,一对多的反向查询是经过:按表名小写_set.all() 来实现的。
显示效果以下所示:
建立/api/serializers/course.py文件夹和文件,将序列化相关内容迁移过来,以下所示:
from api import models from rest_framework import serializers class CourseSerializer(serializers.ModelSerializer): """课程序列化""" # 自定义字段 level = serializers.CharField(source="get_level_display") class Meta: # 帮忙转换没有本身写的字段 model = models.Course fields = ["id", "title", "course_img", "level"] class CourseDetailSerializer(serializers.ModelSerializer): """课程详细序列化""" # 自定义字段 serializers默认对model对象作序列化 title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询 img = serializers.CharField(source="course.course_img") # level = serializers.CharField(source="course.level") # 这个只是拿到了数字 level = serializers.CharField(source="course.get_level_display") # 针对多对多字段使用SerializerMethodField recommends = serializers.SerializerMethodField() # 取get_recommends(obj)的返回值 chapter = serializers.SerializerMethodField() class Meta: model = models.CourseDetail fields = ["course", "title", "img", "level", "slogon", "why", "recommends", "chapter"] def get_recommends(self, obj): # 注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。 # 获取推荐的全部课程 queryset = obj.recommend_courses.all() return [{'id': row.id, 'title': row.title} for row in queryset] def get_chapter(self, obj): # obj是课程详细的对象 queryset = obj.course.chapter_set.all() # course.chapter_set反向查找,取到全部的章节 return [{'id': row.id, 'name': row.name} for row in queryset]
在view/course.py中引入序列化组件:
from rest_framework.views import APIView from rest_framework.response import Response from api import models from api.serializers.course import CourseDetailSerializer, CourseSerializer from rest_framework.viewsets import ViewSetMixin class CourseView(ViewSetMixin, APIView): def list(self, request, *args, **kwargs): """ 课程列表接口 :param request: :param args: :param kwargs: :return: """ ret = {'code': 1000, 'data': None} try: queryset = models.Course.objects.all() # QuerySet里面是一个个对象 ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 True:queryset ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret) def retrieve(self, request, *args, **kwargs): """ 课程详细接口 :param request: :param args: :param kwargs: :return: """ ret = {'code': 1000, 'data': None} try: pk = kwargs.get('pk') # 课程id # 课程详细对象 obj = models.CourseDetail.objects.filter(course_id=pk).first() ser = CourseDetailSerializer(instance=obj, many=False) ret['data'] = ser.data except Exception as e: ret['code'] = 1001 ret['error'] = "获取课程失败" return Response(ret)