登錄、認證、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')
        },

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM