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

1、满足多端适配

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

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

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

2、前后端职责划分

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

3、开发效率

  前后端互相等待。

4、解放前端能力

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

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

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

二、django路由配置

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

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

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

  1. from django.conf.urls import url, include
  2. from api.views import course
  3.  
  4. urlpatterns = [
  5. url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view()),
  6. ]

(3)修改/api/views/course.py类视图文件如下所示:

  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
  4.  
  5. class CourseView(APIView):
  6. versioning_class = URLPathVersioning
  7.  
  8. def get(self, request, *args, **kwargs):
  9. print(request.version)
  10. return Response('...')

(4)访问显示效果

  

三、django部分构建中间件解决跨域问题

  创建/api/cors.py,代码如下所示:

  1. from django.middleware.common import CommonMiddleware # 通过它找到要引入的模块
  2. from django.utils.deprecation import MiddlewareMixin
  3.  
  4. class CORSMiddleware(MiddlewareMixin):
  5. """自定义中间件"""
  6. def process_response(self, request, response):
  7. # 添加响应头
  8.  
  9. # 允许你的域名来获取我的数据
  10. response['Access-Control-Allow-Origin'] = "*"
  11. # 允许你携带Content-Type请求头,这里不能写*
  12. response['Access-Control-Allow-Headers'] = "Content-Type"
  13. # 允许你发送GET/POST/DELETE/PUT
  14. response['Access-Control-Allow-Methods'] = "GET, POST"
  15. return response

  修改settings.py,添加中间件:

  1. MIDDLEWARE = [
  2. 'django.middleware.security.SecurityMiddleware',
  3. 'django.contrib.sessions.middleware.SessionMiddleware',
  4. 'django.middleware.common.CommonMiddleware',
  5. 'django.middleware.csrf.CsrfViewMiddleware',
  6. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  7. 'django.contrib.messages.middleware.MessageMiddleware',
  8. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  9. 'api.cors.CORSMiddleware'
  10. ]

四、API示例数据录入

1、构建模型表并数据迁移

  1. from django.db import models
  2. # Create your models here.
  3.  
  4. class Course(models.Model):
  5. """课程表"""
  6. title = models.CharField(verbose_name='课程名称', max_length=32)
  7. course_img = models.CharField(verbose_name="课程图片", max_length=64)
  8. level_choices = (
  9. (1, "初级"),
  10. (2, "中级"),
  11. (3, "高级"),
  12. )
  13. level = models.IntegerField(verbose_name="课程难易程度", choices=level_choices, default=1)
  14.  
  15. def __str__(self):
  16. return self.title
  17.  
  18. class CourseDetail(models.Model):
  19. """课程详细表"""
  20. course = models.OneToOneField(to='Course', on_delete=models.CASCADE)
  21. slogon = models.CharField(verbose_name="口号", max_length=255)
  22. why = models.CharField(verbose_name="为什么要学习?", max_length=255)
  23. recommend_courses = models.ManyToManyField(verbose_name="推荐课程", to="Course", related_name="rc")
  24.  
  25. def __str__(self):
  26. return "课程详细:" + self.course.title
  27.  
  28. class Chapter(models.Model):
  29. """章节"""
  30. num = models.IntegerField(verbose_name="章节")
  31. name = models.CharField(verbose_name="章节名称", max_length=32)
  32. course = models.ForeignKey(verbose_name="所属课程", to="Course", on_delete=models.CASCADE)
  33.  
  34. def __str__(self):
  35. return "课程章节" + self.name

  执行数据迁移操作。

2、admin组件使用

  1. from django.contrib import admin
  2. from api import models
  3.  
  4. # Register your models here.
  5.  
  6. """ root/root!2345 """
  7. admin.site.register(models.Course)
  8. admin.site.register(models.CourseDetail)
  9. admin.site.register(models.Chapter)

3、数据录入

  

五、api课程查询接口

  基于rest-framework实现查询课程查询接口。

1、方式一:根据带不带id交给同一视图不同代码去处理

  api/urls.py:

  1. from django.conf.urls import url, include
  2. from api.views import course
  3.  
  4. urlpatterns = [
  5. # 方式一:
  6. url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view()),
  7. url(r'^(?P<version>[v1|v2]+)/course/(?P<pk>\d+)/$', course.CourseView.as_view()),
  8. ]

  api/views/course.py:

  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from api import models
  4. from rest_framework import serializers
  5.  
  6. class CourseSerializer(serializers.ModelSerializer):
  7. """对django model 的实例进行序列化"""
  8. class Meta:
  9. # 帮忙转换没有自己写的字段
  10. model = models.Course
  11. fields = "__all__"
  12.  
  13. class CourseView(APIView):
  14.  
  15. def get(self, request, *args, **kwargs):
  16. """
  17. 查看所有的课程:http://127.0.0.1:8000/api/v1/course/
  18. 查看某一课程:http://127.0.0.1:8000/api/v1/course/1
  19. """
  20. ret = {'code': 1000, 'data': None}
  21. try:
  22. pk = kwargs.get('pk')
  23. if pk: # 如果pk有值
  24. obj = models.Course.objects.filter(id=pk).first()
  25. ser = CourseSerializer(instance=obj, many=False)
  26. else:
  27. queryset = models.Course.objects.all() # QuerySet里面是一个个对象
  28. ser = CourseSerializer(instance=queryset, many=True) # 序列化结果
  29. ret['data'] = ser.data
  30. except Exception as e:
  31. ret['code'] = 1001
  32. ret['error'] = "获取课程失败"
  33.  
  34. return Response(ret)

  显示效果:

  

  这种方法虽然可以实现但是如果代码很多时,就看起来很不简洁了。如果能交给不同的方法来执行就比较好了。

2、方式二:由视图类中不同的方法来处理不同的查询操作

  api/urls.py:

  1. from django.conf.urls import url, include
  2. from api.views import course
  3.  
  4. urlpatterns = [
  5. # 方式二:前提是要重写as_view
  6. url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view({'get': 'list'})),
  7. url(r'^(?P<version>[v1|v2]+)/course/(?P<pk>\d+)/$', course.CourseView.as_view({'get': 'retrieve'})),
  8. ]

  api/views/course.py改写如下:

  1. from rest_framework.viewsets import ViewSetMixin
  2.  
  3. class CourseView(ViewSetMixin, APIView):
  4.  
  5. def list(self, request, *args, **kwargs):
  6. """
  7. 课程列表接口
  8. :param request:
  9. :param args:
  10. :param kwargs:
  11. :return:
  12. """
  13. ret = {'code': 1000, 'data': None}
  14. try:
  15. queryset = models.Course.objects.all() # QuerySet里面是一个个对象
  16. ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 True:queryset
  17. ret['data'] = ser.data
  18. except Exception as e:
  19. ret['code'] = 1001
  20. ret['error'] = "获取课程失败"
  21.  
  22. return Response(ret)
  23.  
  24. def retrieve(self, request, *args, **kwargs):
  25. """
  26. 课程详细接口
  27. :param request:
  28. :param args:
  29. :param kwargs:
  30. :return:
  31. """
  32. ret = {'code': 1000, 'data': None}
  33. try:
  34. pk = kwargs.get('pk')
  35. obj = models.Course.objects.filter(id=pk).first()
  36. ser = CourseSerializer(instance=obj, many=False) # many描述是model对象还是QuerySet False:对象
  37. ret['data'] = ser.data
  38. except Exception as e:
  39. ret['code'] = 1001
  40. ret['error'] = "获取课程失败"
  41.  
  42. return Response(ret)

  注意:many描述是model对象还是QuerySet,当many=True时,描述是QuerySet;当many=False时,描述是model对象。

六、api示例之课程详细接口

1、简单实现详细信息的序列化(depth)

  course.py调整如下:

  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from api import models
  4. from rest_framework import serializers
  5.  
  6. class CourseSerializer(serializers.ModelSerializer):
  7. """对django model 的实例进行序列化"""
  8. class Meta:
  9. # 帮忙转换没有自己写的字段
  10. model = models.Course
  11. fields = "__all__"
  12.  
  13. class CourseDetailSerializer(serializers.ModelSerializer):
  14. """课程详细序列化"""
  15. class Meta:
  16. model = models.CourseDetail
  17. fields = "__all__"
  18. depth = 1 # 0-10之间,0是帮忙找一层(当前表关联的表)的数据,1是找两层(再往下找一层关联表)的数据
  19.  
  20. from rest_framework.viewsets import ViewSetMixin
  21.  
  22. class CourseView(ViewSetMixin, APIView):
  23. def list(self, request, *args, **kwargs):
  24. """
  25. 课程列表接口
  26. :param request:
  27. :param args:
  28. :param kwargs:
  29. :return:
  30. """
  31. ret = {'code': 1000, 'data': None}
  32. try:
  33. queryset = models.Course.objects.all() # QuerySet里面是一个个对象
  34. ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 True:queryset
  35. ret['data'] = ser.data
  36. except Exception as e:
  37. ret['code'] = 1001
  38. ret['error'] = "获取课程失败"
  39.  
  40. return Response(ret)
  41.  
  42. def retrieve(self, request, *args, **kwargs):
  43. """
  44. 课程详细接口
  45. :param request:
  46. :param args:
  47. :param kwargs:
  48. :return:
  49. """
  50. ret = {'code': 1000, 'data': None}
  51. try:
  52. pk = kwargs.get('pk') # 课程id
  53. # 课程详细对象
  54. obj = models.CourseDetail.objects.filter(course_id=pk).first()
  55. ser = CourseDetailSerializer(instance=obj, many=False)
  56. ret['data'] = ser.data
  57.  
  58. except Exception as e:
  59. ret['code'] = 1001
  60. ret['error'] = "获取课程失败"
  61.  
  62. return Response(ret)

  主要是调整了retrieve方法,增加了CourseDetailSerializer来处理详细信息序列化。

  注意:这里配置depth = 1   官方推荐是:配置0-10之间,0是帮忙找一层(当前表关联的表)的数据,1是找两层(再往下找一层关联表)的数据。

  显示效果:

  

  虽然这种方法可以实现效果,但是它往往给的数据过多了。

2、指定数据库字段在页面显示

  对CourseDetailSerializer做了如下修改:

  1. class CourseDetailSerializer(serializers.ModelSerializer):
  2. """课程详细序列化"""
  3. # 自定义字段 serializers默认对model对象做序列化
  4. title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询
  5. img = serializers.CharField(source="course.course_img")
  6. # level = serializers.CharField(source="course.level") # 这个只是拿到了数字
  7. level = serializers.CharField(source="course.get_level_display")
  8.  
  9. class Meta:
  10. model = models.CourseDetail
  11. fields = ["course", "title", "img", "level", "slogon", "why"]

  注意source的用法,且get_字段名_display()可以用来获取对应字段的值

  显示效果:

  

3、进一步拿到推荐课程信息

  source用来解决一对一、外键、choice的跨表查询。但是遇到多对多就不好用了。

  1. class CourseDetailSerializer(serializers.ModelSerializer):
  2. """课程详细序列化"""
  3. # 自定义字段 serializers默认对model对象做序列化
  4. title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询
  5. img = serializers.CharField(source="course.course_img")
  6. # level = serializers.CharField(source="course.level") # 这个只是拿到了数字
  7. level = serializers.CharField(source="course.get_level_display")
  8.  
  9. # 针对多对多字段使用SerializerMethodField
  10. recommends = serializers.SerializerMethodField() # 取get_recommends(obj)的返回值
  11.  
  12. class Meta:
  13. model = models.CourseDetail
  14. fields = ["course", "title", "img", "level", "slogon", "why", "recommends"]
  15.  
  16. def get_recommends(self, obj): # 注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。
  17. # 获取推荐的所有课程
  18. queryset = obj.recommend_courses.all()
  19. return [{'id': row.id, 'title': row.title} for row in queryset]

  注意多对多的字段使用SerializerMethodField,recommends取的是get_recommends函数的返回值。

  注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。显示效果如下:

  

七、api示例之课程优化(练习题 )

1、查询所有课程level字段修改为中文

  修改CourseSerializer实现对课程列表序列化修改:

  1. class CourseSerializer(serializers.ModelSerializer):
  2. """对django model 的实例进行序列化"""
  3. # 自定义字段
  4. level = serializers.CharField(source="get_level_display")
  5.  
  6. class Meta:
  7. # 帮忙转换没有自己写的字段
  8. model = models.Course
  9. fields = ["id", "title", "course_img", "level"]

  显示效果:

  

2、查询课程详细——显示该课程相关的所有章节

  修改CourseDetailSerializer如下所示:

  1. class CourseDetailSerializer(serializers.ModelSerializer):
  2. """课程详细序列化"""
  3. # 自定义字段 serializers默认对model对象做序列化
  4. title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询
  5. img = serializers.CharField(source="course.course_img")
  6. # level = serializers.CharField(source="course.level") # 这个只是拿到了数字
  7. level = serializers.CharField(source="course.get_level_display")
  8.  
  9. # 针对多对多字段使用SerializerMethodField
  10. recommends = serializers.SerializerMethodField() # 取get_recommends(obj)的返回值
  11. chapter = serializers.SerializerMethodField()
  12.  
  13. class Meta:
  14. model = models.CourseDetail
  15. fields = ["course", "title", "img", "level", "slogon", "why", "recommends", "chapter"]
  16.  
  17. def get_recommends(self, obj): # 注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。
  18. # 获取推荐的所有课程
  19. queryset = obj.recommend_courses.all()
  20. return [{'id': row.id, 'title': row.title} for row in queryset]
  21.  
  22. def get_chapter(self, obj): # obj是课程详细的对象
  23. queryset = obj.course.chapter_set.all() # course.chapter_set反向查找,取到所有的章节
  24. return [{'id': row.id, 'name': row.name} for row in queryset]

  Django 中的一对多关系用 ForeignKey 来实现,一对多的反向查询是通过:按表名小写_set.all() 来实现的。

  显示效果如下所示:

  

3、序列化和视图解耦

  创建/api/serializers/course.py文件夹和文件,将序列化相关内容迁移过来,如下所示:

  1. from api import models
  2. from rest_framework import serializers
  3.  
  4. class CourseSerializer(serializers.ModelSerializer):
  5. """课程序列化"""
  6. # 自定义字段
  7. level = serializers.CharField(source="get_level_display")
  8.  
  9. class Meta:
  10. # 帮忙转换没有自己写的字段
  11. model = models.Course
  12. fields = ["id", "title", "course_img", "level"]
  13.  
  14. class CourseDetailSerializer(serializers.ModelSerializer):
  15. """课程详细序列化"""
  16. # 自定义字段 serializers默认对model对象做序列化
  17. title = serializers.CharField(source="course.title") # source与数据库的某个字段绑定,这样写完成了跨表查询
  18. img = serializers.CharField(source="course.course_img")
  19. # level = serializers.CharField(source="course.level") # 这个只是拿到了数字
  20. level = serializers.CharField(source="course.get_level_display")
  21.  
  22. # 针对多对多字段使用SerializerMethodField
  23. recommends = serializers.SerializerMethodField() # 取get_recommends(obj)的返回值
  24. chapter = serializers.SerializerMethodField()
  25.  
  26. class Meta:
  27. model = models.CourseDetail
  28. fields = ["course", "title", "img", "level", "slogon", "why", "recommends", "chapter"]
  29.  
  30. def get_recommends(self, obj): # 注意这个方法必须是“get_"拼接配置了SerializerMethodField的字段。
  31. # 获取推荐的所有课程
  32. queryset = obj.recommend_courses.all()
  33. return [{'id': row.id, 'title': row.title} for row in queryset]
  34.  
  35. def get_chapter(self, obj): # obj是课程详细的对象
  36. queryset = obj.course.chapter_set.all() # course.chapter_set反向查找,取到所有的章节
  37. return [{'id': row.id, 'name': row.name} for row in queryset]

  在view/course.py中引入序列化组件:

  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from api import models
  4. from api.serializers.course import CourseDetailSerializer, CourseSerializer
  5. from rest_framework.viewsets import ViewSetMixin
  6.  
  7. class CourseView(ViewSetMixin, APIView):
  8.  
  9. def list(self, request, *args, **kwargs):
  10. """
  11. 课程列表接口
  12. :param request:
  13. :param args:
  14. :param kwargs:
  15. :return:
  16. """
  17. ret = {'code': 1000, 'data': None}
  18. try:
  19. queryset = models.Course.objects.all() # QuerySet里面是一个个对象
  20. ser = CourseSerializer(instance=queryset, many=True) # 序列化结果 True:queryset
  21. ret['data'] = ser.data
  22. except Exception as e:
  23. ret['code'] = 1001
  24. ret['error'] = "获取课程失败"
  25.  
  26. return Response(ret)
  27.  
  28. def retrieve(self, request, *args, **kwargs):
  29. """
  30. 课程详细接口
  31. :param request:
  32. :param args:
  33. :param kwargs:
  34. :return:
  35. """
  36. ret = {'code': 1000, 'data': None}
  37. try:
  38. pk = kwargs.get('pk') # 课程id
  39. # 课程详细对象
  40. obj = models.CourseDetail.objects.filter(course_id=pk).first()
  41.  
  42. ser = CourseDetailSerializer(instance=obj, many=False)
  43. ret['data'] = ser.data
  44.  
  45. except Exception as e:
  46. ret['code'] = 1001
  47. ret['error'] = "获取课程失败"
  48.  
  49. return Response(ret)

  

vue+rest-framework前后端分离整合的更多相关文章

  1. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  2. Vue+Spring Boot 前后端分离的商城项目开源啦!

    新蜂商城 Vue 移动端版本开源啦! 去年开源新蜂商城项目后,就一直在计划这个项目 Vue 版本的改造,2020 年开始开发并且自己私下一直在测试,之前也有文章介绍过测试过程和存在的问题,修改完成后, ...

  3. ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

    一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自 ...

  4. VUE开发(二)nginx配合vue来实现前后端分离部署

    一.引言 由于本地是采用vue+spring boot实现的前后端分离项目,本机启动的时候先启动后场服务,再单独启动vue工程,然后可以实现全流程贯穿,但是我们要部署到服务器上的时候,一般都是打一个j ...

  5. vue项目实践-前后端分离关于权限的思路

    前后端分离后权限的思路 最近看到许多关于权限的思路,但好像都是使用动态加载路由的方式,现在也分享下我在项目中使用的解决方案. 前后端分离关于权限的处理每个人都不一样,根据项目选择制定合适的方案就好 我 ...

  6. vue+node+mongodb前后端分离博客系统

    感悟 历时两个多月,终于利用工作之余完成了这个项目的1.0版本,为什么要写这个项目?其实基于vuejs+nodejs构建的开源博客系统有很多,但是大多数不支持服务端渲染,也不支持动态标题,只是做到了前 ...

  7. Springboot + Vue + shiro 实现前后端分离、权限控制

    本文总结自实习中对项目对重构.原先项目采用Springboot+freemarker模版,开发过程中觉得前端逻辑写的实在恶心,后端Controller层还必须返回Freemarker模版的ModelA ...

  8. Vue+Express实现前后端分离

    先说明一下缘由,因为自己前段时间在实习,实习期间为了参与项目开发,粗略学习了下Vue.Vuex.Vue-Router,大致会一些基础的.这里也快要做毕业设计了,趁着放假回来的这两天,学习下Node的相 ...

  9. vue+Ueditor集成 [前后端分离项目][图片、文件上传][富文本编辑]

    后端DEMO:https://github.com/coderliguoqing/UeditorSpringboot 前端DEMO:https://github.com/coderliguoqing/ ...

随机推荐

  1. python基础之格式化字符串

    一.格式化字符功能介绍 应用场景:一般在print的时候提供占位符;python中提供两种格式化字符串方式:第一种是古老的利用百分号的方式,第二种是增强的格式化字符串.format 函数. 二.古老的 ...

  2. 2. Shell编程第二讲

    (1) 条件测试: test   [ 命令 test 或 [ 可以测试一个条件是否成立,如果测试结果为真,则该命令的Exit Status为0,如果测试结果为假,则命令的Exit Status为1(注 ...

  3. 洛谷 P4001 [ICPC-Beijing 2006]狼抓兔子

    题目描述 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: ...

  4. javascrip 中排序的函数怎么理解

    其中s是数组[888,2222,9,4]:我不明白sort函数中参数是如何作用的,function中的a和b又是干什么的? 那个function的作用就是比较两个数的大小用的,然后返回结果的正负作为排 ...

  5. Little Elephant and Magic Square

    Little Elephant loves magic squares very much. A magic square is a 3 × 3 table, each cell contains s ...

  6. POJ - 1733 离散化 并查集

    题意:求问数列区间奇偶信息正确与否 和上一题一样(甚至弱化),加个离散就ok啦 #include<iostream> #include<algorithm> #include& ...

  7. php 的基本语法

    八种数据类型: 4种标量类型:boolean.integer.float.string 2种复合类型:array.object 2种特殊类型:resource.NULL 如果想看某个表达式的值和类型用 ...

  8. [转] Spring Boot特性

    [From] http://blog.javachen.com/2015/03/13/some-spring-boot-features.html 1. SpringApplication Sprin ...

  9. C++ GUI Qt4编程(07)-3.1menu

    1. C++ GUI Qt4编程第三章,添加menu菜单. 2. mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include < ...

  10. 转 AIX7.2+11.2.0.4RAC实施

    参考 https://blog.csdn.net/alangmei/article/details/18310381 https://blog.csdn.net/smasegain/article/d ...