vue+rest-framework前后端分離整合


一、為什么要做前后端分離項目

1、滿足多端適配

  隨着移動端的興起,現在公司產品不只限於pc端的,包括Android,IOS。

  按照以前的方式,我們后端其實就要有多套,pc一套,APP端兩套。開發成本以及開發效率會很低,如果前后端分離,我們后端只需要有一套就可以了~

  后端只提供接口,前端不管是pc還是APP都可以去調用數據。

2、前后端職責划分

  以前的編程方式,前后端職責不清晰,模板語言前端后端都可以寫。

3、開發效率

  前后端互相等待。

4、解放前端能力

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

5、后端語言和模板語言解耦

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

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

  

三、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'
]

四、API示例數據錄入

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

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

  執行數據遷移操作。

2、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)

3、數據錄入

  

五、api課程查詢接口

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

1、方式一:根據帶不帶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)

  顯示效果:

  

  這種方法雖然可以實現但是如果代碼很多時,就看起來很不簡潔了。如果能交給不同的方法來執行就比較好了。

2、方式二:由視圖類中不同的方法來處理不同的查詢操作

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

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

1、簡單實現詳細信息的序列化(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是找兩層(再往下找一層關聯表)的數據。

  顯示效果:

  

  雖然這種方法可以實現效果,但是它往往給的數據過多了。

2、指定數據庫字段在頁面顯示

  對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()可以用來獲取對應字段的值

  顯示效果:

  

3、進一步拿到推薦課程信息

  source用來解決一對一、外鍵、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的字段。顯示效果如下:

  

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

1、查詢所有課程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"]

  顯示效果:

  

2、查詢課程詳細——顯示該課程相關的所有章節

  修改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() 來實現的。

  顯示效果如下所示:

  

3、序列化和視圖解耦

  創建/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)

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM