django+vue快速入门1

1、创建django项目、创建一个app应用

此处略过,巴拉巴拉巴拉,我的示例里面app命名为app01

添加到注册表,加入到 settings.py 文件中的 INSTALLED_APPS 列表里

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config'
]

2、配置数据库引擎

配置 settings.py 文件,配置 Mysql 数据库引擎

DATABASES = {
    'default': {
        // 固定这么写
        'ENGINE': 'django.db.backends.mysql',
        // 数据库的一些连接信息
        'NAME': 'test01',
        'USER': 'root',
        'PASSWORD': '123',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}

并且安装mysqlclient库

pip install mysqlclient

3、建表、同步迁移

在app01里面的model.py里面建表

from django.db import models

# Create your models here.


class UserInfo(models.Model):
    name = models.CharField(verbose_name='姓名', max_length=10)
    phone = models.IntegerField(verbose_name='电话')

    def __unicode__(self):
        return self.name

    def __str__(self):
        return self.name

同步到数据库

python manage.py makemigrations
python manage.py migrate

4、新增接口

在app01 views.py新增接口

import json
from aip import AipNlp
from django.shortcuts import render, HttpResponse, redirect
from django.http import JsonResponse
from django.contrib.auth import authenticate, login, logout
from django.core.paginator import Paginator
import random
from app01.models import *


# Create your views here.


def index(request):
    return HttpResponse('I am your father')


def user_list(request):
    if request.method != 'GET':
        return JsonResponse({'result': 1, 'msg': 'the method you request is not correct'})
    name = request.GET.get('name', '')
    // 分页
    page = request.GET.get('page', 1)
    pageSize = request.GET.get('pageSize', 10)
    // 模糊搜索
    if name:
        queryset = UserInfo.objects.filter(name__contains=name)
    else:
        queryset = UserInfo.objects.all()
    paginator = Paginator(queryset.values(), pageSize)
    users = paginator.get_page(page)
    res = list(users)
    return JsonResponse({'result': 0, 'msg': 'success', 'data': res, 'total': paginator.count})

在根目录urls.py下面添加路由

5、创建前端项目

使用 node 自带的 npm 包管理器安装 vue 和相关模块。推荐使用淘宝的 cnpm 命令行工具代替默认的 npm。

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

安装vue.js

cnpm install -g vue

安装vue-cli

cnpm install -g vue-cli

在项目根目录下,新建一个前端工程目录

vue-init webpack frontend

在创建项目的过程中会弹出一些与项目相关的选项需要回答,按照真实情况进行输入即可,如下:

Project name(工程名):回车
Project description(工程介绍):回车
Author:作者名 :回车
Vue build ==> (是否安装编译器)runtime-compiler、 runtime-only 都是打包方式,第二个效率更高;
Install vue-router ==> 是否要安装 vue-router,项目中肯定要使用到路由,所以Y 回车;
Use ESLint to lint your code ==> 是否需要ESLint检测代码,目前我们不需要所以 n 回车;
Set up unit tests ==> 是否安装 单元测试工具 目前我们不需要 所以 n 回车;
Setup e2e tests with Nightwatch ==>是否需要端到端测试工具目前我们不需要所以n回车;
Should we run npm install for you after the project has been created? (recommended) (Use arrow keys)==> 安装依赖npm install
回车;

安装 vue 依赖模块

cd frontend
cnpm install
cnpm install vue-resource
cnpm install element-ui

当前目录结构如下

在 frontend 目录 src 下包含入口文件 main.js,入口组件 App.vue 等。后缀为 vue 的是 Vue.js 框架定义的单文件组件,一个文件包含且仅包含三块内容,如下:

1. <template></template > 前端渲染的模板
2. 专为此模板写渲染逻辑的 <script></script>
3. 专为此模板写样式的 <style></style>

在 src/components 文件夹下新建一些组件,通过调用之前在 Django 上写好的 api,实现添加书籍和展示书籍信息的功能。在样式组件上我们使用了饿了么团队推出的 element-ui,这是一套专门匹配 Vue.js 框架的功能样式组件。由于组件的编码涉及到了很多 js、html、css 的知识,并不是本文的重点,因此在此只贴出部分代码。

<template>

  <div class="home">
    <h1 class="title">用户管理</h1>

    <el-row display="margin-top:10px">
      <el-input v-model="name" placeholder="请输入姓名"
                style="display:inline-table; width: 30%; float:left"></el-input>
      <el-input v-model="phone" placeholder="请输入号码"
                style="display:inline-table; width: 30%; float:left"></el-input>
      <el-button type="primary" @click="addUser()" style="float:left; margin: 2px;">新增</el-button>
    </el-row>
    <el-row display="margin-top:10px">
      <el-input v-model="searchName" placeholder="请输入姓名"
                style="display:inline-table; width: 30%; float:left"></el-input>
      <el-button type="primary" @click="searchUser()" style="float:left; margin: 2px;">搜索</el-button>
    </el-row>
    <el-row>
      <el-table :data="data" style="width: 100%" border>
        <el-table-column prop="id" label="编号" min-width="100">
          <template v-slot:default="scope"> {{ scope.row.id }}</template>
        </el-table-column>
        <el-table-column prop="name" label="姓名" min-width="100">
          <template v-slot:default="scope"> {{ scope.row.name }}</template>
        </el-table-column>
        <el-table-column prop="phone" label="电话" min-width="100">
          <template v-slot:default="scope"> {{ scope.row.phone }}</template>
        </el-table-column>
        <el-table-column label="操作" min-width="100">
          <template v-slot:default="scope">
            <el-button type="primary" @click="editUser(scope.row)" style="margin: 2px;">编辑</el-button>
            <el-button type="danger" @click="deleteUser(scope.row)" style="margin: 2px;">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="currentPage"
        :page-sizes="[5, 10, 20, 50]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        style="margin-top: 10px">
      </el-pagination>


    </el-row>

    <el-dialog title="编辑用户" :visible.sync="dialogVisible" width="30%" @close="resetForm">
      <el-form :model="editForm">
        <el-form-item label="姓名">
          <el-input v-model="editForm.name"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="editForm.phone"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitEdit()">确 定</el-button>
      </div>
    </el-dialog>

  </div>

</template>


<script>
import {MessageBox} from "element-ui";
import axios from "axios";

export default {
  name: 'home',
  data() {
    return {
      name: '',
      phone: '',
      searchName: '',
      data: [],
      dialogVisible: false,
      editForm: {
        id: '',
        name: '',
        phone: ''
      },
      total: 0,
      pageSize: 10,
      currentPage: 1,
    }
  },
  mounted: function () {
    this.showUser()
  },
  methods: {
    addUser() {
      if (this.phone === "" || this.name === "") {
        this.$message.warning("name or phone can not be blank")
      } else {
        axios.post('http://127.0.0.1:9999/user_add/',
          {
            name: this.name, phone: this.phone
          },
          {
            headers: {'Content-Type': 'application/json'}
          }
        )
          .then((response) => {
            var res = response.data
            if (res.result === 0) {
              this.showUser()
              this.$message.success(res.msg)
            } else {
              this.$message.error(res.msg)
              console.log(res['msg'])
            }
          })
          .catch((error) => {
            console.error(error);
            this.$message.error('请求失败,请检查网络连接或服务器状态')
          })
      }
    },

    showUser(page = this.currentPage) {
      axios.get('http://127.0.0.1:9999/user_list/?page=' + page + '&pageSize=' + this.pageSize)
        .then((response) => {
          var res = response.data
          console.log(res)
          if (res.result === 0) {
            this.data = res['data']
            this.total = res['total']
          } else {
            this.$message.error('查询失败')
            console.log(res['msg'])
          }
        })
        .catch((error) => {
          console.error(error);
          this.$message.error('请求失败,请检查网络连接或服务器状态')
        })
    },

    searchUser(page = this.currentPage) {
      axios.get('http://127.0.0.1:9999/user_list/?name=' + this.searchName + '&page=' + page + '&pageSize=' + this.pageSize)
        .then((response) => {
          var res = response.data
          console.log(res)
          if (res.result === 0) {
            this.data = res['data']
            this.total = res['total']
            this.$message.success("查询成功")
          } else {
            this.$message.error('查询失败')
            console.log(res['msg'])
          }
        })
        .catch((error) => {
          console.error(error);
          this.$message.error('请求失败,请检查网络连接或服务器状态')
        })
    },

    deleteUser(user) {
      MessageBox.confirm('此操作将永久删除该用户, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        axios.post('http://127.0.0.1:9999/user_delete/', {id: user.id}, {
          headers: {'Content-Type': 'application/json'}
        })
          .then((response) => {
            var res = response.data
            if (res.result === 0) {
              this.showUser()
              this.$message.success(res.msg)
            } else {
              this.$message.error(res.msg)
              console.log(res['msg'])
            }
          })
          .catch((error) => {
            console.error(error);
            this.$message.error('请求失败,请检查网络连接或服务器状态')
          })
      }).catch(() => {
        this.$message.info('已取消删除')
      });
    },

    editUser(user) {
      this.dialogVisible = true;
      console.log(this.dialogVisible);
      this.editForm = Object.assign({}, user);
      console.log(this.editForm);

    },

    submitEdit() {
      axios.post('http://127.0.0.1:9999/user_edit/', this.editForm, {
        headers: {'Content-Type': 'application/json'}
      })
        .then((response) => {
          if (response.status === 500) {
            this.$message.error('服务器错误')
          }
          var res = response.data
          if (res.result === 0) {
            this.showUser()
            this.$message.success(res.msg)
            this.dialogVisible = false;
          } else {
            this.$message.error(res.msg)
            console.log(res['msg'])
          }
        })
        .catch((error) => {
          console.error(error);
          this.$message.error('请求失败,请检查网络连接或服务器状态')
        })
    },

    resetForm() {
      this.editForm = {
        id: '',
        name: '',
        phone: ''
      };
    },

    handleSizeChange(val) {
      this.pageSize = val;
      this.showUser();
    },

    handleCurrentChange(val) {
      this.currentPage = val;
      this.showUser();
    },

  }
}
</script>


<style scoped>
.title {
  font-size: 2em;
  color: #333;
  margin-bottom: 20px;
}

.home {
  padding: 20px;
  box-sizing: border-box;
  margin-left: 300px;
}

.el-row {
  margin-bottom: 20px;
}

.el-input {
  margin-right: 10px;
}

.el-button {
  margin-right: 10px;
}

.el-table {
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.el-dialog {
  top: 20vh;
}

.dialog-footer {
  text-align: right;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>


一定要注意,在 src/router 目录的 index.js 中,把新建的 Home 组件,配置到 vue-router 路由中

import Vue from 'vue'
import Router from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Userinfo from "../views/Userinfo.vue";
import Textcheck from "../views/Textcheck.vue";
import Apitest from "../views/Apitest.vue";
import Login from "../views/login.vue";
import Lottery from "../views/Lottery.vue";

Vue.use(Router)

let router = new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      meta: { requiresAuth: true }
    },
    {
      path: '/about',
      name: 'about',
      component: About,
      meta: { requiresAuth: true }
    },
    {
      path: '/userinfo',
      name: 'userinfo',
      component: Userinfo,
      meta: { requiresAuth: true }
    },
    {
      path: '/textcheck',
      name: 'textcheck',
      component: Textcheck,
      meta: { requiresAuth: true }
    },
    {
      path: '/apitest',
      name: 'apitest',
      component: Apitest,
      meta: { requiresAuth: true }
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    },
    {
      path: '/lottery',
      name: 'lottery',
      component: Lottery,
      meta: { requiresAuth: true }
    }
  ]
})

router.beforeEach((to, from, next) => {
  // 检查路由是否需要认证
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 检查用户是否已经登录
    if (!localStorage.getItem('user')) {
      // 如果用户未登录,重定向到登录页面
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next() // 确保一定要调用 next()
    }
  } else {
    next() // 确保一定要调用 next()
  }
})

export default router


在 src/main.js 文件中,导入 element-ui、vue-resource 库

// 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 ElementUT from 'element-ui'
import VueResource from 'vue-resource'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUT)
Vue.use(VueResource)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

6、解决跨域

安装第三包

pip install django-cors-headers

修改setting.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',    # 添加1,注意中间件的添加顺序
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True        # 添加2

7、整合前后端

目前我们已经分别完成了 Django 后端和 Vue.js 前端工程的创建和编写,但实际上它们是运行在各自的服务器上,和我们的要求是不一致的。

在前端工程 frontend 目录下,输入 npm run build,如果项目没有错误的话,就能够看到所有的组件、css、图片等都被 webpack 自动打包到 dist 目录下了,里面有一个 index.html 和一个文件夹 static。

修改项目下的 urls ,使用通用视图创建最简单的模板控制器,访问 『/』时直接返回 index.html

配置 Django 项目的模板搜索路径。修改 settings.py 文件,如下: 

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        // 这里
        'DIRS': [os.path.join(BASE_DIR, 'frontend/dist')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

配置静态文件的搜索路径:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "frontend/dist/static"),
]

配置完成,启动 Django 服务 python manage.py runserver,就能够看到我们的前端页面在浏览器上展现

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值