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) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术