• 路飞学成项目前后端分离/API接口


    路飞学城项目之前后端分离

    前后端故名思议就是前端和后端分离开来,一个人写前端,一个人写后端.

    前后端都是要基于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
    modles.py表结构

    里面涉及了多对多,一对多,还有choice字段,基本涵盖了大多数字段的类型

    url.py

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/(?P<version>w+)/', include('api.urls')),
    ]
    主url.py
    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'})),
    
    ]
    分发的url

    获取所有的课程表

    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)
    views.py获取课程与课程详细

    跨域请求处理

    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
    cors.py

    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',
    ]
    注册cors.py

    序列化组建的使用

    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]
    serializers

    自定义字段需要写在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]    与上述的思路一样,这里不做赘述
    View Code

    前端数据的接收

    目录结构

    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/>'
    })
    main.js

    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'
    })
    index.js

    course.vue

    <template>
      <div>
        <h1>课程列表</h1>
        <!--<ul v-for="row in courseList">-->
          <!--&lt;!&ndash;<li><router-link to="/detail">{{row.title}}</router-link></li>&ndash;&gt;-->
          <!--<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>
    course.vue

    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>
    detail.vue

    补充ORM跨表

    FK
    反向查询,表名小写+_set.all()

    可以用反向字段

    OnetoOne字段
    反向查询 表明小写就可以进行跨表

    I can feel you forgetting me。。 有一种默契叫做我不理你,你就不理我

  • 相关阅读:
    1
    最大子串
    线段树
    mybatis分页插件
    springmvc下载文件
    获“领跑衫”感言
    finnal 评论 II
    用户使用报告
    事后诸葛亮会议 (尸体解剖)
    final阶段成员贡献分
  • 原文地址:https://www.cnblogs.com/weidaijie/p/10435745.html
Copyright © 2020-2023  润新知