01 - vue + rest framework - v1.0、v1.1 - 课程列表、课程详细列表
code
https://github.com/alice-bj/luffy/releases
一、V1.0 知识点
1. 渲染器 规定页面显示的效果(无用) 2. 版本 原理:要了解 使用: 1. 添加配置 REST_FRAMEWORK = { .... 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', 'ALLOWED_VERSIONS':['v1','v2'], # 允许的版本 'VERSION_PARAM':'version', # 参数 'DEFAULT_VERSION':'v1', # 默认版本 .... } 2. 设置路由 s9luffycity/urls.py urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>\w+)/', include('api.urls')), ] api/urls.py urlpatterns = [ url(r'^course/$', course.CourseView.as_view()), ] 3. 获取版本 request.version 获取版本 3. vue+rest framework vue: - 路由 + 组件 - axios发送ajax请求 - that api: - 跨域 补充: - 域名不同 - 端口不同 cors: 本质设置响应头 跨域的简单请求和复杂请求 # 允许你的域名来获取我的数据 response['Access-Control-Allow-Origin'] = "*" # 允许你携带Content-Type请求头 response['Access-Control-Allow-Headers'] = "Content-Type" # 允许你发送DELETE,PUT response['Access-Control-Allow-Methods'] = "DELETE,PUT"
效果图
1. 版本:
1. 添加配置 REST_FRAMEWORK = { .... 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', 'ALLOWED_VERSIONS':['v1','v2'], # 允许的版本 'VERSION_PARAM':'version', # 参数 'DEFAULT_VERSION':'v1', # 默认版本 .... } 2. 设置路由 s9luffycity/urls.py urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>\w+)/', include('api.urls')), ] api/urls.py urlpatterns = [ url(r'^course/$', course.CourseView.as_view()), ] 3. 获取版本 request.version 获取版本
2. 跨域的简单请求和复杂请求
cors.py
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' # 允许你发送 DELETE PUT请求 response['Access-Control-Allow-Methods'] = 'DELETE,PUT' return response
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', ... ... 'api.cors.CORSMiddleware' ]
二、V1.1 知识点
s9day105 内容回顾: - restful 规范(10) - django rest framework框架(10) - 跨域 - 为什么会有跨域? - 绕过浏览器同源策略就可以跨域。 - jsonp 动态创建script标签 同源策略会阻止ajax请求;不阻止具有src属性的标签 <script src='xxxx'></script> - cors 设置响应头 今日内容: - vue - api 内容详细: 1. vue - 课程列表 - 课程详细 - 任务: - 课程表 id title course_img level(choices) - 课程详细(one2one 课程 ) id why 推荐课程 - 章节 id name - 功能: a. 课程列表显示图片 b. 课程详细显示: - 为什么要学习这个课程 - 所有推荐课程 - 所有章节 2. api 1. 查询所有的课程 http://127.0.0.1:8000/api/v1/course/ 练习:level变中文 2. 查询课程详细 http://127.0.0.1:8000/api/v1/course/1/ - 路由 as_view 是否添加参数,取决于视图继承的类 - 序列化 - depth - source - 自定义method 练习:显示该课程相关的所有章节 this补充: 题目1: var name = '景女神' function Foo(name,age){ this.name = name; this.age = age; this.getName = function(){ console.log(this.name); # 文州 (function(){ console.log(this.name); # 女神 })() } } obj = new Foo('文州',19) obj.getName() 题目2: var name = '景女神' function Foo(name,age){ this.name = name; this.age = age; this.getName = function(){ console.log(this.name); # 文州 var that = this (function(){ console.log(that.name); # 文州 })() } } obj = new Foo('文州',19) obj.getName() 题目3: var name = '景女神' obj = { name:'文州', age: 19, getName:function(){ console.log(this.name); # 文州 var that = this (function(){ console.log(that.name); # 文州 })() } } obj.getName() 作业: 1. 前后端流程 2. 图片 3. CC视频账号 4. git相关 - 安装git软件 - 注册github账号
效果图
1. 前端 - axios 发送ajax请求
通过ajax向接口发起请求,并获取课程列表
axios 可以发ajax请求 npm install axios --save
1. 在main.js中配置
2. 使用axios发送请求
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import axios from 'axios' // 在vue的全局变量中设置了 $axios // 以后组件 使用 this.$axios Vue.prototype.$axios = axios Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
<template> <div> <h2>课程列表</h2> <div v-for="row in courseList"> <div style="width: 350px; float: left;"> <img src=""> <h3><router-link :to="{name:'detail',params:{id:row.id}}">{{row.title}}</router-link></h3> <p>{{row.level}}</p> </div> </div> </div> </template> <script> export default{ name:'course', data(){ return { msg:'course', courseList:[], } }, // 页面加载时自动执行 mounted:function(){ this.initCourse() }, methods:{ initCourse:function(){ //通过ajax向接口发动请求,并获取课程列表 // axios 可以发ajax请求 npm install axios --save // 1. 在main.js中配置 // 2. 使用axios发送请求 var that = this this.$axios.request({ url:'http://127.0.0.1:8000/api/v1/course/', method:'GET' }).then(function(ret){ // ajax请求发送成功后,获得的响应内容 // ret.data if(ret.data.code==1000){ that.courseList = ret.data.data console.log(ret.data.data) }else{ alert('获取数据失败') } }).catch(function(ret){ //ajax请求失败后,获取响应的内容 }) } } } </script> <style scoped> </style>
<template> <div> <p>{{detail.course}}</p> <p>{{detail.img}}</p> <p>{{detail.level}}</p> <p>{{detail.slogon}}</p> <p>{{detail.title}}</p> <p>{{detail.why}}</p> <div> <ul v-for='item in detail.chapters'> <li>{{item.name}}</li> </ul> </div> <div> <ul v-for='item in detail.recommends'> <li>{{item.title}}</li> </ul> </div> </div> </template> <script> export default{ name:'detail', data(){ return { detail:{ course:null, img:null, level:null, slogon:null, title:null, why:null, chapters:[], recommends:[] } } }, mounted:function(){ console.log(this.$route.params.id) this.initDetail() }, methods:{ initDetail:function(){ var nid = this.$route.params.id var that = this this.$axios.request({ url:'http://127.0.0.1:8000/api/v1/course/'+nid+'/', method:"GET" }).then(function(ret){ if(ret.data.code==1000){ that.detail = ret.data.data }else{ alert(ret.data.error) } }).catch(function(ret){ }) } } } </script> <style scoped> </style>
2. 前端 - 链接带参数,并取消#
mode:'history'
path: '/detail/:id'
参数得使用:
Course.vue
<template> <div> <h2>课程列表</h2> <div v-for="row in courseList"> <div style="width: 350px; float: left;"> <img src=""> <h3><router-link :to="{name:'detail',params:{id:row.id}}">{{row.title}}</router-link></h3> <p>{{row.level}}</p> </div> </div> </div> </template>
3. 前端 - 页面获取id从接口取值
Detail.vue
<script> export default{ name:'detail', data(){ return { detail:{ course:null, img:null, level:null, slogon:null, title:null, why:null, chapters:[], recommends:[] } } },
// 页面加载时 自动执行 mounted:function(){ console.log(this.$route.params.id) this.initDetail() }, methods:{ initDetail:function(){ var nid = this.$route.params.id var that = this this.$axios.request({ url:'http://127.0.0.1:8000/api/v1/course/'+nid+'/', method:"GET" }).then(function(ret){ if(ret.data.code==1000){ that.detail = ret.data.data }else{ alert(ret.data.error) } }).catch(function(ret){ }) } } } </script>
index.js
import Vue from 'vue' import Router from 'vue-router' import Index from '@/components/Index' import Course from '@/components/Course' import Micro from '@/components/Micro' import News from '@/components/News' import Detail from '@/components/Detail' Vue.use(Router) export default new Router({ routes: [ { path: '/index', name: 'index', component: Index }, { path: '/course', name: 'course', component: Course }, { path: '/detail/:id', name: 'detail', component: Detail }, { path: '/micro', name: 'micro', component: Micro }, { path: '/news', name: 'news', component: News }, ], mode:'history' })
app.vue
<template> <div id="app"> <router-link to='/index'>首页</router-link> <router-link to='/course'>课程</router-link> <router-link to='/micro'>微职位</router-link> <router-link to='/news'>深科技</router-link> <router-view/> </div> </template> <script> export default { name: 'App' } </script> <style> </style>
4. 后端 - 提供接口
1.查询所有课程 http://localhost:8000/api/v1/course/ 2.查询课程详细 http://localhost:8000/api/v1/course/1/ - 路由 as_view 是否添加参数,取决于视图继承的类 - 序列化 - depth - source - 自定义method
课程,课程详细
from django.db import models 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") 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") def __str__(self): return self.name
urls.py
from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>\w+)/', include('api.urls')), ] ------------------------------------ api/urls.py from django.conf.urls import url from api.views import course urlpatterns = [ # 方式一: # url(r'^course/$', course.CourseView.as_view()), # url(r'^course/(?P<pk>\d+)/$', course.CourseView.as_view()), # 方式二: url(r'^course/$', course.CourseView.as_view({"get": 'list'})), url(r'^course/(?P<pk>\d+)/$', course.CourseView.as_view({"get": "retrieve"})), ]
views/course.py
from rest_framework.views import APIView from rest_framework.response import Response from api.models import * from api.serializers.course import CourseSerializers, CourseDetailSerializers # 方式一: # class CourseView(APIView): # # def get(self,request,*args,**kwargs): # ret = {'code':1000, 'data':None} # try: # pk = kwargs.get('pk') # if pk: # obj = Course.objects.filter(id=pk).first() # ser = CourseSerializers(instance=obj,many=False) # else: # course_list = Course.objects.all() # ser = CourseSerializers(course_list,many=True) # ret['data'] = ser.data # except Exception as e: # ret['code'] = 1001, # ret['data'] = '获取课程失败' # return Response(ret) # 方式二: # view # apiview # viewsets # GenericAPIView from rest_framework.viewsets import GenericViewSet,ViewSetMixin class CourseView(ViewSetMixin, APIView): def list(self,request,*args,**kwargs): """ 课程列表接口 :param request: :param args: :param kwargs: :return: """ ret = {'code':1000, 'data': None} try: course_list = Course.objects.all() ser = CourseSerializers(course_list,many=True) ret['data'] = ser.data except Exception as e: ret['code'] = 1001, ret['data'] = '获取课程失败' 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 = CourseDetail.objects.filter(course_id=pk).first() ser = CourseDetailSerializers(instance=obj,many=False) ret['data'] = ser.data except Exception as e: ret['code'] = 1001, ret['data'] = '获取课程失败' return Response(ret)
serializers/course.py
from rest_framework import serializers from api import models class CourseSerializers(serializers.ModelSerializer): """ 课程序列化 """ level = serializers.CharField(source='get_level_display') class Meta: model = models.Course fields = ['id','title','course_img','level'] # class CourseDetailSerializers(serializers.ModelSerializer): # class Meta: # model = models.CourseDetail # fields = "__all__" # depth = 1 # 这个可以实现 关联的表 都拿到数据 不介意这么做!! # class CourseDetailSerializers(serializers.ModelSerializer): """ 课程详细序列化 """ # source 处理 one2one / fk / choice 只获取一条数据 title = serializers.CharField(source="course.title") # 跨表查询其他字段 img = serializers.CharField(source='course.course_img') level = serializers.CharField(source='course.get_level_display') # 要拿中文的 get_level_display # recommend_courses = serializers.CharField(source='recommend_courses.all') # 这种方式不会序列化 不用这种 # SerializerMethodField 处理 m2m recommends = serializers.SerializerMethodField() # 所有章节 chapters = serializers.SerializerMethodField() class Meta: model = models.CourseDetail fields = ["course","title","img","level","slogon","why","recommends","chapters"] def get_recommends(self, obj): # 获取推荐的所有课程 queryset = obj.recommend_courses.all() # 列表生成式 return [{"id":row.id,"title":row.title} for row in queryset] def get_chapters(self, obj): # queryset = models.Chapter.objects.filter(course=obj.course).all() # 正向查询 queryset = obj.course.chapter_set.all() # 反向查询 return [{"id":row.id,"num":row.num,"name":row.name} for row in queryset]
5. 后端 - as_view() 传参,视图继承ViewSetMixin
url(r'^course/$', course.CourseView.as_view({"get": 'list'})), url(r'^course/(?P<pk>\d+)/$', course.CourseView.as_view({"get": "retrieve"})),
from rest_framework.viewsets import GenericViewSet,ViewSetMixin class CourseView(ViewSetMixin, APIView): def list(self,request,*args,**kwargs): """ 课程列表接口 :param request: :param args: :param kwargs: :return: """ ret = {'code':1000, 'data': None} try: course_list = Course.objects.all() ser = CourseSerializers(course_list,many=True) ret['data'] = ser.data except Exception as e: ret['code'] = 1001, ret['data'] = '获取课程失败' 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 = CourseDetail.objects.filter(course_id=pk).first() ser = CourseDetailSerializers(instance=obj,many=False) ret['data'] = ser.data except Exception as e: ret['code'] = 1001, ret['data'] = '获取课程失败' return Response(ret)
6. 后端 - modes choices 展示显示中文
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 CourseSerializers(serializers.ModelSerializer): """ 课程序列化 """ level = serializers.CharField(source='get_level_display') class Meta: model = models.Course fields = ['id','title','course_img','level']
7. 后端 - 序列化展示 一对一,一对多,多对多 相关内容depth/source/SerializerMethodField
depth = 1 / 2 ... 不介意 ,1层 / 2层 不想要得数据也能拿到
class CourseDetailSerializers(serializers.ModelSerializer): class Meta: model = models.CourseDetail fields = "__all__" depth = 1 # 这个可以实现 关联的表 都拿到数据 不介意这么做!!
一对多 / 一对一:
source
多对多:
chapters = serializers.SerializerMethodField()
def get_chapters(self, obj):
# queryset = models.Chapter.objects.filter(course=obj.course).all() # 正向查询
queryset = obj.course.chapter_set.all() # 反向查询
return [{"id":row.id,"num":row.num,"name":row.name} for row in queryset]
class CourseDetailSerializers(serializers.ModelSerializer): """ 课程详细序列化 """ # source 处理 one2one / fk / choice 只获取一条数据 title = serializers.CharField(source="course.title") # 跨表查询其他字段 img = serializers.CharField(source='course.course_img') level = serializers.CharField(source='course.get_level_display') # 要拿中文的 get_level_display # recommend_courses = serializers.CharField(source='recommend_courses.all') # 这种方式不会序列化 不用这种 # SerializerMethodField 处理 m2m recommends = serializers.SerializerMethodField() # 所有章节 chapters = serializers.SerializerMethodField() class Meta: model = models.CourseDetail fields = ["course","title","img","level","slogon","why","recommends","chapters"] def get_recommends(self, obj): # 获取推荐的所有课程 queryset = obj.recommend_courses.all() # 列表生成式 return [{"id":row.id,"title":row.title} for row in queryset] def get_chapters(self, obj): # queryset = models.Chapter.objects.filter(course=obj.course).all() # 正向查询 queryset = obj.course.chapter_set.all() # 反向查询 return [{"id":row.id,"num":row.num,"name":row.name} for row in queryset]