路飞学城项目之前后端分离
前后端故名思议就是前端和后端分离开来,一个人写前端,一个人写后端.
前后端都是要基于restful协议进行的.那么什么是resful协议?
restful协议
后端主要是写接口,所谓的接口就是url,前端用ajax技术发送请求给后端,向后端拿想要的数据
而后端只需要返回json数据即可.
用Django的restframework框架写API接口
提供简单的model
model
class Course(models.Model):
'''课程表'''
Course_level=(
(0,'低级'),
(1,'中级'),
(2,'高级'),
)
title=models.CharField(max_length=32,verbose_name='课程')
img_src=models.CharField(max_length=64,default='static/images/logo.png')
level=models.PositiveIntegerField(choices=Course_level,default=1)
def __str__(self):
return self.title
class CourseDetail(models.Model):
'''课程详细表'''
name=models.CharField(max_length=255,verbose_name='口号')
course=models.OneToOneField('Course',on_delete=models.CASCADE,related_name='c')
why_study=models.TextField(verbose_name='为什么学习')
recommend_course=models.ManyToManyField('Course',related_name='course_recomend',verbose_name='推荐课程',null=True,blank=True)
def __str__(self):
return self.name
class Chapter(models.Model):
'''章节'''
number_chapter=models.IntegerField(verbose_name='第几章节')
title=models.CharField(max_length=32,verbose_name='章节名称')
chapter_course=models.ForeignKey('Course',on_delete=models.CASCADE)
def __str__(self):
return '第%s章节%s'%(self.number_chapter,self.title)
里面涉及了多对多,一对多,还有choice字段,基本涵盖了大多数字段的类型
url:
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^api/(?P<version>\w+)/', include('api.urls')),
]
urlpatterns = [
path('course/',course.CourseView.as_view({'get':'list'})),
re_path(r'^course/(?P<pk>\d+)/$',course.CourseView.as_view({'get':'retrieve',
'put':'update',
'delete':'destroy'})),
]
获取所有的课程表
view:
class CourseView(ViewSetMixin,APIView):
def list(self,request,*args,**kwargs):
'''查看课程列表'''
ret={'code':1000,'data':None}
try:
queryset=models.Course.objects.all()
ser=apiserializer.CourseModelSerializer(instance=queryset,many=True)
ret['data']=ser.data
except Exception as e:
ret['code']=1001
ret['data']='获取数据错误'
return Response(ret)
再来看序列化组件:
class CourseModelSerializer(ModelSerializer):
'''Course表的序列化'''
level=serializers.CharField(source='get_level_display')
class Meta:
model=models.Course
fields=['id','title','img_src','level']
# 不建议用,因为有太多的无用信息
#depth=1 #0-10
自定义字段需要写在fields里
get_level_display是取choice的中文名称
自定义字段后面会详细说明
前端vue接收数据:
main.js:
import axios from 'axios'
import Vuex from 'vuex'
Vue.use(Vuex);
Vue.prototype.$axios = axios;
const store = new Vuex.Store({
state: {
allCourseList:[],
//这里面的状态跟每个组件的数据属性有关系
},
mutations: {
GETCOURSELIST(state,newValue){
state.allCourseList =newValue;
},
},
actions:{
GetCourseList(context,){
axios.request({
url:'http://127.0.0.1:8000/api/v2/course/',
method:'GET',
}).then(function(ret){
context.commit('GETCOURSELIST',ret.data.data)
}).catch(function(ret){
})
},
},
});
main.js
在初始化course组件的时候发送请求接收数据:
Vcourse.vue
mounted(){
this.$store.dispatch('GetCourseList');
}
View Code
在VcourseList.vue里获取到state里的数据然后传给子vue
<template>
<ul>
<VcourseItem v-for="item in GetAllList" :data="item"></VcourseItem>
</ul>
</template>
<script>
import VcourseItem from './VcourseItem'
export default {
name: "VcourseList",
data() {
return {}
},
components:{
VcourseItem,
},
computed:{
GetAllList(){
return this.$store.state.allCourseList
},
}
}
</script>
<style scoped>
</style>
View Code
VcourseItem.vue
<template>
<li>
<router-link :to="{name:'detail',params:{id:data.id}}">
<img :src="'/'+data.img_src" alt="">
<div>{{data.title}}</div>
</router-link>
<div>难度{{data.level}}</div>
</li>
</template>
<script>
export default {
name: "VcourseItem",
data() {
return {
}
},
props:{
data:Object,
},
}
</script>
<style scoped>
</style>
View Code
props
这里需要说明的是,父vue传数据到子vue,进行验证
props:{
data:Object}
props所有的数据类型
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object
}
可以看到vue里有跳转到查看详细的router-link
<router-link :to="{name:'detail',params:{id:data.id}}">
{
path: '/detail/:id',
name: 'detail',
component: Vdetail
},
如何序列化有外键的字段
view:
def retrieve(self,request,*args,**kwargs):
'''查看课程详细'''
ret={'code':1000,'data':None}
try:
pk=kwargs.get('pk')
obj=models.CourseDetail.objects.filter(course_id=pk).first()
ser=apiserializer.CourseDetailModelSerializer(instance=obj)
ret['data']=ser.data
except Exception as e:
ret['code'] = 1001
ret['error'] = '获取数据错误'
return Response(ret)
具体操作
前面介绍了前后端分离的整个过程,那么这里主要介绍对 对多对,一对多,反向查询的序列化
- 对于正向查询的外键:
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] 与上述的思路一样,这里不做赘述
class CourseDetailModelSerializer(ModelSerializer):
'''CourseDetail'''
title=serializers.CharField(source='course.title')
img=serializers.CharField(source='course.img_src')
level=serializers.CharField(source='course.get_level_display')
recommend_course=serializers.SerializerMethodField()
chapter=serializers.SerializerMethodField()
def get_recommend_course(self, obj):
queryset=obj.recommend_course.all()
return [{'title':row.title,'level':row.get_level_display()} for row in queryset]
def get_chapter(self,obj):
queryset=obj.course.chapter_set.all()
return [{'num':obj.number_chapter,'chater':obj.title} for obj in queryset]
class Meta:
model=models.CourseDetail
fields=['id','name','why_study','title','img','level','chapter','recommend_course']
补充ORM跨表和构建列表:
1.ORM
FK
反向查询,表名小写+_set.all()可以用反向字段
OnetoOne字段
反向查询 表明小写就可以进行跨表
数据结构构建补充:
ret=[{'title':'123','msg':'456'},{'title':'789','msg':'000'},]
new=[{'title':row['title']} for row in ret]
print(new)
[{'title': '123'}, {'title': '789'}]