路飞学城项目之前后端分离
前后端故名思议就是前端和后端分离开来,一个人写前端,一个人写后端.
前后端都是要基于restful协议进行的
后端主要是写接口,所谓的接口就是url,前端用ajax技术发送请求给后端,向后端拿想要的数据
而后端只需要返回json数据即可.
用Django的restframework框架写API接口
后端部分
表结构
1 from django.db import models 2 3 class Course(models.Model): 4 """ 5 课程表 6 """ 7 title = models.CharField(verbose_name='课程名称',max_length=32) 8 course_img = models.CharField(verbose_name='课程图片',max_length=64) 9 level_choices = ( 10 (1,'初级'), 11 (2,'中级'), 12 (3,'高级'), 13 ) 14 level = models.IntegerField(verbose_name='课程难易程度',choices=level_choices,default=1) 15 16 def __str__(self): 17 return self.title 18 19 class CourseDetail(models.Model): 20 """ 21 课程详细 22 """ 23 course = models.OneToOneField(to='Course') 24 slogon = models.CharField(verbose_name='口号',max_length=255) 25 why = models.CharField(verbose_name='为什么要学?',max_length=255) 26 recommend_courses = models.ManyToManyField(verbose_name='推荐课程',to='Course',related_name='rc') 27 28 def __str__(self): 29 return "课程详细:"+self.course.title 30 31 class Chapter(models.Model): 32 """ 33 章节 34 """ 35 num = models.IntegerField(verbose_name='章节') 36 name = models.CharField(verbose_name='章节名称',max_length=32) 37 course = models.ForeignKey(verbose_name='所属课程',to='Course') 38 39 def __str__(self): 40 return self.name
里面涉及了多对多,一对多,还有choice字段,基本涵盖了大多数字段的类型
url.py
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>w+)/', include('api.urls')), ]
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'})), ]
获取所有的课程表
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() ser = CourseSerializer(instance=queryset,many=True) 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: # 课程ID=2 pk = kwargs.get('pk') # 课程详细对象 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)
跨域请求处理
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response 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
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', ]
序列化组建的使用
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): """ 课程详细序列化 """ # one2one/fk/choice title = serializers.CharField(source='course.title') img = serializers.CharField(source='course.course_img') level = serializers.CharField(source='course.get_level_display') # m2m recommends = serializers.SerializerMethodField() chapter = serializers.SerializerMethodField() class Meta: model = models.CourseDetail fields = ['course','title','img','level','slogon','why','recommends','chapter'] def get_recommends(self,obj): # 获取推荐的所有课程 queryset = obj.recommend_courses.all() return [{'id':row.id,'title':row.title} for row in queryset] def get_chapter(self,obj): # 获取推荐的所有课程 queryset = obj.course.chapter_set.all() return [{'id':row.id,'name':row.name} for row in queryset]
自定义字段需要写在fields里
get_level_display是取choice的中文名称
fk、onetoone、choice可以通过source来做
关于多对多和一对多的反向查询
# m2m
recommends = serializers.SerializerMethodField()
chapter = serializers.SerializerMethodField()
class Meta:
model = models.CourseDetail
fields = ['course','title','img','level','slogon','why','recommends','chapter']
def get_recommends(self,obj): #多对多
# 获取推荐的所有课程
queryset = obj.recommend_courses.all()
return [{'id':row.id,'title':row.title} for row in queryset]
def get_chapter(self,obj): #一对多的反向查询
# 获取推荐的所有课程
queryset = obj.course.chapter_set.all()
return [{'id':row.id,'name':row.name} for row in queryset]
对多对,一对多,反向查询的序列化 对于正向查询的外键: title=serializers.CharField(source='course.title')我们只需要用序列化cource字段就可以进行跨表获取数据 对于多对多字段:: recommend_course=serializers.SerializerMethodField()首先需要实例化serializers.SerializerMethodField()类 def get_recommend_course(self, obj): #get_对象名(recommend_course) queryset=obj.recommend_course.all() return [{'title':row.title,'level':row.get_level_display()} for row in queryset] 只要return回想要的数据即可.这里的obj是view传递的obj *对于反向查询的一对多字段: chapter=serializers.SerializerMethodField() def get_chapter(self,obj): queryset=obj.course.chapter_set.all() return [{'num':obj.number_chapter,'chater':obj.title} for obj in queryset] 与上述的思路一样,这里不做赘述
前端数据的接收
目录结构

main.js
// 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=axios // 以后每个组件使用时:this.$axios Vue.prototype.$axios = axios Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
index.js
import Vue from 'vue' import Router from 'vue-router' // import HelloWorld from '@/components/HelloWorld' 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' })
course.vue
<template>
<div>
<h1>课程列表</h1>
<!--<ul v-for="row in courseList">-->
<!--<!–<li><router-link to="/detail">{{row.title}}</router-link></li>–>-->
<!--<li><router-link :to="{name:'detail',params:{id:row.id}}">{{row.title}}</router-link></li>-->
<!--</ul>-->
<div v-for="row in courseList">
<div style=" 350px;float: left">
<!--<img v-bind:src="row.course_img"/>-->
<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: "index",
data() {
return {
courseList:[
]
}
},
mounted:function () {
// vue页面刚加载时自动执行
this.initCourse()
},
methods:{
initCourse:function () {
/*
this.courseList = [
{id:1,title:'Python全栈'},
{id:2,title:'Linux运维'},
{id:3,title:'金融分析'},
]
*/
// 通过ajax向接口发送请求,并获取课程列表
// axios 发送ajax请求
// npm install axios --save
// 第一步:在main.js中配置
// 第二步:使用axios发送请求
var that = this
this.$axios.request({
url:'http://127.0.0.1:8000/api/v1/course/',
method:"GET"
}).then(function (ret) {
// ajax请求发送成功后,获取的响应内容
console.log(ret.data)
if(ret.data.code === 1000){
that.courseList = ret.data.data
}
}).catch(function (ret) {
// ajax请求失败之后,获取响应的内容
})
}
}
}
</script>
<style scoped>
</style>
detail.vue
<template>
<div>
<h1>课程详细页面</h1>
<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.chapter">
<li>{{item.name}}</li>
</ul>
</div>
<div>
<ul v-for="item in detail.recommends">
<li>{{item.title}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "index",
data() {
return {
detail:{
course:null,
img:null,
level:null,
slogon:null,
title:null,
why:null,
chapter:[],
recommends:[],
}
}
},
mounted(){
this.initDetail()
},
methods:{
initDetail(){
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 (arg) {
if(arg.data.code === 1000){
that.detail = arg.data.data
}else{
alert(arg.data.error)
}
})
}
}
}
</script>
<style scoped>
</style>
补充ORM跨表
FK
反向查询,表名小写+_set.all()
可以用反向字段
OnetoOne字段
反向查询 表明小写就可以进行跨表