项目效果和所用开发工具:
1.项目介绍
此项目为快速实现前后端分离模式的 demo 项目 —— 图书管理系统,前端页面实现从后端获取图书列表进行渲染,同时可以灵活地进行增删改查,暂时没有做用户登录认证和权限管理,只为表达前后端通信的逻辑。
2.运行效果
3.电脑系统
window10 企业版
4.后端开发工具
python 3.12.0 + Django 5.0.3 + djangorestframework 3.15.0 + PyCharm 2023.2.5社区版
5.前端开发工具
HBuilder X 4.06
后端开发
1.创建项目
键盘 WIN+R 打开 cmd 命令行创建 django 项目 (社区版 pycharm 创建 django 项目比较麻烦)
django-admin startproject bookmanage
2.创建 api 应用
打开 pycharm 社区版加载上一步创建的项目并配置好开发环境,然后用终端创建 api 应用
3.注册 api 应用、注册 rest_framework
确保开发环境已安装好 django 和 djangorestframe 模块,然后在项目的 settings.py 中注册 api 应用和 rest_framework 模块
4.创建数据库model
在 api 应用的 models.py 中按需添加 model ,注意,前端的书籍变量名称要和这里的一致
5.创建数据库
在终端执行迁移命令 makemigrations 和 migrate
python manage.py makemigrations
python manage.py migrate
6.创建视图
在 api 应用下的 views.py 中添加视图类
7.为 DRF 创建路由
在项目的 urls.py 中添加路由注册
8.运行项目
在终端执行 python manage.py runserver 命令开启后端服务,默认端口为8000
前端开发
1.创建项目
HBuilder 新建 uni-app 项目
2.撸代码
在项目自动生成的 pages/index 文件夹下的 index.vue 直接修改
3.运行前端代码
可以在 HBuilder X 中点击右上角的预览按钮运行项目,就能看到运行效果了
项目源码
1.后端源码
settings.py 主要是在 INSTALLED_APPS 的末尾追加 api.apps.ApiConfig 和 rest_framework
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api.apps.ApiConfig',
'rest_framework',
]
models.py
from django.db import models
# Create your models here.
class Books(models.Model):
object = models.Manager()
bookName = models.CharField(max_length=20)
author = models.CharField(max_length=20)
date = models.DateField()
intro = models.CharField(max_length=100)
views.py
from django.shortcuts import render
# Create your views here.
from .models import Books
from rest_framework.viewsets import ModelViewSet
from rest_framework.serializers import ModelSerializer
class BooksSerializers(ModelSerializer):
class Meta:
model = Books
fields = '__all__'
class BooksView(ModelViewSet):
queryset = Books.object.all()
serializer_class = BooksSerializers
urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
from api import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register('books', views.BooksView, basename='book')
urlpatterns += router.urls
2.前端源码
为了快速实现前后端通信,就一个 index.vue 的代码搞定,没了。
<template>
<view class="content">
<view v-if="showaddframe === true" class="addframe">
<view class="addframebtns">
<button @click="submitbtn" class="btn">提交</button>
<button @click="cancelbtn" class="btn">取消</button>
</view>
<input type="text" placeholder="书籍名称" class="bookinput" v-model="editData.bookName">
<input type="text" placeholder="作者" class="bookinput" v-model="editData.author">
<input type="text" placeholder="出版日期" class="bookinput" v-model="editData.date">
<input type="text" placeholder="简介" class="bookinput" v-model="editData.intro">
</view>
<view v-else class="btns">
<button @click="getbooksbtn" class="btn">刷新</button>
<button @click="addbtn" class="btn">添加</button>
</view>
<view class="book" v-for="item in books" :key="item.id">
<text class="bookname">
{{item.bookName}}
 <text @click="delbookbtn(item.id)" style="font-size: 16rpx; color: blue;">删除</text>
 <text @click="updatebtn(item.id)" style="font-size: 16rpx; color: blue;">修改</text>
</text>
<text class="bookinfo">编号:{{item.id}}  |  作者:{{item.author}}  |  出版时间:{{item.date}}</text>
<text class="bookintro">    {{item.intro}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
showaddframe: false,
editData: {},
books: [],
isUpdate: false
}
},
onLoad() {
this.getbooksbtn();
},
methods: {
addbtn() {
this.isUpdate = false;
this.editData = {};
this.showaddframe = true;
},
getbooksbtn() {
uni.request({
url: 'http://localhost:8000/books/',
method: 'GET',
success: (res) => {
this.books = res.data;
},
fail: (err) => {
console.log(err.errMsg);
}
})
},
cancelbtn() {
this.showaddframe = false;
},
submitbtn() {
if(this.isUpdate) {
uni.request({
url: 'http://localhost:8000/books/' + this.editData.id + '/',
method: 'PUT',
data: {
"bookName": this.editData.bookName,
"author": this.editData.author,
"date": this.editData.date,
"intro": this.editData.intro
}
});
}
else {
uni.request({
url: 'http://127.0.0.1:8000/books/',
method: 'POST',
data: {
"bookName": this.editData.bookName,
"author": this.editData.author,
"date": this.editData.date,
"intro": this.editData.intro
},
success: (res) => {
console.log('添加成功');
},
fail: (err) => {
console.log('添加失败'+err.errMsg);
}
});
}
this.showaddframe = false;
setTimeout( () => {this.getbooksbtn();}, 100);
},
delbookbtn(id) {
uni.request({
url: 'http://localhost:8000/books/' + id + '/',
method: 'DELETE',
fail: (err) => {
console.log(err.errMsg);
}
});
this.getbooksbtn();
},
updatebtn(id) {
uni.request({
url: 'http://localhost:8000/books/' + id + '/',
method: 'GET',
success: (res) => {
this.editData = {
id: res.data.id,
bookName: res.data.bookName,
author: res.data.author,
date: res.data.date,
intro: res.data.intro
}
}
});
this.isUpdate = true;
this.showaddframe = true;
}
}
}
</script>
<style lang="scss">
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.addframe {
width: 80%;
.addframebtns {
display: flex;
flex-direction: row;
margin: 10rpx 0rpx;
}
.bookinput {
padding: 10rpx 20rpx;
background-color: lightcyan;
margin: 10rpx;
border: 1rpx solid gray;
border-radius: 10rpx;
}
}
.btns {
display: flex;
flex-direction: row;
width: 80%;
padding: 10rpx 0rpx;
}
.btn {
font-size: 26rpx;
background-color: lightblue;
}
.book {
width: 80%;
height: 200rpx;
background: radial-gradient(lightcyan,lightblue);
border-radius: 10rpx;
margin: 10rpx;
display: flex;
flex-direction: column;
padding: 0rpx 20rpx;
.bookname {
font-size: 40rpx;
}
.bookinfo {
font-size: 16rpx;
color: gray;
}
.bookintro {
border-top: 1rpx dotted gray;
padding-top: 10rpx;
margin-top: 10rpx;
font-size: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
</style>