前后端分离 项目(一)

1. 后端Django项目

目录结构:

(1)settings.py文件

# 下载 处理跨域问题的app:
pip install django-cors-headers
# 注册app:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'api.apps.ApiConfig',
    'corsheaders'  # 注册app
]
# 注册中间件:

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware', # 必须放在第一个位置
    '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',
]
# corsheaders配置项:
CORS_ORIGIN_ALLOW_ALL = True  # 允许所有人 跨域请求 我的服务端

(2)api/models.py文件

项目表结构设计

  • 课程相关

    • 课程类表CourseCategory
    • 专题课程表Course
    • 课程详情表CourseDetail
    • 老师表Teacher
    • 价格与有课程效期表PricePolicy
    • 课程章节表CourseChapter
    • 课时目录表CourseSection
    • 常见问题表OftenAskedQuestion
  • 优惠券相关

    • 优惠券生成规则Coupon
    • 优惠券发放、消费纪录CouponRecord
  • 订单相关

    • 订单Order
    • 订单详情OrderDetail
  • 用户相关

    • 用户表UserInfo -- 继承AbstractUser
    • Token

注意:settings.py文件中添加配置项

# 告诉Django用我自己写的 UserInfo表 代替内置的 User表
AUTH_USER_MODEL = 'api.UserInfo'    # app名.表名

(3)api/admin.py文件

from django.contrib import admin
from api import models

admin.site.register(models.Course)			# 课程表
admin.site.register(models.CourseDetail)	# 课程详情表
admin.site.register(models.CourseCategory)	# 课程分类表
admin.site.register(models.CourseChapter)	# 课程章节表
  • 创建超级用户
python manage.py createsuperuser
  • 启动Django项目, 进入admin页面, 对以上四个表的数据进行修改

(4)urls.py文件

路由分发:

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^api/', include('api.urls')),
]

(5)api/urls.py文件

路由匹配:

from django.conf.urls import url
from api.views import course as course_view
from api.views import login as login_view

urlpatterns = [
    # 课程展示
    url(r'courses/$', course_view.CourseListView.as_view()),
]

(6)api/views/course.py文件

from rest_framework.generics import ListAPIView
from api import models
from api.serializers.course import CourseModelSerializer
from rest_framework.response import Response

class CourseListView(ListAPIView):
    queryset = models.Course.objects.all()
    serializer_class = CourseModelSerializer
    
    def get(self, request, *args, **kwargs):
        """"重写ListAPIView的get方法"""
        pass

由于是前后端分离, 对于视图函数CourseListView, 我们要返回一个合适的接口, 因此,

必须重写父类的get方法. 这个时候, 就需要看一看ListAPIView的源码了.

  • ListAPIView源码:

  • 点击进入list方法:

现在, 我们回归到本来的问题上, 我们要重写ListAPIView的get方法, 从而返回一个合适的接口, 但是, 由于ListAPIView本身也有一些功能, 如果我们直接重写, 势必会对其他功能造成影响. 我们的解决办法是: 将list方法功能相关的代码直接粘贴到我们重写的get方法里, 然后在不改动这些代码的前提下, 进行我们自己的处理(主要是处理返回值, 即接口).

course.py文件:

from rest_framework.generics import ListAPIView
from api import models
from api.serializers.course import CourseModelSerializer
from rest_framework.response import Response

class CourseListView(ListAPIView):
    queryset = models.Course.objects.all()
    serializer_class = CourseModelSerializer
    
    def get(self, request, *args, **kwargs):
        try:
            #### list源码 ####
            queryset = self.filter_queryset(self.get_queryset())
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
            serializer = self.get_serializer(queryset, many=True)
            #### list源码 ####
            data = serializer.data
        except Exception as e:
            code = 1
            msg = str(e)
        res = {'code': code, 'data': data, 'msg': msg}
        return Response(res)

上面代码中, res这个字典就是我们与前端交互的接口. 由于每次都要自己硬编码该字典内部的key和value, 这样的做法是不可靠且极为繁琐的, 因此, 我们考虑将它们封装为一个对象. 如下所示:

  • 工具类: utils/response.py文件
class BaseResponse(object):

    def __init__(self):
        self.code = 1000  # 状态码
        self.data = None  # 具体数据
        self.msg = ''     # 提示信息

    @property
    def dict(self):
        return self.__dict__  # __dict__把类的属性以字典的形式返回

course.py文件: 改进版本

from rest_framework.generics import ListAPIView
from api import models
from api.serializers.course import CourseModelSerializer
from rest_framework.response import Response
from utils.response import BaseResponse	# 引入自定义工具类

class CourseListView(ListAPIView):
    queryset = models.Course.objects.all()
    serializer_class = CourseModelSerializer
    
    def get(self, request, *args, **kwargs):
        res_obj = BaseResponse()	# 实例化自定义工具类
        try:
            #### list源码 ####
            queryset = self.filter_queryset(self.get_queryset())
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
            serializer = self.get_serializer(queryset, many=True)
            #### list源码 ####
            res_obj.data = serializer.data	# 赋值给data属性
        except Exception as e:
            res_obj.code = 1		  # 赋值给code属性
            res_obj.msg = str(e)	  # 赋值给msg属性
        return Response(res_obj.dict) # 通过dict方法就可以拿到所有属性组成的字典

(7)api/serializers/course.py文件

自定义序列化类CourseModelSerializer:

from rest_framework import serializers
from api import models

class CourseModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Course
        fields = "__all__"

2. 前端Vue项目

(1)src/main.js文件

配置根URL:

// 导入Axios
import Axios from 'axios'
Axios.defaults.baseURL = 'http://127.0.0.1:8001/api';
Vue.prototype.$axios = Axios;

(2)src/router.js文件

路由匹配:

import Course from './views/Course.vue'

Vue.use(Router);
export default new Router({
    routes: [
                {
            path: '/course',
            name: 'course',
            component: Course,
        },
    ]
})

(3)src/views/Course.vue文件

自定义组件Course.vue:

getCourseList() {
    this.$axios.get('/courses/')    // 修改路径,要与后端的路径信息相匹配
        .then((res) => {            // res 是接收到的后段的返回值
            // console.log(res);
            if (res.data.code === 1000) {   // code状态码
                this.courseList = res.data.data;    // data具体数据信息
                // 去掉加载动画
                this.loading = false
            }
        })
},
原文地址:https://www.cnblogs.com/haitaoli/p/10300651.html