vue+rest-framework先後端分離整合

1、爲何要作先後端分離項目

一、知足多端適配前端

  隨着移動端的興起,如今公司產品不僅限於pc端的,包括Android,IOS。python

  按照之前的方式,咱們後端其實就要有多套,pc一套,APP端兩套。開發成本以及開發效率會很低,若是先後端分離,咱們後端只須要有一套就能夠了~數據庫

  後端只提供接口,前端不論是pc仍是APP均可以去調用數據。django

二、先後端職責劃分編程

  之前的編程方式,先後端職責不清晰,模板語言前端後端均可以寫。後端

三、開發效率api

  先後端互相等待。跨域

四、解放前端能力session

  前端配合後端,只寫模板語言,能力受限。cors

五、後端語言和模板語言解耦

   後端開發語言與模板語言耦合度較高,依賴開發語言,更換後端語言的成本很高。

2、django路由配置

(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)訪問顯示效果

  

3、django部分構建中間件解決跨域問題

  建立/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'
]

4、API示例數據錄入

一、構建模型表並數據遷移

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

  執行數據遷移操做。

二、admin組件使用

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)

三、數據錄入

  

5、api課程查詢接口

  基於rest-framework實現查詢課程查詢接口。

一、方式一:根據帶不帶id交給同一視圖不一樣代碼去處理

  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對象。

6、api示例之課程詳細接口

一、簡單實現詳細信息的序列化(depth)

  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的字段。顯示效果以下:

  

7、api示例之課程優化(練習題 )

一、查詢全部課程level字段修改成中文 

  修改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)
相關文章
相關標籤/搜索