Djnaog 之路飞学城前后端分离/api接口
路飞学城项目之前后端分离
前后端故名思议就是前端和后端分离开来,一个人写前端,一个人写后端.
前后端都是要基于restful协议进行的.那么什么是resful协议?
后端主要是写接口,所谓的接口就是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){
})
},
},
});
在初始化course组件的时候发送请求接收数据:
Vcourse.vue
mounted(){
this.$store.dispatch('GetCourseList');
}
在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>
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>
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'}]