• 登录、认证、token处理、前台cookie存储token


    免费课程相关表设计

    models的设计

    from django.contrib.contenttypes.fields import GenericRelation
    class 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 ContentType
    from django.contrib.contenttypes.fields import GenericForeignKey
    class 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组件

    <div id="nav">
        <router-link to="/">主页</router-link>
        |
        <router-link to="/login">登录</router-link>
        |
        <router-link to="/course">免费课程</router-link>
        |
        <!-- 在页面中转跳路由传递参数, 传递了params参数,this.$router.params可以拿到这个参数字典 -->
        <!--<router-link :to="{'name': 'degree-course', 'params': {'id': 1, 'name': 'owen'}}">学位课程</router-link>-->
        <router-link :to="{'name': 'degree-course'}">学位课程</router-link>
    </div>

    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.py
    class 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__.py
    from .Course import CourseView
    
    # views文件夹/Course文件/CourseView
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    from api.utils import ApiResponse
    from api import models, objectjson
    
    class 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

    <template>
        <div class="course">
            <h2>免费课程</h2>
            <!--course_list是从后台获取,有多少条数据,就会渲染多少个子组件CourseView-->
            <div v-for="(course,i) in course_list" :key="course.name+i">
                <!--通过绑定属性的方式,将course传给属性course,在组件内部提高props=['course']拿到父组件的数据-->
                <CourseView :course="course"></CourseView>
            </div>
        </div>
    </template>
    
    <script>
        import CourseView from "../components/CourseView";
    
        export default {
            name: "Course",
            data: function () {
                return {
                    course_list: []
                }
            },
            components: {
                CourseView: CourseView,
            },
            mounted: function () {
                let _this = this;
                this.$ajax({
                    method: 'get',
                    url: this.$base_api + 'courses/',
                }).then(function (response) {
                    _this.course_list = response.data.results
                })
            }
        }
    </script>
    
    <style scoped>
    
    </style>

    components/CourseView.vue

    <template>
        <div class="course-view">
            <div class="box">
                <h3 @click="goDetail(course.id)">{{course.name }}</h3>
                <p>{{ course.title }}</p>
            </div>
            <img @click="goDetail(course.id)" :src="$base_api + course.image" alt="">
        </div>
    </template>
    
    <script>
        export default {
            name: "CourseView",
            props: ['course'],
            methods: {
                goDetail: function (pk) {
                    //前往详情页,在方法中,router如何完成路由的跳转
                    this.$router.push('/course-detail');
                    // 业务逻辑下,路由转跳携带参数
                    this.$router.course_id = pk;
                    window.console.log(pk)
                }
            }
        }
    </script>
    
    <style scoped>
        .course-view {
            padding: 0 200px;
            margin: 25px 0;
        }
    
        .course-view .box {
            float: left;
            width: 380px;
        }
    
        .course-view img {
            width: 400px;
            float: left;
        }
    
        .course-view:after {
            display: block;
            clear: both;
            content: '';
        }
    </style>

    views/Course.vue/CourseDetail.vue

    <template>
        <div class="course-detail">
            <h2>免费课程详情</h2>
            <p>{{ detail }}</p>
        </div>
    </template>
    
    <script>
        export default {
            name: "CourseDetail",
            data: function () {
                return {
                    detail: {},
                }
            },
            mounted: function () {
                let _this = this;
                this.$ajax({
                    method: 'get',
                    url: this.$base_api + 'course/' + this.$router.course_id + '/',
                }).then(function (response) {
                    window.console.log(response);
                    _this.detail = response.data.results;
                })
            }
        }
    </script>
    
    <style scoped>
    
    </style>

    前后台登录认证

    时区国际化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 BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    
    from api import models
    
    class 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的模块

    <template>
        <div class="login">
            <h2>登录页面</h2>
            <div class="form">
                <el-form :model="form"  label-width="100px">
                    <el-form-item label="用户名">
                        <el-input v-model="form.username"></el-input>
                    </el-form-item>
                    <el-form-item label="密码">
                        <el-input type="password" v-model="form.password" autocomplete="off"></el-input>
                    </el-form-item>
                    <el-form-item>
                        <el-button type="primary" @click="onSubmit">登录</el-button>
                    </el-form-item>
                </el-form>
            </div>
        </div>
    </template>
    
    <script>
        export default {
            name: "Login",
            data: function () {
                return {
                    form: {
                        username: '',
                        password: '',
                    }
                }
            },
            methods: {
                onSubmit() {
                    // window.console.log('aaaa');
                    let _this = this;
                    this.$ajax({
                        method: 'post',
                        url: this.$base_api + 'login/',
                        data: {
                            username: this.form.username,
                            password: this.form.password
                        }
                    }).then(function (response) {
                        window.console.log(response);
                        let token = response.data.token;
                        _this.$cookie.set('token', token);
                        _this.$router.push('/')
                    })
                }
            }
        }
    </script>
    
    <style scoped>
    
    </style>
    View Code

    router.js

    {
      path: '/login',
      name: 'login',
      component: () => import('./views/Login.vue')
            },
  • 相关阅读:
    c语言中sscanf()与sprintf()的使用
    oracle 12c 创建能用navicat 远程登录的帐号
    ubuntu14.0安装 oracle instant client(oracle 12c)
    【转】Xilinx FPGA ChipScope的ICON/ILA/VIO核使用
    ChipScope——ISE软件的抓波形操作
    【转】彻底掌握Quartus——基础篇
    千兆以太网(4):发送——ODDR原语和Wireshark抓包工具
    千兆以太网(3):发送——组建以太网心跳包
    千兆以太网(2):接收——包校验和数据筛选
    千兆以太网(1):接收——RGMII协议和IDDR原语
  • 原文地址:https://www.cnblogs.com/ShenJunHui6/p/10932732.html
Copyright © 2020-2023  润新知