Python Web 前后端分离 后台管理系统 Django+vue(完整代码)

1.前后端分离的架构

在前后端分离的架构中,前端和后端分别作为独立的项目进行开发和部署。前端项目通过API与后端项目进行通信。

  • 前端:使用Vue.js构建用户界面,调用后端提供的RESTful API获取和发送数据。
  • 后端:使用Django构建API,处理业务逻辑和数据存储
  • 2.创建django项目及vue项目

    创建app的时候需要进入项目的目录下  win10 cd xmmc

    django-admin startproject xmmc
    django-admin startapp app01

    以管理员身份打开命令行界面,进入任意一个想要创建项目的文件夹

    vue create vueproject

    然后按自己的需求选择,可以去专门搜一下创建vue的教程

    3.配置setting

    (1)配置数据库
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',  # 加载mysql引擎
            'NAME': 'db_goods',  # 数据库名称
            'USER': 'root',  # mysql账户名
            'PASSWORD': '123456',  # mysql账户密码
            'PORT': 3306,  # 端口号
            'HOST': 'localhost'
        }
    }
    (2)配置模板文件
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            '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',
                ],
            },
        },
    ]
    (3)配置静态文件
    STATIC_URL = 'static/'
    STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
    (4)注册app(使用创建 django-admin startapp app名称)

    (5)解决跨域问题

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'corsheaders.middleware.CorsMiddleware',  # 跨域,一定要写在第三行
        '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
    CORS_ALLOW_CREDENTIALS = True
    
    # 允许所有的请求头
    CORS_ALLOW_HEADERS = ('*')

    4.查看目录结构

    web_django/
    ├── static/
    ├── templates/
    ├── user/
    │   ├── migrations/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   ├── views.py
    ├── web_django/
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   ├── wsgi.py
    ├── webapp/
    │   ├── migrations/
    │   ├── views/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   ├── views.py
    

    5.前后端实现登录

    (1)编写登录的前端(完整代码)
    <template>
      <div class="login-background">
        <div class="login-container">
          <el-card class="box-card" style="opacity: 0.9;">
            <h2>登录</h2>
            <el-form
              :model="ruleForm"
              status-icon
              :rules="rules"
              ref="ruleForm"
              label-position="left"
              label-width="70px"
              class="login-form"
            >
              <el-form-item label="用户名" prop="uname">
                <el-input v-model="ruleForm.uname"></el-input>
              </el-form-item>
              <el-form-item label="密码" prop="password">
                <el-input
                  type="password"
                  v-model="ruleForm.password"
                  autocomplete="off"
                ></el-input>
              </el-form-item>
            </el-form>
            <div class="btn-group">
              <el-button type="primary" @click="submitForm('ruleForm')"
                >登录</el-button
              >
              <el-button @click="resetForm('ruleForm')">重置</el-button>
              <router-link to="/register">
                <el-button style="margin-left:10px">注册</el-button>
              </router-link>
            </div>
          </el-card>
        </div>
      </div>
    </template>
    
    <script>
    import axios from 'axios';
    
    export default {
      data() {
        return {
          ruleForm: {
            uname: "",
            password: "",
          },
          rules: {
            uname: [
              { required: true, message: "用户名不能为空!", trigger: "blur" },
            ],
            password: [
              { required: true, message: "密码不能为空!", trigger: "blur" },
            ],
          },
        };
      },
      methods: {
        async submitForm(formName) {
          try {
            const response = await axios.post('http://127.0.0.1:8005/user/login/', this.ruleForm);
            // 在这里处理后端响应
            console.log(response.data);
            if (response.data.success) {
              sessionStorage.setItem('username', JSON.stringify(response.data))
              // 登录成功,跳转到 Index 页面
              this.$router.push('/components/News');
            } else {
              // 登录失败,显示错误消息或其他处理
              console.error('登录失败:', response.data.message);
            }
          } catch (error) {
            console.error('提交表单时发生错误:', error);
          }
        },
        resetForm(formName) {
          this.$refs[formName].resetFields();
        },
      },
    };
    </script>
    
    <style scoped>
    .login-background {
      background-image: url('https://pic4.zhimg.com/v2-b730c73ebd78bd5f22293aab0d343f4b_r.jpg?source=1940ef5c');
      background-size: cover;
      background-position: center;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .box-card {
      width: 400px;
    }
    
    .login-form {
      margin: auto;
    }
    
    .btn-group {
      margin-top: 20px;
    }
    </style>
    

    登录效果图

    (2)编写登录后端views(视图文件)
    from django.http import JsonResponse
    from django.contrib.auth import authenticate, login
    from django.contrib.auth.models import User
    import json
    
    
    def login_view(request):
        # if request.method == 'GET':
    
        if request.method == 'POST':
            js = request.body.decode('utf8')
            data = json.loads(js)
            username = data['uname']
            password = data['password']
    
            # 使用 Django 自带的 authenticate 方法验证用户身份
            user = authenticate(request, username=username, password=password)
    
            if user is not None:
                # 登录成功,将用户添加到当前会话中
                login(request, user)
    
                print(user)
                return JsonResponse({'code': 200, 'success': True, 'message': '登录成功', 'username': str(user)})
            else:
                # 登录失败,返回错误消息
                return JsonResponse({'code': 500, 'success': False, 'message': '用户名或密码错误', })
    
        # 如果不是 POST 请求,则返回错误消息
        return JsonResponse({'success': False, 'message': '只支持 POST 请求'})
    
    
    
    def register_view(request):
        if request.method == 'POST':
            # 从请求体中获取JSON数据
            js = request.body.decode('utf8')
            data = json.loads(js)
            print(data)
            username = data['uname']
            password = data['password']
    
    
            # 检查用户名是否已经存在
            if User.objects.filter(username=username).exists():
                return JsonResponse({'code': 400, 'success': False, 'message': '用户名已存在'})
    
            # 创建用户
            user = User.objects.create_user(username=username, password=password)
    
            # 返回成功消息
            return JsonResponse({'code': 200, 'success': True, 'message': '注册成功', 'username': username})
    
        # 如果不是 POST 请求,则返回错误消息
        return JsonResponse({'code': 405, 'success': False, 'message': '只支持 POST 请求'})
    
    (3)子视图文件(在user app中创建一个urls)
    from . import views
    
    from django.urls import path, re_path
    
    urlpatterns = [
        path('login/', views.login_view, name='login'),
        path('register/', views.register_view, name='register'),
    ]
    
    (4)在主路由中配置user app(主路由跟setting在同一个文件夹)

    6.管理页面的前后端完整代码 

    (1)管理页面前端代码(业务范围页面)
    <template>
      <div>
        <el-row style="height: 50px;">
          <el-col :span="1" class="grid">
            <el-button
              type="success"
              @click="showAddDialog"
              icon="el-icon-circle-plus-outline"
              size="mini"
              round
            >新增</el-button>
          </el-col>
        </el-row>
        <el-dialog title="新增" :visible.sync="addDialogVisible" width="42%">
          <el-form :model="addFormData" :rules="addFormRules" ref="addForm" label-width="120px">
            <el-form-item label="标题" prop="title">
              <el-input v-model="addFormData.title"></el-input>
            </el-form-item>
            <el-form-item label="图片链接" prop="image_url">
              <el-input v-model="addFormData.image_url"></el-input>
            </el-form-item>
            <el-form-item label="内容" prop="content">
              <el-input v-model="addFormData.content"></el-input>
            </el-form-item>
            <el-form-item label="图片详情链接" prop="details_image_url">
              <el-input v-model="addFormData.details_image_url"></el-input>
            </el-form-item>
          </el-form>
          <span slot="footer" class="dialog-footer">
            <el-button @click="addDialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="submitAddForm">确 定</el-button>
          </span>
        </el-dialog>
    
        <el-dialog title="编辑" :visible.sync="editDialogVisible" width="40%">
          <el-form :model="editFormData" :rules="editFormRules" ref="editForm" label-width="100px">
            <el-form-item label="标题" prop="title">
              <el-input v-model="editFormData.title"></el-input>
            </el-form-item>
            <el-form-item label="图片链接" prop="image_url">
              <el-input v-model="editFormData.image_url"></el-input>
            </el-form-item>
            <el-form-item label="内容" prop="content">
              <el-input v-model="editFormData.content"></el-input>
            </el-form-item>
            <el-form-item label="图片详情链接" prop="details_image_url">
              <el-input v-model="editFormData.details_image_url"></el-input>
            </el-form-item>
          </el-form>
          <span slot="footer" class="dialog-footer">
            <el-button @click="editDialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="submitEditForm">确 定</el-button>
          </span>
        </el-dialog>
    
        <el-table
          :data="tableData.slice((currentPage-1)*pagesize,currentPage*pagesize)"
          style="width: 100%; margin-top: 10px;"
          border
          stripe
          ref="multipleTable"
          tooltip-effect="dark"
        >
          <el-table-column label="序号" type="index" width="80px" align="center"></el-table-column>
    
          <el-table-column prop="title" width="100px" label="标题"></el-table-column>
          <el-table-column prop="image_url" width="150px" label="图片链接"></el-table-column>
          <el-table-column prop="content" width="120px" label="内容"></el-table-column>
          <el-table-column prop="details_image_url" label="图片详情链接"></el-table-column>
          <el-table-column width="190px" label="操作">
            <template slot-scope="scope">
              <el-button type="primary" icon="el-icon-edit" size="mini" @click="editBook(scope.row)">编辑</el-button>
              <el-button
                icon="el-icon-delete"
                size="mini"
                type="danger"
                @click="confirmDelete(scope.row)"
              >删除</el-button>
            </template>
          </el-table-column>
        </el-table>
    
        <div class="pagination">
          <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="currentPage"
            :page-sizes="[5, 10, 20, 40]"
            :page-size="pagesize"
            layout="total, sizes,prev, pager, next"
            :total="tableData.length"
            prev-text="上一页"
            next-text="下一页"
          ></el-pagination>
        </div>
      </div>
    </template>
    
    <script>
    import axios from "axios";
    import { MessageBox } from "element-ui";
    
    export default {
      name: "app",
      data() {
        return {
          currentPage: 1, // 默认显示页面为1
          pagesize: 5, // 每页的数据条数
          tableData: [], // 需要 data 定义一些,tableData 定义一个空数组,请求的数据都是存放这里面
          addDialogVisible: false,
          activeIndex2: "1",
          addFormData: {
            title: "",
            image_url: "",
            content: "",
            details_image_url: ""
          },
          addFormRules: {
            title: [{ required: true, message: "请输入标题", trigger: "blur" }],
            image_url: [
              { required: true, message: "请输入图片地址", trigger: "blur" }
            ],
            content: [{ required: true, message: "请输入内容", trigger: "blur" }],
            details_image_url: [
              { required: true, message: "请输入图片详情", trigger: "blur" }
            ]
          },
          editDialogVisible: false,
          editFormData: {
            id: null,
            title: "",
            image_url: "",
            content: "",
            details_image_url: ""
          },
          editFormRules: {
            title: [{ required: true, message: "请输入标题", trigger: "blur" }],
            image_url: [
              { required: true, message: "请输入图片地址", trigger: "blur" }
            ],
            content: [{ required: true, message: "请输入内容", trigger: "blur" }],
            details_image_url: [
              { required: true, message: "请输入图片详情", trigger: "blur" }
            ]
          }
        };
      },
      mounted() {
        this.getData();
      },
      methods: {
        getData() {
          axios.get(" http://127.0.0.1:8005/web_drf/coreBusinessView/").then(
            response => {
              console.log(response.data);
              this.tableData = response.data;
            },
            error => {
              console.log("error");
            }
          );
        },
        handleSizeChange(size) {
          this.pagesize = size;
        },
        handleCurrentChange(currentPage) {
          this.currentPage = currentPage;
        },
        showAddDialog() {
          this.addDialogVisible = true;
        },
        submitAddForm() {
          this.$refs.addForm.validate(valid => {
            if (valid) {
              axios
                .post(
                  " http://127.0.0.1:8005/web_drf/coreBusinessView/",
                  this.addFormData
                )
                .then(response => {
                  console.log("新增成功", response.data);
                  this.addDialogVisible = false;
                  this.getData();
                  this.addFormData = {
                    title: "",
                    image_url: "",
                    content: "",
                    details_image_url: ""
                  };
                })
                .catch(error => {
                  console.error("新增失败", error);
                });
            }
          });
        },
        confirmDelete(book) {
          MessageBox.confirm("确定删除该条记录吗?", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning"
          })
            .then(() => {
              this.deleteBook(book);
            })
            .catch(() => {
              // 取消删除操作
            });
        },
        deleteBook(book) {
          const url = ` http://127.0.0.1:8005/web_drf/coreBusinessView/${book.id}/`;
          console.log(book.id);
          axios
            .delete(url)
            .then(response => {
              console.log("删除成功");
              this.getData();
            })
            .catch(error => {
              console.error("删除失败", error);
            });
        },
        editBook(book) {
          this.editFormData = { ...book };
          this.editDialogVisible = true;
        },
        submitEditForm() {
          this.$refs.editForm.validate(valid => {
            if (valid) {
              const url = ` http://127.0.0.1:8005/web_drf/coreBusinessView/${this.editFormData.id}/`;
              axios
                .put(url, this.editFormData)
                .then(response => {
                  console.log("编辑成功", response.data);
                  this.editDialogVisible = false;
                  this.getData();
                })
                .catch(error => {
                  console.error("编辑失败", error);
                });
            }
          });
        }
      }
    };
    </script>
    
    <style>
    .pagination {
      margin-top: 20px;
      text-align: center;
    }
    </style>
    

    业务范围管理页面效果图

    (2)业务范围管理系统后端代码views
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from rest_framework import serializers
    
    from webapp.models import CoreBusiness
    
    
    class coreBusinessSerializers(serializers.ModelSerializer):
        class Meta:
            model = CoreBusiness
            fields = '__all__'
    
    
    class coreBusinessView(APIView):
    
        def get(self, request):
            coreBusiness = CoreBusiness.objects.all()
            ser = coreBusinessSerializers(instance=coreBusiness, many=True)
            print(ser.data)
            return Response(ser.data)
    
        def post(self, request):
            ser = coreBusinessSerializers(data=request.data)
            if ser.is_valid():
                CoreBusiness.objects.create(**ser.validated_data)
                return Response(ser.validated_data)
            else:
                return Response(ser.errors)
    
    
    class coreBusinessdetailview(APIView):
        def get(self, request, pk):
            coreBusiness = CoreBusiness.objects.get(pk=pk)
            ser = coreBusinessSerializers(instance=coreBusiness, many=False)
            return Response(ser.data)
    
        def put(self, request, pk):
            ser = coreBusinessSerializers(data=request.data)
            if ser.is_valid():
                CoreBusiness.objects.filter(pk=pk).update(**ser.validated_data)
                return Response(ser.validated_data)
            else:
                return Response(ser.errors)
    
        def delete(self, request, pk):
            CoreBusiness.objects.get(pk=pk).delete()
            return Response('删除成功')
    
    (3)配置子路由

    (4)在主路由中配置该app(主路由跟setting在同一个文件夹)

    7.创建数据库表

    在任意一个models中都可以创建数据库表

    from django.db import models
    
    
    # 联系我们表
    class Contact(models.Model):
        id = models.AutoField(primary_key=True)
        company_name = models.CharField(max_length=100, verbose_name='公司名称')
        phone = models.CharField(max_length=20, verbose_name='电话')
        address = models.CharField(max_length=200, verbose_name='地址')
        email = models.CharField(max_length=255, verbose_name='邮箱')
    
        class Meta:
            db_table = 'contact'
    

    创建表完成之后进行数据库的迁移,在项目的根目录下的控制台进行

    python manage.py makemigrations
    python manage.py migrate

    8.启动vue项目

    npm run dev

     9.打开浏览器访问http://localhost:8080/#/user/login/ 即可登录

    作者:况老师话竞赛

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python Web 前后端分离 后台管理系统 Django+vue(完整代码)

    发表回复