django+vue全

萧奇
2023-12-01

本篇写有部门和员工(为前后端开发)


django:

admin创建超级用户:

at 14:01:52 ❯ python manage.py createsuperuser  # 创建超级用户的命令c
用户名 (leave blank to use 'mac'): mmm
电子邮件地址: 
Password:        # 密码输入时,是看不到的
Password (again): 
密码长度太短。密码必须包含至少 8 个字符。
这个密码太常见了。
密码只包含数字。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

项目准备:

创建项目:

​ django-admin startproject django14(项目名)

创建APP:

​ python manage.py startapp goods(APP名)

修改配置文件: settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  # drf框架
    'corsheaders',  # 跨域APP
    'branch'  # 自己的APP, 随着自己创建的APP名改变而改变
]

#  中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware', 
    'corsheaders.middleware.CorsMiddleware', # 添加跨域中间件
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',  # 关闭csrf验证
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True  # 允许所有源访问


# 指定图片上传的根路径时,media目录需要手动创建
MEDIA_URL = 'media/(文件名)'  # 配置后期图片资源展示的url前缀
MEDIA_ROOT = os.path.join(BASE_DIR, 'media(文件名)')  # 指定图片上传的根路径

创建子路由:(goods/urls.py)

python manage.py startapp app名

修改主路由:

from django.contrib import admin
from django.urls import path, include
from django.conf import settings		# 图片
from django.conf.urls.static import static	# 图片

urlpatterns = [
                  path('admin/', admin.site.urls),
                  path('', include('branch.urls')),  # 实现路由分发
              ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# 上传的图片想要展示,需要做路由映射

模型类:

部门:部门名称

class Branch(models.Model):
    name = models.CharField(max_length=20, verbose_name='部门', unique=True)      # unique=True 去重

员工:员工名称、所属部门、职位、年龄、入职日期、证件照片

class Staff(models.Model):
    name = models.CharField(max_length=20, verbose_name='员工名称')
    title = models.CharField(max_length=10, verbose_name='职位')
    age = models.IntegerField(verbose_name='年龄')
    time = models.DateField(auto_now_add=True, verbose_name='入职日期')
    image = models.ImageField(upload_to='image/%y/%m/%d', verbose_name='证件照片')
    branch = models.ForeignKey(to=Branch, on_delete=models.CASCADE, verbose_name='所属部门')

DateField(auto_now_add=True, verbose_name=‘创建日期’)

DateField(auto_now=True, verbose_name=‘更新日期’)

生成迁移文件

python manage.py makemigrations

生成表

python manage.py migrate

序列化器

序列化器为了 简化 代码的编写过程

需要在自己在 子应用中,创建 序列化文件, 如 goods/serializers.py

from rest_framework.serializers import ModelSerializer
from branch.models import *


class BranchModelSerializer(ModelSerializer):
    class Meta:
        model = Branch
        fields = '__all__'


class StaffModelSerializer(ModelSerializer):
    class Meta:
        model = Staff
        fields = '__all__'

视图

具体的实现过程, 在子应用中的 views.py, 如 goods/views.py

部门

  • 添加 POST cate/ 表单数据
  • 展示 GET cate/
from rest_framework.response import Response
from rest_framework.views import APIView
from branch.serializers import *


# Create your views here.
# 分类
class BranchAPIView(APIView):
    # 添加    POST   cate/  表单数据
    def post(self, request):
        # 1. 获取参数
        data = request.data
        # 2. 创建序列化器对象
        ser = BranchModelSerializer(data=data)
        # 3. 校验
        ser.is_valid(raise_exception=True)
        # 4. 保存
        ser.save()
        # 5. 返回响应
        return Response(ser.data, status=201)

    # 展示   GET   cate/
    def get(self, request):
        # 1. 查询所有数据
        branchs = Branch.objects.all()
        # 2. 创建序列化器对象
        ser = BranchModelSerializer(branchs, many=True)
        # 3. 返回响应
        return Response(ser.data)

删除 DELETE cate/id/

class  BranchDestroyAPIView(APIView):
	def delete(self, request, pk):
        Branch.objects.filter(id=pk).delete()
        return Response(status=204)

员工

  • 添加 POST goods/ 表单参数
  • 展示 GET goods/?cate_id=1
class StaffAPIView(APIView):
    # 添加 POST  goods/  表单参数
    def post(self, request):
        # 1. 获取参数
        data = request.data
        # 2. 创建序列化器对象
        ser = StaffModelSerializer(data=data)
        # 3. 校验
        ser.is_valid(raise_exception=True)
        # 4. 保存
        ser.save()
        # 5. 返回响应
        return Response(ser.data, status=201)

    # 展示 GET  goods/?cate_id=1
    def get(self, request):
        # 1. 接收参数:分类id
        branch_id = request.GET.get('branch_id')        # 部门id
        # 2. 根据外键:分类id,查询分类对应的商品
        staffs = Staff.objects.filter(branch_id=branch_id)
        # 3. 创建序列化器对象
        ser = StaffModelSerializer(staffs, many=True)
        # 4. 返回响应
        return Response(ser.data)

访问上传的图片,网址: http://127.0.0.1:8000/ + meida/ + 真正的图片路径

删除商品 DELETE goods/id/

class  GoodsDestroyAPIView(APIView):
	def delete(self, request, pk):
        Goods.objects.filter(id=pk).delete()
        return Response(status=204)

搜索

搜索:

  • 前端发送参数
  • 后端接收参数
  • 根据参数进行过滤查询
  • 将结果使用序列化器解析
  • 将结果返回给前端
  • 前端接收结果,循环展示
from django.db.models import Q	# 查询		# 视图
from rest_framework.views import APIView	# API
from rest_framework.response import Response	# 返回
from goods.serializers import *		# 导app名.序列化器


class SearchAPIView(APIView):
    def get(self, request):
        # 1. 接收参数
        text = request.GET.get('text')		# 获取text输入框, 和前端vue一致
        
        # 2. 根据分类名或商品名的关键字进行模糊查询
        goods = Goods.objects.filter(Q(name__contains=text) | Q(cate__name__contains=text) )
        
        
        # 3. 创建序列化器解析
        ser = GoodsModelSerializer(goods, many=True)
        
        
        # 4. 返回结果
        return Response(ser.data)

路由

在子路由goods/urls.py中,对当前app的视图做路由匹配

from django.urls import path
from goods.views import *

urlpatterns = [
    # 127.0.0.1:8000/branch/
    path('branch/', BranchAPIView.as_view()),  # 部门的添加和展示
    # 127.0.0.1:8000/staff/
    path('staff/', StaffAPIView.as_view()),  # 员工的添加和展示
    # 127.0.0.1:8000/show/1/
    path('show/<int:pk>/', ShowAPIView.as_view())  # 删除详细
    # 127.0.0.1:8000/branch/?text=人工智能
    path('search/', SearchAPIView.as_view()),  # 搜索
]

Vue

创建Vue项目

使用命令

vue init webpack vue14 # 需要联网下载项目模板
vue init webpack vue14 --offline  # 离线创建项目,必须是本地之前已经有过项目模板时,才能使用

选择配置项

> Use cached template at ~/.vue-templates/webpack

? Project name (vue14)  # 项目名称,可以默认回车

? Project description (A Vue.js project) # 项目描述,可以默认回车

? Author    # 项目作者, 默认回车即可

? Vue build (Use arrow keys)  # 代表项目打包编译配置项,默认回车即可
❯ Runtime + Compiler: recommended for most users
  Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .
vue files - render functions are required elsewhere

? Install vue-router? (Y/n) y  # 是否安装路由模块,必须输入 y,代表安装

? Use ESLint to lint your code? (Y/n) n  # 是否开启 eslint规则检查代码规范性,不建议开启,输入n

? Set up unit tests ? (Y/n) n  # 建议 输入n

? Setup e2e tests with Nightwatch? (Y/n) n  # 建议输入n

? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys)   # 询问使用哪种方式安装  基本依赖包, 建议使用上下键 选择 第三项
  Yes, use NPM
  Yes, use Yarn
❯ No, I will handle that myself

项目创建成功

提示如下信息,说明项目创建成功

# Project initialization finished!
# ========================

To get started:

  cd vue14
  npm install (or if using yarn: yarn)
  npm run dev

需要在项目中按照依赖包

  • 方法一: 联网安装
# 先进入项目
cd vue14
# 使用 cnpm 联网安装基本依赖包
cnpm install
# 使用cnpm 联网安装 axios
cnpm install axios --save
  • 方法二: 可以将以前的 Vue项目中的 node_modules 文件夹复制到 当前的 Vue项目中

启动项目

使用webstrom打开Vue项目, 接下来启动项目

  • 方法一: 终端启动: npm run dev
  • 方法二: 在webstrom配置启动项

基本配置项

src/main.js中添加 axios配置

import axios from 'axios'  // 导入axios模块

axios.defaults.baseURL = 'http://127.0.0.1:8000/'  // 设置axios请求的默认域名

Vue.prototype.$axios = axios // 全局挂载axios

分类添加

  • 利用 v-mdoel获取表单数据
  • 点击按钮,触发方法
  • 在方法中,使用axios提交数据,接收响应
<template>
  <div>
      //1. 使用v-model获取表单数据 
    部门名称:<input type="text" v-model="name">
    
      // 2. 点击按钮,触发对应的方法
    <button @click="add">添加</button>
  </div>
</template>

<script>
export default {
  name: "branch",
  data() {
    return {
      name:''
    }
  },
  methods:{
    // 3. 在方法中,先构建表单数据,再使用axios提交数据  
    add(){		// 添加
      let data = new FormData();
      data.append('name',this.name)
      this.$axios.post('branch/', data)
      .th在这里插入代码片en(resp=>{		// 成功
        console.log(resp.data)
      })
      .catch(err=>{		// 失败返回
        console.log(err.response.data)
      })
    }
  }
}
    // 出错后,可以使用console.log(resp.data)进行找错
</script>

商品添加

  • 利用 v-mdoel获取表单数据
    • 商品属于分类,也就是添加商品时的,分类id不能随便乱填,建议使用下拉框选择
      • 此时的分类数据,不能写死,需要提前从 django的 分类接口中查询
        • 使用axios的get请求,请求所有分类
        • 在生命周期的钩子函数中挂载,让请求,自动执行
        • 在下拉框的选项中循环展示
    • 添加的商品数据中,多了 图片, 此时的图片文件是 只读文件, 需要引用一种新方式
      • 通过选中文件触发的 change 事件,来获取图片内容
  • 点击按钮,触发方法
  • 在方法中,使用axios提交数据,接收响应
<template>
  <div>
    员工名称:<input type="text" v-model="name"><br>
    职位:<input type="text" v-model="title"><br>
    年龄:<input type="text" v-model="age"><br>
    入职日期:<input type="text" v-model="time"><br>
    //1.4 选中图片时,触发change事件,执行对应的方法   
    证件照片:<input type="file" @change="getImage"><br>
     //  getImage获取的名字 
    所属部门:
    <select v-model="branch">
      // 1.3 在下拉框的选项中循环展示   
      <option :value="branch.id" v-for="branch in branchs">{{branch.name}}</option>
    </select>
	// 2. 点击添加按钮,触发方法 
    <button @click="add">添加</button>
  </div>
</template>

<script>
<script>
export default {
  name: "staff",
  data(){return{
    name:'',
    title:'',
    age:'',
    time:'',
    image:'',
    branch:'',
    branchs:[]		// 空列表
  }},
  methods:{
    // 1.1 使用axios的get请求,请求所有分类
    get_branch(){
      this.$axios.get('branch/')	// 前端分类的路由
      .then(resp=>{
        this.branchs = resp.data	// 列表
      })
    },
    // 1.5 在当前的方法中,获取图片对象,赋值给 image变量
    getImage(e){	// 图片
    //  当文件选择框 选中文件时,会触发change事件, 执行对应的方法, 该方法会有一个形参e, e就代表当前的单元素
      this.image = e.target.files[0]
    },
    // 3. 在方法中,打包表单数据,然后使用axios的post请求提交数据,接收响应
    add(){		// 添加
      let data = new FormData();
      data.append('name',this.name)
      data.append('title',this.title)
      data.append('age',this.age)
      data.append('time',this.time)
      data.append('image',this.image)
      data.append('branch',this.branch)
      this.$axios.post('staff/', data)		// 前端商品的路由
      .then(resp=>{
        console.log(resp.data)	// 成功返回
      })
      .catch(err=>{
        console.log(err.response.data)		// 失败返回
      })
    }
  },
  mounted() {
    // 1.2 在生命周期的钩子函数中挂载,让请求,自动执行
    this.get_branch();
  }
}
</script>

<style scoped>

</style>

分类展示

  • 定义方法,在方法中使用axios的get请求,获取所有分类
  • 钩子函数挂载
  • 模板中循环展示
<template>
  <div>
    <h3 v-for="branch in branchs">	// 分类在列表里 
      {{branch.id}}					//非跳转
      //对每个分类名,使用 超链接,点击跳转到商品列表页面,传递 分类id-->
      <router-link :to="{name:'staff_show', query:{'bid': branch.id}}">{{branch.name}}</router-link>	<!--要跳转的-->
    </h3>
  </div>
</template>

<script>
export default {
  name: "branch_show",
  data(){return{
    branchs:[]	// 空列表
  }},
  methods:{
    get_staff(){
      this.$axios.get('branch/')	// 类的路由
      .then(resp=>{
        this.branchs = resp.data	// 列表
      })
    }
  },
  mounted() {		// 钩子挂载
    this.get_staff();
  }
}
</script>

<style scoped>

</style>

商品展示

  • 在钩子哈数中,获取路由对象中的分类id
  • 定义方法,使用axios的get请求,携带分类id, 去django请求对应的商品数据,接收响应
  • 钩子函数挂载执行
  • 模板循环展示
<template>
  <div>
    <h3 v-for="staff in staffs">	//循环列表
      {{staff.id}}
      <router-link :to="{name:'show', query:{'staff_id': staff.id}}">{{staff.name}}</router-link>
      //跳转标签		要跳转的页面    传输的id     本页的id     展示出来的name
    </h3>
  </div>
</template>

<script>
export default {
  name: "staff_show",
  data(){return{
    branch_id:'',	// 定义id
    staffs:[]	// 列表
  }},
  methods:{
    // 2. 定义方法,请求数据
    get_staff(){
      this.$axios.get('staff/?branch_id=' + this.branch_id)
      .then(resp=>{
        this.staffs = resp.data
      })
    }
  },
  mounted() {
    //   1. 先获取分类id
    this.branch_id = this.$route.query.bid
    //  3. 挂载执行方法
    this.get_staff();
  }‘’‘
}
</script>

<style scoped>

</style>

搜索:

<template>
  <div>
    //<!--1.获取用户输入关键字-->
      <input type='text' v-model='text'>		//查询输入框
      
      //<!--2.点击按钮,执行搜索方法-->
      <button @click="search">
          搜索
    </button>
      //<!--4.循环展示所有商品-->
      <h3 v-for="g in goods">			//随意命名 in 空列表
          {{g.id}} == {{g.name}}=={{g.gender}}=={{g.cate_name}}		<!--类里的name-->
    </h3>
  </div>
</template>

<script>
export default {
  name: "Search",
  data() {
    return {
      text: '',			// 要查询的输入框
      goods: [],		// 空列表
    }
  },
  methods: {
    // 3. 在方法中,请求对应数据,不能挂载,点击按钮,才执行方法
    search() {
      this.$axios.get('search/?text=' + this.text)
        .then(resp => {
          this.goods = resp.data	# 列表
        })
    }
}
</script>

详细页面:

django:

class ShowAPIView(APIView):
    def get(self, request, pk):
       # try:
            emp = Staff.objects.get(id=pk)
        #except Exception as e:
         #   return Response({'msg': '找不到'}, status=404)
        ser = StaffModelSerializer(emp)     # emp 随意命名 见名识意
        return Response(ser.data)

django删除:

    def delete(self, request, pk):
        Staff.objects.filter(id=pk).delete()
        return Response(status=204)

vue详情:

<template>
  <div>
    <h3>
      {{ emp.id }} ||
      {{ emp.name }} ||
      {{ emp.title}} ||
      {{ emp.age }} ||
      {{ emp.time }} ||
      //<!-- 图片  后端的端口 + g.图片  style设置宽高  -->
      <img :src="emp.image" style="width: 250px;height: 400px">
    </h3>
    //<!--删除1. 点击删除按钮,触发方法, 同时传递分类的id作为参数 -->
    <a href="" @click.prevent="del">删除</a>  <!--prevent禁止跳转-->
    //<!--括号里的意思是通过id指定删除-->
  </div>
</template>

<script>
export default {
  name: "show",
  data(){return{
    id: '',		// 传参
    emp:{	// 字典
      name: '',
      title: '',
      age: '',
      time: '',
      image: '',
      branch: ''
    }
  }},
  methods: {
    getEmp(){			// 获取
      this.$axios.get(`show/${this.id}/`)	// 根据商品id删除
        .then(resp => {
          console.log(resp.data)
          this.emp.name = resp.data.name
          this.emp.title = resp.data.title
          this.emp.age = resp.data.age
          this.emp.time = resp.data.time
          this.emp.image = "http://127.0.0.1:8000/" + resp.data.image
          this.emp.branch = resp.data.branch
        })
    },
    // 删除2: 在方法中,发送 delete请求,根据id,删除分类, 删除成功,刷新页面
    del(){
      this.$axios.delete(`show/${this.id}/`)	// 前端路由`/${id删除}/`
        .then(resp => {
          this.$router.push('branch_show/?branch_id=' + this.emp.branch)
        })		  // press_list 要跳转的页面
    }
  },
  mounted() {
    this.id = this.$route.query.staff_id	// 挂载
    this.getEmp()		// 路由
  }
}
</script>

<style scoped>

</style>

本篇仅供参考严禁侵权

 类似资料: