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

一、为什么要做前后端分离项目

1、满足多端适配

  随着移动端的兴起,现在公司产品不只限于pc端的,包括Android,IOS。

  按照以前的方式,我们后端其实就要有多套,pc一套,APP端两套。开发成本以及开发效率会很低,如果前后端分离,我们后端只需要有一套就可以了~

  后端只提供接口,前端不管是pc还是APP都可以去调用数据。

2、前后端职责划分

  以前的编程方式,前后端职责不清晰,模板语言前端后端都可以写。

3、开发效率

  前后端互相等待。

4、解放前端能力

  前端配合后端,只写模板语言,能力受限。

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

   后端开发语言与模板语言耦合度较高,依赖开发语言,更换后端语言的成本很高。

二、django路由配置

(1)项目urls.py修改如下:

1
2
3
4
5
6
from django.conf.urls import url, include
  
urlpatterns = [
    # path('admin/', admin.site.urls),
    url(r'^api/', include('api.urls')),
]

(2)应用目录下创建urls.py文件,配置如下:

1
2
3
4
5
6
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类视图文件如下所示:

1
2
3
4
5
6
7
8
9
10
11
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,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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,添加中间件:

1
2
3
4
5
6
7
8
9
10
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、构建模型表并数据迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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组件使用

1
2
3
4
5
6
7
8
9
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:

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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:

1
2
3
4
5
6
7
8
9
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改写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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调整如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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做了如下修改:

1
2
3
4
5
6
7
8
9
10
11
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的跨表查询。但是遇到多对多就不好用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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实现对课程列表序列化修改:

1
2
3
4
5
6
7
8
9
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如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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文件夹和文件,将序列化相关内容迁移过来,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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中引入序列化组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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)

  

 

posted @   休耕  阅读(1269)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示