zoukankan      html  css  js  c++  java
  • ModelViewSet+ModelSerializer+Vue实现图书管理系统

    1 页面逻辑分析

    1.1 父子组件传参思路

    1.1.1 父传子
    父组件中用:data传递要传的data
    子组件中用props['data']来接收
    (子组件中可以用v-model绑定,这样就能双向绑定父组件中的data值)
    
    1.1.2 子传父
    子组件中用this.$emit('look', 'data')
    		# this.$emit('父组件中定义方法', '要传递的参数')
    
    @look="look"
    父组件中在方法中调用 look(data){
        
    }
    

    1.2 整体页面逻辑

    1.2.1 展示
    • 前端展示出所有数据

    1.2.2 修改
    • 点击修改时,在原有数据基础上进行修改(涉及得到父子组件传参)

    1.2.3 删除
    • 因为对接的是ModelViewSet,所以执行的是物理删除(没有业务逻辑,所以没有写APIView)

    1.2.4 添加
    • 在子组件上进行添加,实际上是v-model绑定的数据,数据将会写在父组件上

    2 Django端

    2.1 项目目录结构

    book_project
    ├── book
    │   ├── apps(源根目录)
    │   │   ├── bookapp
    │   │   │   ├── admin.py
    │   │   │   ├── apps.py
    │   │   │   ├── __init__.py
    │   │   │   ├── migrations
    │   │   │   │   ├── __init__.py
    │   │   │   ├── models.py
    │   │   │   ├── serializers.py
    │   │   │   ├── tests.py
    │   │   │   ├── urls.py
    │   │   │   └── views.py
    │   │   └── __init__.py
    │   ├── book
    │   │   ├── __init__.py
    │   │   ├── settings.py
    │   │   ├── urls.py
    │   │   └── wsgi.py
    │   ├── db.sqlite3
    │   ├── libs
    │   │   └── __init__.py
    │   ├── manage.py
    │   ├── static
    │   ├── templates
    │   └── utils
    │       └── __init__.py
    ├── celery_task
    │   └── __init__.py
    ├── logs
    ├── packages
    ├── README.en.md
    ├── README.md
    ├── scrips
    └── uwsgi_conf
    

    2.2 settings.py

    import os, sys
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
    
    SECRET_KEY = '@77(0ge9)z*nv62wlk$*64o5nk&)s^9av9v6oqd&94br9ms-m!'
    
    DEBUG = True
    
    ALLOWED_HOSTS = ['*']
    
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'corsheaders',
        'bookapp.apps.BookappConfig'
    ]
    
    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',
    ]
    
    # CORS_ORIGIN_ALLOW_ALL = True
    ROOT_URLCONF = 'book.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 = 'book.wsgi.application'
    
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'book_db',
            'USER': 'root',
            'PASSWORD': '1',
            'HOST': '127.0.0.1',
            'PORT': '3306'
        }
    }
    
    
    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',
        },
    ]
    
    
    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False
    
    
    STATI_URL = '/static/'
    

    2.3 urls.py(主路由)

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('book/', include('bookapp.urls'))
    ]
    

    2.4 urls.py(子路由)

    from django.urls import path
    from rest_framework.routers import DefaultRouter
    
    from bookapp import views
    
    router = DefaultRouter()
    router.register('books', views.BooksViewSet)
    urlpatterns = [
    
    ]
    urlpatterns += router.urls
    

    2.5 models.py

    from django.db import models
    
    class Books(models.Model):
        books_name = models.CharField(max_length=30)
        author = models.CharField(max_length=30)
        desc = models.TextField()
        datetime = models.DateField()
    
    

    2.6 views.py

    from rest_framework import viewsets
    
    from bookapp.models import Books
    from bookapp.serializers import BooksSerializer
    
    
    class BooksViewSet(viewsets.ModelViewSet):
        queryset = Books.objects.all()
        serializer_class = BooksSerializer
    

    3 Vue端

    3.1 项目目录结构

    book_vue
    ├─ build
    ├─ config
    ├─ node_modules
    ├─ src
    │  ├─ assets
    │  ├─ components
    │  ├─ http
    │  │  ├── apis.js
    │  │  ├── index.js
    │  ├─ router
    │  │  ├── index.js
    │  └─ views
    │     ├── components
    │  │  │  ├── ChildBook.vue
    │  │  ├── FatherBook.vue  
    │  ├── App.vue
    │  ├── main.js 
    ├── static
    ├── .babelrc
    ├── .editorconfig
    ├── .postcssrc.js
    ├── index.html
    ├── package.json
    └── README.md
    

    3.2 安装 element-ui

    cnpm i element-ui -S  # element—ui
    

    3.3 main.js

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

    3.4 http/index.js

    /* eslint-disable */
    // 第一步:实例化axios对象,简单封装
    const axios = require('axios');                         // 生成一个axios实例
    axios.defaults.baseURL = 'http://192.168.56.100:1594';  // 设置请求后端的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
    });
    
    //  第三:根据上面分装好的axios对象,封装 get、post、put、delete请求
    // 
    //  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)
            })
        })
    }
    
    // 
    //  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) => {
                reject(err)
            })
        })
    }
    export function put(url, params, headers) {
        return new Promise((resolve, reject) => {
            axios.put(url + params.id + '/', params.data, headers).then((res) => {
                // ModelViewSet 真的是个大坑,修改后面必须以 ‘/’ 结束
                resolve(res)
            }).catch((err) => {
                reject(err)
            })
        })
    }
    export function del(url, params, headers) {
        return new Promise((resolve, reject) => {
            axios.delete(url + params,  headers ).then((res) => {
                // 删除过于复杂,我直接修改成这样的了
                resolve(res)
            }).catch((err) => {
                reject(err)
            })
        })
    }
    export default axios;
    

    3.5 http/apis.js

    import { get, post, put, del } from './index'
    
    export const getBookList = (params, headers) => get("/book/books/", params, headers)
    export const addBook = (params, headers) => post("/book/books/", params, headers) 
    export const updateBook = (params, headers) => put("/book/books/", params, headers) 
    export const delBook = (params, headers) => del("/book/books/", params, headers)
    

    3.6 router/index.js

    import Vue from 'vue'
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    import FatherBook from '@/views/FatherBook'
    import ChildBook from '@/views/components/ChildBook'
    // @ 代表 src
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'HelloWorld',
          component: HelloWorld
        },
        {
          path: '/father_book',
          name: 'FatherBook',
          component: FatherBook
        },
        {
          path: '/child_book',
          name: 'ChildBook',
          component: ChildBook
        }
      ]
    })
    

    3.7 views/FatherBook.vue

    <template>
        <div> 
            <h3>图书管理系统&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;<el-button @click="addNew">添加图书</el-button></h3>
            <Child 
                :visible.sync="dialogFormVisible"
                :editData='editData'
                @addBookList='addBookList'
            >
            <!-- :visible.sync="dialogFormVisible:控制子组件是否显示,是控制子组件中的属性!并非父组件是否显示子组件 -->
            <!-- :editData='editData':把 editData传递给子组件 -->
            <!-- @addBookList='addBookList':不调用是没办法触发子组件增加或者修改图书时的方法的 -->
            </Child>
            <el-table
                :data="book_list"
                style=" 100%"
                
                
            >
                <el-table-column
                    label="id"
                    width="50"
                    prop='id'>
                </el-table-column>
    
                <el-table-column
                    label="图书名"
                    width="180"
                    prop='books_name'>
                </el-table-column>
    
                <el-table-column
                    label="作者"
                    width="80"
                    prop='author'>
                </el-table-column>
    
                <el-table-column
                    label="描述"
                    width="380"
                    prop='desc'>
                </el-table-column>
    
                <el-table-column
                    label="日期"
                    width="130"
                    prop="datetime">
                </el-table-column>
    
                <el-table-column
                    label="操作"  
                >
                    <template slot-scope="scope"> 
                        <!-- 用于获取id,不得不写 -->
                        <el-button type="primary" icon="el-icon-edit" circle @click="updateBook(scope.row.id)"></el-button>
                        <el-button type="danger" icon="el-icon-delete" circle  @click="deleteBook(scope.row.id)"></el-button>    
                    </template>
                </el-table-column>
            </el-table>
        </div>
    </template>
    
    <script>
    import { getBookList, addBook, delBook, updateBook } from '@/http/apis'
    import ChildBook from '@/views/components/ChildBook'
    import Child from './components/ChildBook'
    export default {
        components:{
            Child
        },
        data() {
            return {
                book_list:[
    
                ],
                dialogFormVisible:false,
                editData:{
                        'books_name':'',
                        'author':'',
                        'desc':'',
                        'datetime':''
                }
            }
        },
        methods: {
            bookList(){
                getBookList().then(res=>{
                    this.book_list = res
                })
            },
            addNew(){
                this.dialogFormVisible = true
                // 初始化列表,否则下次修改或添加会包含上一次的数据
                this.editData = 
                    {
                        'books_name':'',
                        'author':'',
                        'desc':'',
                        'datetime':''
                    }
                
            },
            addBookList(){
                if(this.editData.id){
                    let params = {
                        id: this.editData.id,
                        data: this.editData
                    }
                    updateBook(params).then(res=>{
                        console.log(res)
                        alert('修改成功')
                        this.bookList()
                    })
    
                }else{
                    addBook(this.editData).then(res=>{
                        console.log(res)
                        alert('添加图书成功')
                        this.bookList()
                    })
                }
    
                this.dialogFormVisible = true
            },
            deleteBook(p){
                console.log(typeof id)
                // console.log(params)
                delBook(p).then(res=>{
                    console.log(res)
                    alert('删除成功')
                    this.bookList()
                })
    
            },
            updateBook(book_id){
                
                this.editData = JSON.parse(JSON.stringify(this.book_list[book_id-1]))
                // 固定方法, 把父组件列表里的值复制到 editData中,再传给子组件显示出来
                this.dialogFormVisible = true 
                console.log(this.editData)
            }
    
        },
        created() {
            this.bookList()
        }
    }
    </script>
    
    <style scoped>
    .el-table .warning-row {
        background: oldlace;
    }
    
    .el-table .success-row {
        background: #f0f9eb;
    }
    
    </style>
    

    3.8 views/components/ChildBook.vue

    <template>
        <div>
            <el-dialog title="请进行操作" :visible="visible"> 
                <div>
                    <span>图书名:</span> 
                    <el-input class='elinput' style="400px" v-model="editData.books_name"></el-input> 
                    
                </div>
                <br> 
                <div>
                    <span>作&emsp;者:</span> 
                    <el-input class='elinput' style="400px" v-model="editData.author"></el-input> 
                                                                            <!-- 与父组件中的 editData 中的值双向绑定 -->
                </div> 
                <br>
                <div>
                    <span>描&emsp;述:</span> 
                    <el-input class='elinput' style="400px" v-model="editData.desc"></el-input> 
                </div> 
                <br>
                <div>
                    <span>日&emsp;期:</span> 
                    <el-input class='elinput' style="400px" v-model="editData.datetime"></el-input> 
                </div> 
                <br>
                <el-button @click="cancel">取 消</el-button> 
                <el-button type="primary" @click="writeBook">确 定</el-button> 
            </el-dialog>
        </div>
    </template>
    
    <script>
    export default {
        props:[ 'visible', 'editData' ],
        // 参数 visible 是控制弹窗是否显示的
        // 参数 editData 是父组件中传递过来的图书空列表,子组件中的值和它进行 v-model 双向绑定
        data() {
            return {
                dialogFormVisible: false,
    
            }
        },
        methods: {
            cancel(){
                this.$emit('update:visible', false)
                // 关闭弹窗
            },
            writeBook(){
                this.$emit('addBookList')
                // 调用父组件中 addBookList方法,调用的是最外面的方法哦,因为添加和修改要写在一起
                this.$emit('update:visible', false)
            }
        },
        created() {
    
        }
    }
    </script>
    .el-input { 
         120px; 
        height: 40px; 
    }
    <style scoped>
    
    </style>
    
  • 相关阅读:
    虚拟机安装CentOS不能联网的解决
    64位openSUSE12.3最完整的安装QQ的方法
    打水井
    一个阶乘中末尾零的个数
    DiscuzX开发手册【精品】
    一个获取PHP消耗时间的小函数
    php获取本月的第一天与最后一天
    在博客园创建了一个自己的博客~
    ie6 fixed 很纠结~这个js就解决了
    现在各个网站都在使用瀑布流布局吧~
  • 原文地址:https://www.cnblogs.com/mapel1594184/p/14021298.html
Copyright © 2011-2022 走看看