一、為什么要做前后端分離項目
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)
