Django框架 + vue框架 实现登录页面前后端分离

1、背景

 主要介绍如何使用 后端Django + 前端Vue 技术栈快速地搭建起一套 Web 项目框架。

 Vue提供前端页面展示,Django提供后端接口。实现注册、登录、主页之间跳转并保持登录状态。

 为什么使用Django和Vue?

     Django 和 Vue 都具有独特的前端优势。Django 的上下文驱动模板视图提供了从后端模型内容直接快速开发页面的功能。

     Vue 的现代 reactive 组件为在丰富的 Javascript 生态系统中构建复杂的UI提供了强大的工具。

2、环境准备

Django

        PyCharm     代码编辑器
        Python         3.7 
        Django         2.2 建议使用 conda 虚拟环境安装django,命令 conda install django=2.2
        conda          安装教程:「详解」conda 安装与使用_conda安装_ViatorSun的博客-CSDN博客
        Mysql          5.7  安装连接Mysql的库  conda install pymysql

Vue

        Vscode        代码编辑器
        node js        v18.6.0( (官网下载:Node.js 中文网)                         
        npm             8.18.0(有关Vue的模块(包括vue)我们都使用node自带的npm包管理器安装)

3、构建后端 Django 项目

3.1、创建 Django 项目

1、mac终端内执行安装命令 conda install django ;
 conda install django=2.2
2、查看 django 安装是否成功;     
python -m django --version
3、使用 django-admin 查看相关命令;        
python -m django 或者 django-admin
 4、创建后端项目(项目名为:backend_project);

第一种方式使用命令

django-admin startproject backend_project

第二种方式使用 PyCharm 创建 django 项目

备注:Previously configured interpreter 选择虚拟环境;

5、使用 PyCharm 打开项目目录,并查看 gjango 项目架构;
$ cd backend_project/
$ tree
.
|-- backend_project/ # 项目的容器。
|   |-- __init__.py # 一个空文件,告诉 Python 该目录是一个 Python 包。
|   |-- asgi.py # 一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。
|   |-- settings.py # 该 Django 项目的设置/配置。
|   |-- urls.py # 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
|   `-- wsgi.py # 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
`-- manage.py # 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
6、在 PyCharm 终端内使用命令运行 gjango 项目;
python manage.py runserver 127.0.0.1:8000
注意:127.0.0.1 让其它电脑可连接到开发服务器,8000 为端口号。如果不说明,那么端口号默认为 8000。

7、浏览器内输入 http://127.0.0.1:8000/  访问项目,此刻 django 框架 项目部署成功;

8、更改 django 项目页面渲染;

在 backend_project/backend_project 目录内创建  views.py 文件,输入以下内容;

from django.http import HttpResponse


def hello(request):
    return HttpResponse("😊😊Hello, welcome testers to the backend project!😊😊")

在 urls.py 文件里面 把默认的代码注释掉,然后输出输入以下内容;

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.hello), 
# url使用正则表达式模式匹配浏览器中的URL,把它映射到Django项目中的某个模块上
    # url('hello/', views.hello),
]

完成后,启动 Django 地址或者刷新 Django 地址,并在浏览器访问打开浏览器并访问;

注意:项目中如果代码有改动,服务器会自动监测代码的改动并自动重新载入,所以如果你已经启动了服务器则不需手动重启。

9、修改 django 项目运行方式;

在 manage.py 文件添加以下代码;

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from django.core.management.commands.runserver import Command as Runserver # 添加的包


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend_project.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    Runserver.default_addr = '127.0.0.1' # 添加本地IP
    Runserver.default_port = '8000' #添加 端口号(可支持修改)
    main()
10、使用命令在终端运行 gjango 项目;
python manage.py runserver

3.2、 sqlite3 数据库

 3.2.1、配置 sqlite3 数据库

在 settings.py 在有默认的 sqlite3 配置,但是满足不了使用情况,所以咱们就自定义 sqlite3数据库配置。把 settings.py 默认的数据库配置注释掉。粘贴内容如下。

# 自定义数据库配置
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": os.path.join(BASE_DIR, "common", "db", "auto.db").replace("\\", "/"),
        'OPTIONS': {
            'timeout': 100,
        },
        "DATETIME_INPUT_FORMATS": '%Y-%m-%d %H:%M:%S',
        'threaded': True,
    }
}
3.2.2、创建应用程序

应用程序是指创建一个独立的功能模块,用于实现特定的功能或提供特定的服务。每个 Django 项目可以由多个应用程序组成,每个应用程序具有自己的模型、视图、URL 映射和模板等组件。

创建一个新的应用程序(myapp)
运行命令: python manage.py startapp myapp

在应用程序的目录中,可以找到一个名为 models.py 的文件,用于定义应用程序的数据模型。

在应用程序的目录中,可以找到一个名为 views.py 的文件,用于处理 HTTP 请求并生成响应。

在 setings.py 内添加应用程序

注意:要登录后台,必须在工程中注册应用,即在settings.py文件中将上面创建的APP也就是 myapp 添加进来

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp'    # 新添加的应用程序
]
3.2.3、创建 sqlite3 数据库表
在 myapp/models.py 文件内编写数据模型

内容如下:

from django.db import models
import time

class Register(models.Model):
    """
    注册用户
    :param username: 用户名
    :param password: 用户密码
    :param phone: 手机号码
    :param mailbox: 邮箱地址
    :param description: 描述信息
    :param create_time: 创建时间
    :param update_time: 更新时间
    """
    username = models.CharField(
        max_length=256,
        unique=True,
        null=False,
    )
    password = models.CharField(max_length=256, null=False,)
    email = models.CharField(max_length=256, unique=True,)
    phone = models.IntegerField(
        null=True,
        unique=True,
        default=int(time.strftime("%Y%m%d%H%M%S", time.localtime())),
    )
    create_time = models.CharField(
        max_length=256,
        null=False,
        default=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
    )
    update_time = models.CharField(
        max_length=256,
        null=False,
        default=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
    )

    def __str__(self):
        return self.username

    class Meta:
        # 数据库名称
        db_table = 'tm_register_username'
# Create your models here.
admin后台注册表

修改myapp的admin.py文件:把models创建的表添加到admin后台中

 终端中输入命令:python manage.py makemigrations

在终端中输入命令:python manage.py migrate

 

第一条命令是将你对models.py文件中的改动保存到当前目录中一个叫migrations的文件夹中,但还未同步到数据库,第二条命令将改动同步到数据库。

3.3.4、查看 sqlite3 数据库

3.3.5、下载数据库代码编辑器

在 PyCharm->Preferences->Plugins 下载 Database Navigator

下载完成后,在 PyCharm 右侧出现 Database

链接数据库 

 

添加数据库后,点击刷新,然后点击数据库名称,展示数据库表名;

 默认数据库表名

auth_group: 身份验证组;
auth_group_permissions: 授权组权限;
auth_permission: 身份验证权限;
auth_user: 身份验证用户;
auth_user_groups: 身份验证用户权限;
auth_user_user_permissions: Auth user用户权限;
django_admin_log: Django管理日志;
django_content_type: Django内容类型;
django_migrations: django迁移;
django_session: django会话;
myapp_register_username: 注册用户名;
sqlite_master:
sqlite_sequence:

4、构建前端 Vue 项目

1、安装 node.js、npm,查看是否安装成功;

node –v  # 查看 nodejs 版本
npm -v # 查看 npm 版本

2、使用 npm 安装 cnpm 模块,并使用 淘宝镜像源;

npm install -g cnpm --registry=https://registry.npm.taobao.org # 安装cnpm

# 如果安装失败可以使用以下方式再次进行安装;
npm cache clean --force # 清空缓存
npm config set registry https://registry.npm.taobao.org # 设置淘宝镜像源
npm config get registry  # 配置镜像源是否配置成功
sudo npm install -g cnpm --registry=https://registry.npm.taobao.org # 输出本机密码

3、查看 cnpm,是否安装成功;

cnpm -v # 查看 cnpm 版本

输出:
cnpm@9.2.0 (/usr/local/lib/node_modules/cnpm/lib/parse_argv.js)
npm@9.8.0 (/usr/local/lib/node_modules/cnpm/node_modules/npm/index.js)
node@18.6.0 (/usr/local/bin/node)
npminstall@7.9.0 (/usr/local/lib/node_modules/cnpm/node_modules/npminstall/lib/index.js)
prefix=/usr/local 
darwin x64 20.6.0 
registry=https://registry.npmmirror.com

4、使用 npm 安装 vue-cli 脚手架工具(vue-cli是官方脚手架工具,能迅速帮你搭建起 vue 项目的框架);

sudo npm i -g @vue/cli-init 或者使用 cnpm install -g vue-cli

5、查看 vue-cli, 是否安装成功;

vue -V # 查看 vue 版本

输出:
@vue/cli 5.0.8

6、创建前端项目(项目名为:frontend_project);

vue init webpack frontend_project

输出:
? Project name frontend_project # 输入项目名称
? Project description 前端项目 # 输入项目描述
? Author tankaihua # 输入项目负责人
? Vue build standalone # 点击回车键
? Install vue-router? Yes # 是否安装 vue-router(须要它来做前端路由)
? Use ESLint to lint your code? Yes # 是否安装 ESLint
? Pick an ESLint preset Standard # 点击回车键
? Set up unit tests No # 是否需要安装测试
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm # 选择运行方式

前端项目部署成功;

7、使用 Vscode 打开项目目录;

项目目录结构:

$ cd frontend_project/
$ tree
.
|-- bulid: # 项目构建相关代码
|-- config: # 配置目录,包括端口号等
|-- node_modules: # npm加载的依赖模块
|-- src: # 开发的目录,基本上要做的事情都在这个目录里。里面包括几个目录及文件:
    |-- assets: # 放置一些图片
    |-- components: # 组件文件,可以不用
    |-- router: # 配置路由跳转
    |-- views: # 存放页面
    |-- App.vue: # 项目入口文件,我们可以把组件写在这里,而不是用components目录
    |-- main.js: # 项目核心文件
|-- static: # 静态资源目录,如图片、字体等
|-- .babelrc: # 用来设置转码规则presets和插件plugins
|-- .editorconfig: # 用来配置编码规则
|-- .gitignore: # 过滤,向git提交代码时不需要提交
|-- .postcssrc.js: # 用来兼容浏览器
|-- index.html: # 首页入口文件
|-- package-lock.json:
|-- package.json: # 项目配置文件
|-- README.md: # 项目说明文档,Markdown格式

8、打开目录的内 README.md 文档,使用 npm install 安装 依赖;

npm install # 安装框架依赖

9、依赖安装成功后,在 Vscode 终端内使用 npm run dev 运行前端项目;

如果修改默认的前端访问地址,可以在 config/index.js 内修改;

注意:修改地址后,需要重新 启动 才能生效;

10、 浏览器内输入 http://localhost:8080 访问项目,此刻 vue框架 项目部署成功;     

5、使用 Django 框架实现 注册 接口编写

修改 backend_project 的 urls.py, 实现转发功能。

from django.conf.urls import url, include
from django.urls import path
from . import views
 
urlpatterns = [
    path("user/", include('myapp.user.urls'))
]

1、业务流程分析

        1. 对参数进行校验

              1. 判断用户名是否为空,是否已存在;

              2. 判断用户名在数据库是否已存在;

2、接口设计

2.1 接口说明

类型说明
请求方法POST
URL定义/user/register/
参数格式表单(Body->from-data)

注意:POST 请求,后端接口请求要带上 @csrf_exempt 解决跨域问题。

2.2 参数说明

参数名类型是否必须描述
username字符串用户输入的用户名
password字符串用户输入的密码
email字符串用户输入的邮箱地址
phone字符串用户输入的手机号码

2.3 返回结果

{
    "用户创建成功": "admin 用户"
}

3、注册 API

1、视图

myapp/user/views.py

import logging
from addict import Dict
import json

from myapp.user.models import Register
from django.http import JsonResponse,HttpResponse
from django.views.decorators.csrf import csrf_exempt

logger = logging.getLogger('user')

@csrf_exempt
def register(request):
    """
    注册用户
    :param request:
    :return:
    """
    if request.method == "POST":
        # 是使用 JSON 格式发送的,而不是表单形式,请使用 request.body 来获取请求体,并将其转换为字典对象进行处理。
        body = request.body.decode('utf-8')
        data = json.loads(body)
        # 获取表单中的参数值
        username = data.get('username')
        password = data.get('password')
        email = data.get('email')
        phone = data.get('phone')

        # 打印参数值
        print('username:',username)
        print('password:', password)
        print('email:', email)
        print('phone:', phone)

        # 检查用户名是否已存在,Register是创建在自定义的数据库内
        if Register.objects.filter(username=username).exists():
            return JsonResponse({
                'error': '用户创建失败,'
                '{} 用户已存在'.format(username)
            })

        # body参数
        if username:
            user_data = {
                "username": username,
                "password": password,
                "email": email,
                "phone": phone,
            }

            # 创建新用户
            user = Register.objects.create(**user_data)
            user.save()

        return JsonResponse({
            'message': '用户创建成功',
            'username': username
        })
    else:
        return JsonResponse({'error': 'Invalid request method'})

2、URL

myapp/user/urls.py

from django.urls import path
from myapp.user import views
 
urlpatterns = [
    path('register', views.register, name='注册') # 生效
]

4、访问 API

1、使用 接口工具 进行访问,比如 postman、jemter、apifox 都可以;

运行 Django 项目

 python manage.py runserver

2、使用 Postman 调试注册接口

5、数据库内查询

6、调试API错误大全

1、Forbidden (CSRF cookie not set.): /register/user/

使用接口工具调用API出现: Forbidden (CSRF cookie not set.): /register/user/

就把settings.py->MIDDLEWARE的禁掉 'django.middleware.csrf.CsrfViewMiddleware'

2、Not Found: /user/register/

使用接口工具调用API出现: Page not found at /user/register/

接口调用API,最后一层不要加 /

去掉 / 后,重新调用API

3、 PackagesNotFoundError

使用 conda install 安装插件时报错

点击 https://anaconda.org 进入 conda 官网,搜索框内输入 要安装的插件名称。

按照以下步骤,插件就能安装成功啦!

 

6、使用 vue 框架实现 注册 页面展示

1、注册页面代码 

前提:需要下载 axios、element-ui

在 src/components 文件夹内创建 Register.vue 粘贴以下代码

<template>     <!-- 模版 元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用 js 实例化。-->
    <div class="registerBody"> <!--是一个通用型的流内容容器,在不使用CSS的情况下,其对内容或布局没有任何影响。-->
      <div class="registerDiv"> <!--是一个通用型的流内容容器,在不使用CSS的情况下,其对内容或布局没有任何影响。-->
          <h1 class="register-title">注册用户</h1> <!--元素呈现了六个不同的级别的标题,<h1> 级别最高,而 <h6> 级别最低。-->
          <el-form
              :model="registerForm"
              :rules="registerRules"
              label-width="100px"
              ref="registerForm"
              >

              <el-form-item
              label="用户名"
              prop="username"
              :required="true"
              >
                <el-input
                style="width: 250px"
                type="text"
                id="register_username"
                v-model="registerForm.username"
                placeholder="请输入用户名"
                maxlength="8"
                show-word-limit
                prefix-icon="el-icon-user-solid"
                clearable
                >
              </el-input>
                <!--
                  type: 类型
                  id:
                  v-model:是Vue框架的一种内置的API指令,本质是一种语法糖写法。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。”
                  placeholder: 描述信息
                  prefix-icon: icon样式
                  clearable:输入框可一键清除-->
              </el-form-item>

              <el-form-item
              label="密码"
              prop="password"
              :required="true"
              >
                <el-input
                style="width: 250px"
                type="password"
                id="register_password"
                v-model="registerForm.password"
                placeholder="请输入用户密码"
                maxlength="8"
                show-word-limit
                show-password
                prefix-icon="el-icon-key"
                >
                </el-input>
              </el-form-item>

            <el-form-item
            label="手机号码"
            prop="phone"
            :required="true"
            >
              <el-input
              style="width: 250px"
              type='text'
              id="register_phone"
              v-model="registerForm.phone"
              placeholder="请输入手机号码"
              prefix-icon="el-icon-phone"
              maxlength="11"
              show-word-limit
              oninput="this.value=this.value.replace(/\D/g,'')"
              clearable>
              </el-input>
            </el-form-item>

            <el-form-item
            label="邮箱"
            prop="email"
            :required="true"
            >
              <el-input
              style="width: 250px"
              type="email"
              id="register_mailbox"
              v-model="registerForm.email"
              placeholder="请输入邮箱地址"
              prefix-icon="el-icon-message"
              >
              </el-input>
            </el-form-item>

            <el-form-item>
              <el-button
              type="primary"
              @click="submitForm('registerForm')"
              >注册
            </el-button>
              <el-button
              type="primary"
              @click="resetForm('registerForm')"
              >重置
            </el-button>
            </el-form-item>
          </el-form> <!--是 Element UI 中的一个表单组件,用于快速创建表单并对表单数据进行校验和提交。-->
        </div>
      </div>
</template>

<script>
import { baseUrl } from '@/config/env'
import axios from 'axios'

export default {
  name: 'register',
  data  () {
    return {
      baseUrl,
      registerForm: {
        username: '',
        password: '',
        phone: '',
        email: ''
      },
      registerRules: {
        username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
        password: [{ required: true, message: '请输入用户密码', trigger: 'blur' }],
        phone: [{ required: true, message: '请输入手机号码', trigger: 'blur' }],
        email: [{ required: true, message: '请输入邮箱地址', trigger: 'blur' }]
      }
    }
  },
  methods: {
    registerUser () {
      // 创建axios实例
      const instance = axios.create({
        baseURL: this.baseUrl, // 设置baseURL为配置文件中的baseUrl
        timeout: 5000 // 设置请求超时时间
      })
      // 发送POST请求
      instance.post('/user/register', {
        username: this.registerForm.username,
        password: this.registerForm.password,
        phone: this.registerForm.phone,
        email: this.registerForm.email
      }, {
        referrerPolicy: 'no-referrer-when-downgrade'
      })
        .then(response => {
        // 请求成功处理逻辑
          console.log(response.data)
          if (response.data.error) {
            this.$message.error(response.data.error)
          } else {
            this.$message.success('用户注册成功')
          }
        })
        .catch(error => {
        // 请求失败处理逻辑
          console.log(error)
          this.$message.error('用户注册失败')
        })
    },
    submitForm (formName) {
      this.$refs[formName].validate(valid => {
        if (valid) {
          this.registerUser()// 调用注册方法
        } else {
          console.log('表单校验不通过')
        }
      })
    },
    resetForm (formName) {
      this.$refs[formName].resetFields()
    }
  }
}
</script> <!--script标签用于定义客户端脚本,比如 js。
export default是 JavaScript 的一个语法,用于导出模块的默认成员。
name就是设置当前组件对应的名称。
-->

<style scoped>
    .registerBody {
      /* 第1部分 */
      width: 150%;
      /* 宽度 */
      height: 150%;
      /* 高度 */
      background-color: #B3C0D1;
      /* CSS background-color是属性设置元素的背景颜色*/
    }
    .registerDiv {
      /* 第2部分*/
      position: absolute;
      /* 位置:绝对的*/
      top: 50%;
      /* 上图*/
      left: 50%;
      /* 左*/
      margin-top: -300px;
      /* 功能为设置元素的上外边距。所有主流浏览器都支持 margin-top 属性。*/
      margin-left: -250px;
      /* 该属性用来设定左边距的宽度*/
      width: 500px;
      /* 宽度*/
      height: 450px;
      /* 高度*/
      background: #fff;
      /* 背景*/
      border-radius: 5%;
    }
    .register-title {
      margin: 40px 0;
      /* 边缘*/
      text-align: center;
      /* 就是把HTML元素中的文本排列到中间的意思
      llef默认值,左对齐
      center居中对齐
      right右对齐
      justify两端对齐
      inherit规定应该从父元素继承 text-align 属性的值。*/
    }
    .required:before {
      /* 必输项样式*/
    content:'*';
      /* 内容*/
    color: red;
    /* 颜色*/
}
</style><!--style中的scoped表示局部作用域,该样式只针对某一个文件例如Home.vue 使用-->

前端注册代码分析

<template>:定义了前端界面的模板,包括注册表单的各个输入项和按钮。
<el-form>:使用了Element UI中的表单组件,用于创建表单并对表单数据进行校验和提交。
<el-form-item>:表单项,包括用户名、密码、手机号码和邮箱。
<el-input>:输入框组件,用于输入用户名、密码、手机号码和邮箱等信息。
<el-button>:按钮组件,包括注册按钮和重置按钮,用于提交表单和重置表单。
<script>:定义了Vue组件,包括注册页面的相关逻辑和方法。
import和export:引入了baseUrl和axios,并将baseUrl导出为全局变量。
data:定义了注册表单的数据对象,包括username、password、phone和email。
registerRules:定义了注册表单的校验规则,包括各个表单项的必填和提示信息。
methods:定义了注册页面的方法,包括registerUser、submitForm和resetForm等。
submitForm:表单提交方法,对表单进行校验后调用registerUser方法进行注册。
resetForm:重置表单方法,将表单字段重置为空。
<style scoped>:定义了组件的样式,包括注册页面的背景颜色、表单容器的位置和样式等。

在 router/index.js 导入 register.vue 或者粘贴以下代码

import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login.vue'
import Register from '../components/Register.vue'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/register',
      name: 'register',
      component: Register
    }
  ]
})

在 src/main.js 内导入 element-ui 或者 粘贴以下代码

// 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 ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

2、注册页面

3、调用API错误大全

1、调用注册API出现 【CSRS error

打开检查。API Status展示 CSRS error,Referrer Policy: strict-origin-when-cross-origin

根据提供的错误信息,是CORS(跨源资源共享)错误导致的。这种错误通常发生在浏览器阻止了来自不同源的请求。

在 Django 内下载:conda install -c conda-forge django-cors-headers 库。

在 settings.py 内配置内容如下:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders', # 添加新的
    'myapp.user'
]

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',
    'corsheaders.middleware.CorsMiddleware', # 添加新的
]

# 添加新的
CORS_ALLOWED_ORIGINS = [
    'http://localhost:8080',
]

# 添加新的
CORS_ALLOW_METHODS = [
    'GET',
    'POST',
    'PUT',
    'PATCH',
    'DELETE',
    'OPTIONS'
]

# 添加新的
CORS_ALLOW_HEADERS = [
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with'
]

添加后,注册页面重新调用。注册API不在报 CSRS error

2、 调用注册API出现 【404

打开检查。API Status展示 404,Request Method: OPTIONS

查看 Register.vue 找到后端地址,把最后一层的 / 去掉,去掉后,重新调用即可。

3、 调用注册API出现【500】

打开检查,API Status展示 500,Status Code:500 Internal Server Error

前端注册界面:

后端接口返回结果:

4、调用注册API提示注册成功,但是数据库内不展示。

在 后端注册代码内填写以下内容

    if request.method == "POST":
        # 是使用 JSON 格式发送的,而不是表单形式,请使用 request.body 来获取请求体,并将其转换为字典对象进行处理。
        body = request.body.decode('utf-8')
        data = json.loads(body)
        # 获取表单中的参数值
        username = data.get('username')
        password = data.get('password')
        email = data.get('email')
        phone = data.get('phone')

4、前端注册用户

后端数据库内查询

7、使用 Django 框架实现 登录接口编写 

1、业务流程分析

        1. 对参数进行校验

              1. 判断用户名是否为空,是否已存在;

              2. 判断用户名在数据库是否已存在;

              3. 判断用户密码是否正确;

2、接口设计

2.1 接口说明

类型说明
请求方法POST
URL定义/user/login
参数格式表单(Body->from-data)

注意:POST 请求,后端接口请求要带上 @csrf_exempt 解决跨域问题。

2.2 参数说明

参数名类型是否必须描述
username字符串用户输入的用户名
password字符串用户输入的密码

2.3 返回结果

{
    "message": "admin 登录成功"
}

3、登录 API

1、视图

myapp/user/views.py

import logging
from addict import Dict
import json

from myapp.user.models import Register
from django.http import JsonResponse,HttpResponse
from django.views.decorators.csrf import csrf_exempt

logger = logging.getLogger('user')

@csrf_exempt
def login(request):
    """
    登录用户
    :param request:
    :return:
    """

    if request.method == "POST":
        # body内容
        data = json.loads(request.body)
        username = data.get('username')
        password = data.get('password')


        # 输入body内容
        print('username:',username)
        print('password:', password)

        # 判断用户在数据库内是否存在
        if not Register.objects.filter(username=username).exists():
            return JsonResponse({
                'error':'登录失败',
                'message': '{} 用户不存在'.format(username)
            })

        # 获取用户对象
        user = Register.objects.get(username=username)

        # 验证密码
        if password != user.password:
            return JsonResponse({
                'error': '{}登录失败'.format(username),
                'message': '密码错误'
            })

        # 响应结果
        res = JsonResponse({
            'message': '{} 登录成功'.format(username)})
        return res

2、URL

myapp/user/urls.py

from django.urls import path
from myapp.user import views

urlpatterns = [
    path('login', views.login, name='登录'),
    path('register', views.register, name='注册')

4、访问 API

1、使用 接口工具 进行访问,比如 postman、jemter、apifox 都可以;

运行 Django 项目

 python manage.py runserver

2、使用 Postman 调试登录接口

8、使用 vue 框架实现 登录 页面展示 

1、注册页面代码 

前提:需要下载 axios、element-ui

在 src/components 文件夹内创建 Login.vue 粘贴以下代码

<template> <!-- 模版 -->
    <div class="loginbBody"> <!-- 第1部分 -->
        <div class="loginDiv"> <!-- 第2部分 -->
            <div class="login-content"> <!-- 第3部分 -->
                <h1 class="login-title">用户登录</h1> <!-- 标题 -->
                <el-form
                :model="loginForm"
                label-width="100px"
                :rules="loginRules"
                ref="loginForm"
                >
                <el-form-item
                  label="用户名"
                  prop="username"
                  :required="true"
                >
                <el-input
                style="width: 200px"
                type="text"
                id="login_username"
                v-model="loginForm.username"
                autocomplete="off"
                placeholder="请输入用户名"
                prefix-icon="el-icon-user-solid"
                >
              </el-input>
              </el-form-item>

              <el-form-item
              label="密码"
              prop="password"
              >
              <el-input
              style="width: 200px"
              type="password"
              id="login_password"
              v-model="loginForm.password"
              show-password
              autocomplete="off"
              size="small"
              placeholder="请输入用户密码"
              prefix-icon="el-icon-lock"
              >
            </el-input>
            </el-form-item>

            <el-form-item>
            <el-button
            type="primary"
            @click="submitForm('loginForm')"
            >登录</el-button>
            </el-form-item>

            </el-form>
            </div>
        </div>
    </div>
</template>

<script>
import { baseUrl } from '@/config/env'
import axios from 'axios'

export default {
  name: 'login',
  data () {
    return {
      baseUrl,
      loginForm: {
        username: '',
        password: ''
      },
      loginRules: {
        username: [{required: true, message: '请输入用户名', trigger: 'blur'}],
        password: [{required: true, message: '请输入用户密码', trigger: 'blur'}]
      }
    }
  },
  methods: {
    loginUser () {
      // 创建axios实例
      const instance = axios.create({
        baseURL: this.baseUrl, // 设置baseURL为配置文件中的baseUrl
        timeout: 5000 // 设置请求超时时间
      })
      // 发送POST请求
      instance.post('/user/login', {
        username: this.loginForm.username,
        password: this.loginForm.password
      }, {
        referrerPolicy: 'no-referrer-when-downgrade'
      })
        .then(response => {
        // 请求成功处理逻辑
          console.log(response.data)
          if (response.data.error) {
            this.$message.error(response.data.error)
          } else {
            this.$message.success('用户登录成功')
          }
        })
        .catch(error => {
        // 请求失败处理逻辑
          console.log(error)
          this.$message.error('用户登录失败')
        })
    },
    submitForm () {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          // 表单验证通过,执行登录操作
          this.loginUser()
        } else {
          // 表单验证未通过,不执行登录操作
        }
      })
    }
  }
}
</script>

<style scoped >
    .loginbBody {
        width: 100%;
        height: 100%;
        background-color: #B3C0D1;
    }
    .loginDiv {
        position: absolute;
        top: 50%;
        left: 50%;
        margin-top: -200px;
        margin-left: -250px;
        width: 450px;
        height: 330px;
        background: #fff;
        border-radius: 5%;

    }
    .login-title {
        margin: 20px 0;
        text-align: center;
    }
    .login-content {
        width: 400px;
        height: 250px;
        position: absolute;
        top: 25px;
        left: 25px;
    }
    .required:before {
      /* 必输项样式*/
    content:'*';
      /* 内容*/
    color: red;
    /* 颜色*/
    }
</style>

前端注册代码分析

<template>:定义了前端界面的模板,包括注册表单的各个输入项和按钮。
<el-form>:使用了Element UI中的表单组件,用于创建表单并对表单数据进行校验和提交。
<el-form-item>:表单项,包括用户名、密码、手机号码和邮箱。
<el-input>:输入框组件,用于输入用户名、密码、手机号码和邮箱等信息。
<el-button>:按钮组件,包括注册按钮和重置按钮,用于提交表单和重置表单。
<script>:定义了Vue组件,包括注册页面的相关逻辑和方法。
import和export:引入了baseUrl和axios,并将baseUrl导出为全局变量。
data:定义了注册表单的数据对象,包括username、password、phone和email。
registerRules:定义了注册表单的校验规则,包括各个表单项的必填和提示信息。
methods:定义了注册页面的方法,包括registerUser、submitForm和resetForm等。
submitForm:表单提交方法,对表单进行校验后调用registerUser方法进行注册。
resetForm:重置表单方法,将表单字段重置为空。
<style scoped>:定义了组件的样式,包括注册页面的背景颜色、表单容器的位置和样式等。

在 router/index.js 导入 login.vue 或者粘贴以下代码

import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login.vue'
import Register from '../components/Register.vue'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'register',
      component: Register
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    }
  ]
})

在 src/main.js 内导入 element-ui 或者 粘贴以下代码

// 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 ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

2、注册页面

3、前端登录用户

9、后端项目配置 日志输出

1、在settings.py内配置 日志 路径

# 当前项目根目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 日志路径
BASE_LOG_DIR = os.path.join(BASE_DIR,'logs')

# 日志格式
LOGGING = {
    "version": 1,
    "disable_existing_loggers": True,
    "formatters": {
        "standard": {
            "format":
            "[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]"
            "[%(levelname)s] [%(message)s]"
        },
        "simple": {
            "format":
            "[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d] %(message)s"
        },
        "specification": {
            "format": "%(message)s"
        },
    },
    "filters": {
        "require_debug_true": {
            "()": "django.utils.log.RequireDebugTrue"
        }
    },
    "handlers": {
        "console": {
            "level": "DEBUG",
            "filters": ["require_debug_true"],
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
        "default": {
            "level": "INFO", # 输出标准日志
            "class": "logging.handlers.RotatingFileHandler",
            "filename": os.path.join(BASE_LOG_DIR, "TEST_PLAT_INFO.log"),
            "maxBytes": 1024 * 1024 * 50,
            "backupCount": 3,
            "formatter": "simple",
            "encoding": "utf-8",
        },
        "error": {
            "level": "ERROR",
            "class": "logging.handlers.RotatingFileHandler",
            "filename": os.path.join(BASE_LOG_DIR, "TEST_PLAT_ERROR.log"),
            "maxBytes": 1024 * 1024 * 50,
            "backupCount": 5,
            "formatter": "standard",
            "encoding": "utf-8",
        },
        "specification": {
            "level": "INFO",
            "class": "logging.handlers.RotatingFileHandler",
            "filename": os.path.join(BASE_LOG_DIR, "TEST_PLAT_SPECIFICATION.log"),
            "maxBytes": 1024 * 1024 * 50,
            "backupCount": 5,
            "formatter": "specification",
            "encoding": "utf-8",
        },
    },
    "loggers": {
        "autotest": {
            "handlers": ["default", "console", "error"],
            "level": "INFO",
            "propagate": True,
        },
        "user": {
            "handlers": ["default", "console", "error"],
            "level": "INFO",
            "propagate": True,
        },
        "specification": {
            "handlers": ["console", "specification"],
            "level": "INFO",
            "propagate": True,
        },
        "django.request": {
            "handlers": ["console"],
            "level": "DEBUG",
            "propagate": True,
        },
        "django.request.get_host()": {
            "handlers": ["console"],
            "level": "DEBUG",
            "propagate": True,
        },
        "django.request.path": {
            "handlers": ["console"],
            "level": "DEBUG",
            "propagate": True,
        },
    },
}

2、在 后端登录代码内添加日志

import logging
import json

from myapp.user.models import Register
from django.http import JsonResponse,HttpResponse
from django.views.decorators.csrf import csrf_exempt

logger = logging.getLogger('user')

@csrf_exempt
def login(request):
    """
    登录用户
    :param request:
    :return:
    """
    if request.method == "POST":
        # body内容
        data = json.loads(request.body)
        username = data.get('username')
        password = data.get('password')

        # 添加日志
        logger.info("开始登录用户: {}".format(username))

        # 输入body内容
        print('username:',username)
        print('password:', password)

        # 判断用户在数据库内是否存在
        if not Register.objects.filter(username=username).exists():
            # 添加日志
            logger.error("用户不存在: {}".format(username))

            return JsonResponse({
                'error':'登录失败',
                'message': '{} 用户不存在'.format(username)
            })

        # 获取用户对象
        user = Register.objects.get(username=username)

        # 验证密码
        if password != user.password:
            # 添加日志
            logger.error("密码错误: {}".format(username))

            return JsonResponse({
                'error': '{}登录失败'.format(username),
                'message': '密码错误'
            })

        # 添加日志
        logger.info("用户登录成功: {}".format(username))

        # 响应结果
        res = JsonResponse({
            'message': '{} 登录成功'.format(username)})
        return res

3、在后端项目内配置 logs 文件夹

4、调用接口,输出日志

10、构建前端镜像

1、打包前端代码

使用Vue的构建命令将前端代码打包到一个目录中。在项目根目录下运行以下命令

npm run build
这将生成一个"dist"文件夹,其中包含构建后的静态文件。

2、创建Nginx配置文件

在项目根目录下创建一个名为"nginx.conf"的文件,并添加以下内容:

server {
    listen 80;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

3、创建Dockerfile

在项目根目录下创建一个名为"Dockerfile"的文件,并添加以下内容:

# 使用一个基础的Nginx镜像
FROM nginx:1.21.1-alpine

# 删除默认的Nginx配置文件
RUN rm -rf /etc/nginx/conf.d/*

# 将自定义的Nginx配置文件复制到容器中
COPY nginx.conf /etc/nginx/conf.d/

# 将构建后的静态文件复制到Nginx的默认静态文件目录
COPY dist/ /usr/share/nginx/html

# 暴露容器的8080端口
EXPOSE 8080

# 启动Nginx服务器
CMD ["nginx", "-g", "daemon off;"]

4、构建镜像

在终端中内到Dockerfile所在的目录,并运行以下命令构建镜像:

docker build -t frontend_project .

5、运行容器

完成镜像构建后,可以使用以下命令来运行容器:

docker run --name frontend_project_8080 -d -p 8080:80 frontend_project

6、访问前端页面

11、构建后端镜像

1、导出项目依赖

在项目根目录下运行以下命令

pip freeze > requirements.txt

2、创建Dockerfile

# 使用官方的Python镜像作为基础镜像
FROM python:3.8

# 设置工作目录
WORKDIR /app

# 复制 requirements.txt 文件到容器中
COPY requirements.txt .

# 升级 pip
RUN pip install --upgrade pip

# 安装项目依赖
RUN pip install -r requirements.txt -i https://pypi.org/simple/

# 将项目文件拷贝到容器中
COPY . .

# 启动 Django 服务器
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

3、构建镜像

在终端中内到Dockerfile所在的目录,并运行以下命令构建镜像:

docker build -t backend_project .

4、运行容器

完成镜像构建后,可以使用以下命令来运行容器:

docker run --name backend_project -d -p 8000:8000 backend_project

5、postman调用登录接口

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值