3.2 settings.py
"""
Django settings for django01 project.
Generated by 'django-admin startproject' using Django 2.2.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'c(9!f^)7+fa%p0@-w_cj(d)7(d_1ku5me9v*c8kjhod((8u&zq'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
# 跨域第一步
'rest_framework',
'apps.app01',
# app配置
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'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'
]
ROOT_URLCONF = 'django01.urls'
CORS_ORIGIN_WHITELIST = (
'http://127.0.0.1:8080',
'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]
WSGI_APPLICATION = 'django01.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'booksdb',
'USER': 'root',
'PASSWORD': '1',
'HOST': '127.0.0.1',
'PORT': 3306
}
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
3.3 主路由urls.py
"""django01 URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('apps.app01.urls')),
re_path("^(?P<path>.*)/$", serve, {'document_root': settings.MEDIA_ROOT})
# 正则路由, <path>匹配任意除了换行
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)
# http://127.0.0.1:8000/media/img/xxxx.jpg
3.4 子路由urls.py
# -*- coding: utf-8 -*-
from django.urls import path
from . import views
urlpatterns = [
path('books/', views.BookView.as_view()), # 图书增删改查
]
3.5 models.py
from django.db import models
# Create your models here.
class Books(models.Model):
books_name = models.CharField(max_length=20)
public_time = models.DateField()
read = models.IntegerField()
comment = models.IntegerField()
is_delete = models.BooleanField(default=False)
class Meta:
db_table = 'books'
3.6 django01/__init__.py
import pymysql
pymysql.install_as_MySQLdb()
3.7 serializer.py
# -*- coding: utf-8 -*-
from rest_framework import serializers
from .models import Books
class BooksSerializer(serializers.ModelSerializer):
class Meta:
model = Books
fields = '__all__'
3.8 views.py
import json
from django.shortcuts import render
# Create your views here.
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import *
from .serializer import BooksSerializer
# 导包一定要有 .
class BookView(APIView):
def get(self, request):
queryset = Books.objects.filter(is_delete=False)
books_serializer = BooksSerializer(queryset, many=True)
return Response({
'books':books_serializer.data,
'code':200
})
# book_list = queryset.values('books_name', 'public_time', 'read', 'comment')
# return Response(book_list)
def post(self, request):
body_json = request.body.decode()
body_dict = json.loads(body_json)
Books.objects.create(**body_dict)
return Response(
{'msg':'添加成功', 'code':200}
)
def put(self, request):
body_json = request.body.decode()
body_dict = json.loads(body_json)
id = body_dict.get('id')
'''
books_name = models.CharField(max_length=20)
public_time = models.DateField()
read = models.IntegerField()
comment = models.IntegerField()
is_delete = models.BooleanField(default=False)
'''
book = Books.objects.filter(id=id).first()
book.books_name = body_dict.get('books_name')
book.public_time = body_dict.get('public_time')
book.read = body_dict.get('read')
book.comment = body_dict.get('comment')
book.save()
return Response(
{'msg':'修改成功', 'code':200}
)
def delete(self, request):
body_json = request.body.decode()
body_dict = json.loads(body_json)
id = body_dict.get('id')
book = Books.objects.get(id=id)
book.is_delete = True
book.save()
return Response(
{'msg':'删除成功', 'code':200}
)
4 Vue端
4.1 总目录概览
详解:
src(项目代码主要在这里)*自己创建
assets 图片等资源
components 公用组件(在很多.vue文件中都会引用)
http 封装axios请求*
view 除了公共组件,所有的.vue文件都写入*
main.js 项目运行入口(引入js组件,可以在任何.vue文件中使用)
index.html 是主页面(所有页面都嵌入,有一个index-router路由)导入css、js文件,在任何一个.vue文件中直接使用
4.2 http文件夹下(跨域)
4.2.1 http/index.js
// 阔落给的(适用)
// import axios from 'axios'
// axios.defaults.baseURL="http://192.168.56.100:8000/"
// axios.defaults.timeout = 1000000;
// axios.defaults.headers.post['Content-Type'] = 'application/json';
// axios.defaults.headers.put['Content-Type'] = 'application/json';
// axios.interceptors.request.use(
// config => {
// // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
// const token = sessionStorage.getItem("jwt_token")
// console.log(token)
// if (token){
// config.headers.Authorization = 'JWT '+ token
// }
// return config;
// },
// error => {
// return Promise.error(error);
// })
// axios.interceptors.response.use(
// // 请求成功
// res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
// // 请求失败
// error => {
// if (error.response) {
// // 判断一下返回结果的status == 401? ==401跳转登录页面。 !=401passs
// console.log(error.response)
// if(error.response.status===401){
// // 跳转不可以使用this.$router.push方法、
// // this.$router.push({path:'/login'})
// window.location.href="http://127.0.0.1:8080/#/login"
// }else{
// // errorHandle(response.status, response.data.message);
// return Promise.reject(error.response);
// }
// // 请求已发出,但是不在2xx的范围
// } else {
// // 处理断网的情况
// // eg:请求超时或断网时,更新state的network状态
// // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
// // store.commit('changeNetwork', false);
// return Promise.reject(error.response);
// }
// });
// // 封装xiaos请求 封装axios里的get
// export function axios_get(url,params){
// return new Promise(
// (resolve,reject)=>{
// axios.get(url,{params:params})
// .then(res=>{
// console.log("封装信息的的res",res)
// resolve(res.data)
// }).catch(err=>{
// reject(err.data)
// })
// }
// )
// }
// export function axios_post(url,data){
// return new Promise(
// (resolve,reject)=>{
// console.log(data)
// axios.post(url,JSON.stringify(data))
// .then(res=>{
// console.log("封装信息的的res",res)
// resolve(res.data)
// }).catch(err=>{
// reject(err.data)
// })
// }
// )
// }
// export function axios_put(url,data){
// return new Promise(
// (resolve,reject)=>{
// console.log(data)
// axios.put(url,JSON.stringify(data))
// .then(res=>{
// console.log("封装信息的的res",res)
// resolve(res.data)
// }).catch(err=>{
// reject(err.data)
// })
// }
// )
// }
// export function axios_delete(url,data){
// return new Promise(
// (resolve,reject)=>{
// console.log(data)
// axios.delete(url,{params:data})
// .then(res=>{
// console.log("封装信息的的res",res)
// resolve(res.data)
// }).catch(err=>{
// reject(err.data)
// })
// }
// )
// }
// -------------------------------------------------------------------------------------------------
// 老师上课讲的
/* eslint-disable */
// 第一步:实例化axios对象,简单封装
const axios = require('axios'); // 生成一个axios实例
axios.defaults.baseURL = 'http://192.168.56.100:8000'; // 设置请求后端的URL地址
axios.defaults.timeout = 10000; // axios请求超时时间
axios.defaults.withCredentials = true;
axios.defaults.headers['Content-Type'] = 'application/json'; // axios发送数据时使用json格式
axios.defaults.transformRequest = data => JSON.stringify(data); // 发送数据前进行json格式化
// 第二:设置拦截器
//
// 请求拦截器(当前端发送请求给后端前进行拦截)
// 例1:请求拦截器获取token设置到axios请求头中,所有请求接口都具有这个功能
// 例2:到用户访问某一个页面,但是用户没有登录,前端页面自动跳转 /login/ 页面
//
axios.interceptors.request.use(config => {
// 从localStorage中获取token
// let token = localStorage.getItem('token');
// 如果有token, 就把token设置到请求头中Authorization字段中
// token && (config.headers.Authorization = token);
return config;
}, error => {
return Promise.reject(error);
});
// 响应拦截器(当后端返回数据的时候进行拦截)
// 例1:当后端返回状态码是401/403时,跳转到 /login/ 页面
//
axios.interceptors.response.use(response => {
// 当响应码是 2xx 的情况, 进入这里
// debugger
return response.data;
}, error => {
// 当响应码不是 2xx 的情况, 进入这里
// debugger
return error
});
//
// get方法,对应get请求
// @param {String} url [请求的url地址]
// @param {Object} params [请求时携带的参数]
//
export function get(url, params, headers) {
return new Promise((resolve, reject) => {
axios.get(url, { params, headers }).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
// 第三:根据上面分装好的axios对象,封装 get、post、put、delete请求
//
// post方法,对应post请求
// @param {String} url [请求的url地址]
// @param {Object} params [请求时携带的参数]
//
export function post(url, params, headers) {
return new Promise((resolve, reject) => {
axios.post(url, params, headers).then((res) => {
resolve(res)
}).catch((err) => {
// debugger
reject(err)
})
})
}
export function put(url, params, headers) {
return new Promise((resolve, reject) => {
axios.put(url, params, headers).then((res) => {
resolve(res)
}).catch((err) => {
// debugger
reject(err)
})
})
}
export function del(url, params, headers) {
return new Promise((resolve, reject) => {
axios.delete(url, { data: params, headers }).then((res) => {
resolve(res)
}).catch((err) => {
// debugger
reject(err)
})
})
}
export default axios;
4.2.2 http/apis.js
/* eslint-disable */
// 接口信息,生成请求方法
// 引入get方法,post方法
// import {axios_get,axios_post,axios_delete,axios_put} from '../http/index'
// export const getBookList = p => axios_get("/app01/books/",p)
/* eslint-disable */
import { get, post, put, del } from './index'
export const getBookList = (params, headers) => get("/app01/books/", params, headers)
export const addBook = (params, headers) => post("/app01/books/", params, headers)
export const editBook = (params, headers) => put("/app01/books/", params, headers)
export const delBook = (params, headers) => del("/app01/books/", params, headers)
4.3 组件
4.3.1 组件总思路
4.3.2 components/BookEdit.vue
<template>
<div>
<el-dialog title="新增图书" :visible="visible" >
<div>
<span>图书名称:</span>
<el-input class='elinput' v-model="data.books_name" ></el-input>
</div>
<div>
<span>发布日期:</span>
<el-input class='elinput' v-model="data.public_time" ></el-input>
</div>
<div>
<span>阅读量:</span>
<el-input class='elinput' v-model="data.read" ></el-input>
</div>
<div>
<span>评论量:</span>
<el-input class='elinput' v-model="data.comment" ></el-input>
</div>
<el-button @click="cancel">取 消</el-button>
<el-button type="primary" @click="addBook" >确 定</el-button>
</el-dialog>
</div>
</template>
<script>
export default {
props: ['data', 'visible'],
data() {
return {
}
},
methods: {
addBook(){
this.$emit('update:visible', false)
this.$emit('save')
},
cancel(){
this.$emit('update:visible', false)
}
},
created() {
}
}
</script>
<style scoped>
.elinput {
220px;
height: 40px;
}
</style>
4.4 主页面
4.4.1 viewsooksBooks.vue
<template>
<div>
<h1>图书管理系统</h1>
<div style="margin: 30px;">
<button @click="addNew">新增图书</button>
<BookEdit
v-show='dialogVisible'
:visible.sync='dialogVisible'
:data='editData'
@save='save' >
</BookEdit>
</div>
<div>
<table style="margin:0 auto">
<tr>
<th>图书编号</th>
<th>图书名字</th>
<th>出版时间</th>
<th>阅读数</th>
<th>评论数</th>
<th>操作</th>
</tr>
<tr v-for="(book, index) in books_list" :key="index">
<td>{{book.id}}</td>
<td>{{book.books_name}}</td>
<td>{{book.public_time}}</td>
<td>{{book.read}}</td>
<td>{{book.comment}}</td>
<td>
<button @click="edit(index)">修改</button>
<button @click="del(index)">删除</button>
</td>
</tr>
</table>
</div>
</div>
</template>
<script>
import { getBookList, addBook, editBook, delBook } from '@/http/apis'
import BookEdit from '@/components/BookEdit'
export default {
components: {
BookEdit
},
data() {
return {
dialogVisible: false,
books_list:[],
editData: {
// 编辑的内容
books_name: "",
public_time: "",
read: 100,
comment: 0
}
}
},
methods: {
// 1.点击新增图书时初始化数据
addNew() {
this.editData = {
// 初始化 编辑内容
books_name: "",
public_time: "",
read: '',
comment: ''
}
this.dialogVisible = true
// 显示弹框
},
// 2.获取图书列表,获取数据
get(){
getBookList().then((data) =>{
this.books_list = data.books
console.log(this.books_list)
})
},
// 3.修改或者添加图书
save() {
// 根据editData中的id判断是更新还是新增
// debugger
console.log(this.editData)
if (this.editData.id) {
// 如果有id, 修改图书
// 修改请求
let params = this.editData
editBook(params).then(res=>{
console.log(res)
this.get()
})
}
else {
// 增加图书
addBook(this.editData).then(res => {
this.get()
})
}
},
// 点击修改弹出修改页面
edit(index) {
this.editData = JSON.parse(JSON.stringify(this.books_list[index]))
// 复制 this.books_list[index] 的数据
// this.editData = this.books_list[index]
this.dialogVisible = true
},
// 删除
del(index) {
let params = {
id: this.books_list[index].id
}
delBook(params).then(res=>{
console.log(res)
this.get()
})
}
},
created() {
this.get()
}
}
</script>
<style scoped>
</style>
4.4.2 修饰符问题
vue修饰符sync的功能是:当一个子组件改变了一个prop的值时,这个变化也会同步到父组件中所绑定。如果我们不用.sync,我们想做上面的那个弹窗功能,我们也可以props传初始值,然后事件监听,实现起来也不算复杂。