Python Web 前后端分离 后台管理系统 Django+vue(完整代码)
Play_Sai 2024-07-08 17:33:02 阅读 63
1.前后端分离的架构
在前后端分离的架构中,前端和后端分别作为独立的项目进行开发和部署。前端项目通过API与后端项目进行通信。
前端:使用Vue.js构建用户界面,调用后端提供的RESTful API获取和发送数据。后端:使用Django构建API,处理业务逻辑和数据存储
2.创建django项目及vue项目
创建app的时候需要进入项目的目录下 win10 cd xmmc
<code>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)解决跨域问题
<code>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">code>
<div class="login-container">code>
<el-card class="box-card" style="opacity: 0.9;">code>
<h2>登录</h2>
<el-form
:model="ruleForm"code>
status-icon
:rules="rules"code>
ref="ruleForm"code>
label-position="left"code>
label-width="70px"code>
class="login-form"code>
>
<el-form-item label="用户名" prop="uname">code>
<el-input v-model="ruleForm.uname"></el-input>code>
</el-form-item>
<el-form-item label="密码" prop="password">code>
<el-input
type="password"code>
v-model="ruleForm.password"code>
autocomplete="off"code>
></el-input>
</el-form-item>
</el-form>
<div class="btn-group">code>
<el-button type="primary" @click="submitForm('ruleForm')"code>
>登录</el-button
>
<el-button @click="resetForm('ruleForm')">重置</el-button>code>
<router-link to="/register">code>
<el-button style="margin-left:10px">注册</el-button>code>
</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(视图文件)
<code>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'),code>
path('register/', views.register_view, name='register'),code>
]
(4)在主路由中配置user app(主路由跟setting在同一个文件夹)
6.管理页面的前后端完整代码
(1)管理页面前端代码(业务范围页面)
<code><template>
<div>
<el-row style="height: 50px;">code>
<el-col :span="1" class="grid">code>
<el-button
type="success"code>
@click="showAddDialog"code>
icon="el-icon-circle-plus-outline"code>
size="mini"code>
round
>新增</el-button>
</el-col>
</el-row>
<el-dialog title="新增" :visible.sync="addDialogVisible" width="42%">code>
<el-form :model="addFormData" :rules="addFormRules" ref="addForm" label-width="120px">code>
<el-form-item label="标题" prop="title">code>
<el-input v-model="addFormData.title"></el-input>code>
</el-form-item>
<el-form-item label="图片链接" prop="image_url">code>
<el-input v-model="addFormData.image_url"></el-input>code>
</el-form-item>
<el-form-item label="内容" prop="content">code>
<el-input v-model="addFormData.content"></el-input>code>
</el-form-item>
<el-form-item label="图片详情链接" prop="details_image_url">code>
<el-input v-model="addFormData.details_image_url"></el-input>code>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">code>
<el-button @click="addDialogVisible = false">取 消</el-button>code>
<el-button type="primary" @click="submitAddForm">确 定</el-button>code>
</span>
</el-dialog>
<el-dialog title="编辑" :visible.sync="editDialogVisible" width="40%">code>
<el-form :model="editFormData" :rules="editFormRules" ref="editForm" label-width="100px">code>
<el-form-item label="标题" prop="title">code>
<el-input v-model="editFormData.title"></el-input>code>
</el-form-item>
<el-form-item label="图片链接" prop="image_url">code>
<el-input v-model="editFormData.image_url"></el-input>code>
</el-form-item>
<el-form-item label="内容" prop="content">code>
<el-input v-model="editFormData.content"></el-input>code>
</el-form-item>
<el-form-item label="图片详情链接" prop="details_image_url">code>
<el-input v-model="editFormData.details_image_url"></el-input>code>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">code>
<el-button @click="editDialogVisible = false">取 消</el-button>code>
<el-button type="primary" @click="submitEditForm">确 定</el-button>code>
</span>
</el-dialog>
<el-table
:data="tableData.slice((currentPage-1)*pagesize,currentPage*pagesize)"code>
style="width: 100%; margin-top: 10px;"code>
border
stripe
ref="multipleTable"code>
tooltip-effect="dark"code>
>
<el-table-column label="序号" type="index" width="80px" align="center"></el-table-column>code>
<el-table-column prop="title" width="100px" label="标题"></el-table-column>code>
<el-table-column prop="image_url" width="150px" label="图片链接"></el-table-column>code>
<el-table-column prop="content" width="120px" label="内容"></el-table-column>code>
<el-table-column prop="details_image_url" label="图片详情链接"></el-table-column>code>
<el-table-column width="190px" label="操作">code>
<template slot-scope="scope">code>
<el-button type="primary" icon="el-icon-edit" size="mini" @click="editBook(scope.row)">编辑</el-button>code>
<el-button
icon="el-icon-delete"code>
size="mini"code>
type="danger"code>
@click="confirmDelete(scope.row)"code>
>删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">code>
<el-pagination
@size-change="handleSizeChange"code>
@current-change="handleCurrentChange"code>
:current-page="currentPage"code>
:page-sizes="[5, 10, 20, 40]"code>
:page-size="pagesize"code>
layout="total, sizes,prev, pager, next"code>
:total="tableData.length"code>
next-text="下一页"code>
></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
<code>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中都可以创建数据库表
<code>from django.db import models
# 联系我们表
class Contact(models.Model):
id = models.AutoField(primary_key=True)
company_name = models.CharField(max_length=100, verbose_name='公司名称')code>
phone = models.CharField(max_length=20, verbose_name='电话')code>
address = models.CharField(max_length=200, verbose_name='地址')code>
email = models.CharField(max_length=255, verbose_name='邮箱')code>
class Meta:
db_table = 'contact'
创建表完成之后进行数据库的迁移,在项目的根目录下的控制台进行
python manage.py makemigrations
python manage.py migrate
8.启动vue项目
npm run dev
9.打开浏览器访问http://localhost:8080/#/user/login/ 即可登录
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。