免费课程相关表设计
models的设计
from django.contrib.contenttypes.fields import GenericRelationclass Course(models.Model): name = models.CharField(verbose_name="课程名", max_length=32) title = models.CharField(verbose_name="课程简介", max_length=128, null=True, blank=True) # 存放media文件夹下图片的相对路径:eg: media/python入门.jpeg image = models.CharField(verbose_name="海报", max_length=64, null=True, blank=True) # 用户连表查询,不会在数据库中产生字段 price_policy = GenericRelation(to='PricePolicy') def __str__(self): return self.name class Meta: verbose_name_plural = "免费课程"class CourseDetail(models.Model): level = models.IntegerField(default=1, choices=((0, '初级'), (1, "中级"), (2, "高级"))) movie_url = models.CharField(max_length=64) info = models.TextField() course = models.OneToOneField(to='Course', null=True, on_delete=models.SET_NULL, db_constraint=False) teacher = models.OneToOneField(to='Teacher', null=True, on_delete=models.SET_NULL, db_constraint=False) def __str__(self): return self.course.name + '的详情' class Meta: verbose_name_plural = "免费课程详情"class Teacher(models.Model): name = models.CharField(max_length=32) info = models.TextField() image = models.CharField(max_length=64) level = models.IntegerField(default=0, choices=((0, '初级讲师'), (1, "金牌讲师"), (2, "特约讲师"))) def __str__(self): return self.name class Meta: verbose_name_plural = "讲师"class DegreeCourse(models.Model): name = models.CharField(max_length=32) price_policy = GenericRelation(to='PricePolicy') def __str__(self): return self.name class Meta: verbose_name_plural = "学位课程"from django.contrib.contenttypes.models import ContentTypefrom django.contrib.contenttypes.fields import GenericForeignKeyclass PricePolicy(models.Model): day = models.IntegerField() price = models.CharField(max_length=8) object_id = models.IntegerField() content_type = models.ForeignKey(to=ContentType, null=True) # 改操作只用于ContentType连表查询,不会产生表字段, 就是关联课程表的对象 model_obj = GenericForeignKey() def __str__(self): return "%s:(%s天 ¥:%s)" % (self.model_obj.name, self.day, self.price) class Meta: verbose_name_plural = "价格策略"
前台路由与页面
APP.vue组件
router.js路由配置
# 在views文件夹中建立对应的组件{ path: '/login', name: 'login', component: () => import('./views/Login.vue')},{ path: '/course', name: 'course', component: () => import('./views/Course.vue')},{ path: '/degree-course', name: 'degree-course', component: () => import('./views/DegreeCourse.vue')},{ path: '/course-detail', name: 'course-detail', component: () => import('./views/CourseDetail.vue')}
项目开发视图与响应的二次封装
# api.utils.pyclass ApiResponse: def __init__(self, status=0, message='ok'): self.status = status self.message = message # 要返回给前台的api字典 @property def api_dic(self): return self.__dict__
# 在api应用文件夹下创建包views# 1.分文件管理不同的视图类,视图类继承ModelViewSet管理一系列视图函数# 2.在__ini__文件中统一管理到 视图类# __init__.pyfrom .Course import CourseView# views文件夹/Course文件/CourseViewfrom rest_framework.viewsets import ModelViewSetfrom rest_framework.response import Responsefrom api.utils import ApiResponsefrom api import models, objectjsonclass CourseView(ModelViewSet): def get(self, request, *args, **kwargs): api_response = ApiResponse() course_list = models.Course.objects.all() course_data = objectjson.CourseJson(course_list, many=True).data api_response.results = course_data return Response(api_response.api_dic)
免费课程首页展示
main.js配置后台请求根路径
// 后台根接口Vue.prototype.$base_api = 'http://127.0.0.1:8000/';
views/Course.vue
免费课程
components/CourseView.vue
{ {course.name }}
{
{ course.title }}
views/Course.vue/CourseDetail.vue
免费课程详情
{
{ detail }}
前后台登录认证
时区国际化settings.py
TIME_ZONE = 'Asia/Shanghai'USE_TZ = False
models.py
class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32)class UserToken(models.Model): token = models.CharField(max_length=64) user = models.OneToOneField(to='User', null=True, on_delete=models.SET_NULL, db_constraint=False) update_time = models.DateTimeField(auto_now=True) failed_time = models.FloatField(default=10)
objectson.py
class UserJson(serializers.ModelSerializer): class Meta: model = models.User fields = '__all__'class UserTokenJson(serializers.ModelSerializer): class Meta: model = models.UserToken fields = '__all__'
auth.py
from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom api import modelsclass LoginAuthenticate(BaseAuthentication): def authenticate(self, request): token = request.META.get('HTTP_TOKEN') result = models.UserToken.objects.filter(token=token).first() # type:models.UserToken if result: # 最近一次登录的时间 before_time = result.update_time # 过期时长秒数 failed_time = result.failed_time # 当你时间往前推演到过期的时长前的最后时刻 import datetime time_difference = datetime.datetime.now() - datetime.timedelta(seconds=failed_time) import time # 存在时区问题,通过国际化解决 before_time_stamp = time.mktime(before_time.timetuple()) # 拿到时间戳 time_difference_stamp = time.mktime(time_difference.timetuple()) if before_time_stamp >= time_difference_stamp: return result.user, token raise AuthenticationFailed('登录过期') else: raise AuthenticationFailed('认证失败')
前台安装操作cookie的模块
登录页面
登录
{ path: '/login', name: 'login', component: () => import('./views/Login.vue') },