一、知足多端適配前端
隨着移動端的興起,如今公司產品不僅限於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)