手把手带你用Django实现一个后台管理系统
一、团队协作、项目初始化
1、小组负责人在本地新建一个Djang项目
// 新建项目
django-admin startproject inventory_management_django_system
2、在gitee上新建仓库,将本地项目上传到新建的gitee仓库中
- 这部分参考博客:blog.csdn.net/qq_48278620…
3、团队成员协作开发(前提:团队成员电脑上已安装Git和Gitee账号已用邮箱注册)
- 项目管理员在仓库分支管理中手动添加分支【分支名称】
- 在想要放置项目的文件夹里右键,执行git init命令进行初始化
- 执行git clone 【项目地址】 把项目克隆到本地
- 成员进入项目文件夹,在此目录下依次执行以下几条命令:
git checkout 【分支名称】
git add .
git commit -m "描述内容“
git pull --rebase origin 【分支名称】
git push -u origin 【分支名称】
此时成员的本地项目便与仓库中属于自己的分支相关联了
- 进入PythonCharm,下载Gitee插件,项目更改后可以将项目右键选择提交与推送到自己的分支
- 如果推送时出现下面的错误提示:
而且输入密码后仍然像下面显示的那样,推送失败:
此时尝试执行以下命令,清除本地的gitee用户名和密码:
git config --system --unset credential.helper
如果仍然推送失败,重启PyCharm,重新进行推送。
二、数据库建表工作
1、创建并注册这部分模块的app
- 创建app:
//创建app(app的名字自定义)
python .\manage.py startapp app01
- 注册app(settings.py里的INSTALLED_APPS代码块如图修改):
- 运行这个初始化项目:
//运行Django项目
python .\manage.py runserver
2、创建并连接数据库
- 在pgAdmin 4手动创建数据库:
- 项目设置文件settings.py中更改DATABASE部分代码如下:
3、在创建表之前观察后台管理系统需要完成的功能
- 本系统为一个简单的后台管理系统,可为企业提供后台管理服务,主要包括系统用户的登录注册、库存管理、订单管理、临时订单、用户管理、数据可视化六大板块的功能。
- 其中系统用户分为两类,一类是高权限的超级管理员,另一类是低权限的普通管理员,其中普通管理员不具备用户管理功能。系统用户既是系统的管理员,也是订单的负责人,每个管理员负责自己的订单与临时订单。
- 在注册页面默认注册低权限的普通管理员,超级管理员可在用户管理板块中对系统的用户权限进行修改。
- 每个订单/临时订单会通过userId这个属性与用户绑定,类似于公司中某个订单是谁负责的,增加订单/临时订单时通过验证目前所登录信息来做到绑定。
- 数据可视化模块中,主要对公司近十年具体的销售额数据进行可视化,里面的数据通过sql语句初始化在数据库表里面,前端无需提供,后端也无法更改,相当于一个独立静态的模块,无数据的改变。
登录注册:(涉及系统用户user表)——完成系统的登录、登录之后所登录用户信息的返回、系统用户的注册
库存管理:(涉及物品ware表)——完成对库存物品的增、删、改、查,以及选择物品加入订单或者临时订单
订单管理:(涉及订单order表、系统用户user表、临时订单cart表)——完成对订单的删、查
临时订单:(涉及临时订单cart表、订单order表、系统用户user表)——完成临时订单的删除、编辑,以及选择临时订单加入订单仓库
用户管理:(涉及系统用户user表)——完成对系统用户的增、删、改、查(注意:只有高权限的系统用户才具有这项功能,在登录时便可进行验证所登录用户是否为高权限用户;【增加用户】与【注册用户】本质是同一个功能)
数据可视化:(涉及公司销售额sales表)——完成公司近十年每年具体销售数据的可视化
4、项目中需要创建的表
从上面展示的六大模块功能可以看出,项目一共涉及五个表,分别是系统用户user表、物品ware表、订单order表、临时订单cart表、公司销售额sales表。
其中每个表的属性介绍如下:
- 系统用户user表:【userId,userName,userPassword,userPower,createTime,updateTime
- 物品ware表:【wareId,wareName,warePower,wareCount,createTime,updateTime】
- 订单order表:【orderId,userId,userName,wareId,wareName,wareCount,createTime】
- 临时订单cart表:【orderId,userId,userName,wareId,wareName,wareCount,createTime,updateTime】
- 公司销售额sales表:【salesId,yearName,yearSales,yearEvents,monthSales,wareSales】
注意:订单order表与临时订单cart表本质是同样的表,但它们的区别是,选择物品加入订单时,仓库中对应的物品会减少,而选择物品加入临时订单时,仓库中对应的物品不会减少。
5、进行表的创建
在项目app文件夹里面的models.py文件进行表模型的构造:
(有关这部分的文档可见:docs.djangoproject.com/zh-hans/4.1…)
本项目的models.py如下:
from django.db import models
from django.db.models import Max, Count
# 导入`connection`用于执行原生sql语句
from django.db import connection
# Create your models here.
# 系统用户user表:【userId,userName,userPassword,userPower,createTime,updateTime】
class User(models.Model):
# primary_key=True表示该属性为该表的主键,暗示null=False和unique=True。
id = models.AutoField(primary_key=True)
userId = models.CharField(max_length=20)
userName = models.CharField(unique=True, max_length=15)
userPassword = models.CharField(max_length=20)
userPower = models.DecimalField(max_digits=3, decimal_places=0, default=10)
createTime = models.DateTimeField(auto_now_add=True)
updateTime = models.DateTimeField(auto_now=True)
def save(self, **kwargs):
if not self.id:
idCount = User.objects.aggregate(Count('id')).get("id__count")
cursor = connection.cursor()
if idCount == 0:
# 要想使用sql原生语句,必须用到execute()函数,然后在里面写入sql原生语句
cursor.execute("TRUNCATE app_user RESTART IDENTITY")
maxid = User.objects.aggregate(Max('id')).get("id__max")
# 让主键从什么位置开始排序
if maxid is not None:
cursor.execute("ALTER SEQUENCE app_user_id_seq RESTART WITH %s", [maxid+1])
self.userId = "{}{:06d}".format('user', (maxid+1) if maxid is not None else 1)
super().save(*kwargs)
# 物品ware表:【wareId,wareName,warePower,wareCount,createTime,updateTime】
class Ware(models.Model):
id = models.AutoField(primary_key=True)
wareId = models.CharField(max_length=20)
wareName = models.CharField(unique=True, max_length=15)
warePower = models.DecimalField(max_digits=8, decimal_places=0, default=0)
wareCount = models.DecimalField(max_digits=10, decimal_places=0, default=0)
createTime = models.DateTimeField(auto_now_add=True)
updateTime = models.DateTimeField(auto_now=True)
def save(self, **kwargs):
if not self.id:
idCount = Ware.objects.aggregate(Count('id')).get("id__count")
cursor = connection.cursor()
if idCount == 0:
# 要想使用sql原生语句,必须用到execute()函数,然后在里面写入sql原生语句
cursor.execute("TRUNCATE app_ware RESTART IDENTITY")
maxid = Ware.objects.aggregate(Max('id')).get("id__max")
# 让主键从什么位置开始排序
if maxid is not None:
cursor.execute("ALTER SEQUENCE app_ware_id_seq RESTART WITH %s", [maxid+1])
self.wareId = "{}{:06d}".format('ware', (maxid+1) if maxid is not None else 1)
super().save(*kwargs)
# 订单order表:【orderId,userId,userName,wareId,wareName,wareCount,createTime】
class Order(models.Model):
id = models.AutoField(primary_key=True)
orderId = models.CharField(max_length=20)
userId = models.CharField(max_length=20)
userName = models.CharField(max_length=15)
wareId = models.CharField(max_length=20)
wareName = models.CharField(max_length=15)
wareCount = models.DecimalField(max_digits=10, decimal_places=0, default=0)
createTime = models.DateTimeField(auto_now_add=True)
def save(self, **kwargs):
if not self.id:
idCount = Order.objects.aggregate(Count('id')).get("id__count")
cursor = connection.cursor()
if idCount == 0:
# 要想使用sql原生语句,必须用到execute()函数,然后在里面写入sql原生语句
cursor.execute("TRUNCATE app_order RESTART IDENTITY")
maxid = Order.objects.aggregate(Max('id')).get("id__max")
# 让主键从什么位置开始排序
if maxid is not None:
cursor.execute("ALTER SEQUENCE app_order_id_seq RESTART WITH %s", [maxid+1])
self.orderId = "{}{:06d}".format('order', (maxid+1) if maxid is not None else 1)
super().save(*kwargs)
# 临时订单cart表:【cartId,userId,userName,wareId,wareName,wareCount,createTime,updateTime】
class Cart(models.Model):
id = models.AutoField(primary_key=True)
cartId = models.CharField(max_length=20)
userId = models.CharField(max_length=20)
userName = models.CharField(max_length=15)
wareId = models.CharField(max_length=20)
wareName = models.CharField(max_length=15)
wareCount = models.DecimalField(max_digits=10, decimal_places=0, default=0)
createTime = models.DateTimeField(auto_now_add=True)
updateTime = models.DateTimeField(auto_now=True)
def save(self, **kwargs):
if not self.id:
idCount = Cart.objects.aggregate(Count('id')).get("id__count")
cursor = connection.cursor()
if idCount == 0:
# 要想使用sql原生语句,必须用到execute()函数,然后在里面写入sql原生语句
cursor.execute("TRUNCATE app_cart RESTART IDENTITY")
maxid = Cart.objects.aggregate(Max('id')).get("id__max")
# 让主键从什么位置开始排序
if maxid is not None:
cursor.execute("ALTER SEQUENCE app_cart_id_seq RESTART WITH %s", [maxid+1])
self.cartId = "{}{:06d}".format('cart', (maxid+1) if maxid is not None else 1)
super().save(*kwargs)
# 公司销售额sales表:【salesId,yearName,yearSales,yearEvents,monthSales,wareSales】
class Sales(models.Model):
id = models.AutoField(primary_key=True)
salesId = models.CharField(max_length=20)
yearName = models.CharField(max_length=5)
yearSales = models.DecimalField(max_digits=10, decimal_places=2, default=0)
yearEvents = models.CharField(max_length=30, default="")
monthSales = models.JSONField(null=True)
wareSales = models.JSONField(null=True)
def save(self, **kwargs):
if not self.id:
idCount = Sales.objects.aggregate(Count('id')).get("id__count")
cursor = connection.cursor()
if idCount == 0:
# 要想使用sql原生语句,必须用到execute()函数,然后在里面写入sql原生语句
cursor.execute("TRUNCATE app_sales RESTART IDENTITY")
maxid = Sales.objects.aggregate(Max('id')).get("id__max")
# 让主键从什么位置开始排序
if maxid is not None:
cursor.execute("ALTER SEQUENCE app_sales_id_seq RESTART WITH %s", [maxid+1])
self.salesId = "{}{:06d}".format('sales', (maxid+1) if maxid is not None else 1)
super().save(*kwargs)
//之后依次执行以下两条语句完成表的创建完成
python .\manage.py makemigrations
python .\manage.py migrate
此时在pgAdmin4中可以看到,表的创建完成:
三、登录注册模块的开发
API文档:docs.djangoproject.com/zh-hans/4.1…(包含: 返回新 QuerySet 的方法 , 返回新 QuerySet 的操作符 , 不返回 QuerySet 的方法 , Field 查找 , 聚合函数 , 查询相关工具等各种相关查询工具的API)
1、注册功能
注册功能——前端提交一个User类,类似这种:
实际上后端只需要拿到userId、userName这两项,剩下的属性靠后端处理得到,再整合到一起,将整合后的结果存储到数据库对应的表中。后端需要处理的属性是createTime、updateTime、userId、userPower,实际上到这步已经无需理会这几项,因为在建user表时,就已经做出了相应处理:
- userPower字段中的deafult = 10属性表示注册的用户默认权限为10(没有额外传另外的值时);
- auto_now_add=True表示当一条新数据被创建成功后,将该入参auto_now_add对应的表字段的值设置为创建成功时的系统时间,以后修改这条新数据时,该表字段的值不会再更新;
- auto_now=True当一条新数据被修改成功后,将该入参auto_now对应的表字段的值设置为修改成功时的系统时间;
- 重写的save函数表示新建一条数据时,自动生成并保存诸如:“userXXXXXX”这种唯一确定的userId。
下面在app文件夹里面的view.py写注册功能函数:
# 导入所需模块
import json
from app01.models import *
# 处理跨域
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.shortcuts import render
# Create your views here.
# 注册功能
@csrf_exempt #处理跨域
def register(request):
# 获取前端传过来的值--request.body表示前端传过来的值,.decode()表示使中文不乱码,用json.loads转换为json格式
reqBody = json.loads(request.body.decode())
# print(reqBody)
username = reqBody['userName']
password = reqBody['userPassword']
if username and password:
# 判断字符串长度
if len(username) > 15 or len(password) > 20:
return JsonResponse({'code': -1, 'msg': '用户名或密码过长'})
# 查找数据库中是否已经存在相同的userName
user_name = User.objects.filter(userName=username)
if user_name.exists():
return JsonResponse({'code': -1, 'msg': '用户已存在'})
# 如果不重复,在保存有关信息
uesr_save = User(userName=username, userPassword=password)
uesr_save.save()
# 返回注册成功信息给前端
return JsonResponse({'code': 0, 'msg': 'success'})
else:
return JsonResponse({'code': -1, "msg": "用户名或密码不能为空"})
然后在urls.py注册路由:
之后运行目前的Django项目:
python .\manage.py runserver
之后我们去测试写的注册功能接口,用【postman】这个软件来测试:
发现报错,因为涉及到跨域问题,解决办法:
在视图views.py里面加上下面两行:
此时,重新在postman上发起请求,发现请求成功:
再去数据库查看数据,发现数据已经进入数据库:
测试注册一个已存在的用户,也成功:
至此,注册功能已完成。
2、登录功能
注册功能——前端提交一个User类,类似这种:
实现登录功能与其它功能操作上相差无几,就是多一个redis缓存的使用,方便获取目前所登录用户的信息,后端返回一个cookie。
步骤如下:
- 安装Redis
- 在settings.py里面配置redis:
# 配置Django缓存存储
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
# 让Django默认使用缓存作为存储session储存后端,这样也不需要安装任何额外的 backend
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
- 在views.py里面编写登录函数
# 导入所需模块
from app01.models import *
# 处理跨域
from django.views.decorators.csrf import csrf_exempt
# JSON对象序列化
import json
from django.core import serializers
from django.http import JsonResponse
# 防止重复登录
from django.contrib.sessions.models import Session
from django.utils import timezone
# 登录功能
@csrf_exempt # 处理跨域
def userLogin(request):
# 获取前端传过来的值--request.body表示前端传过来的值,.decode()表示使中文不乱码,用json.loads转换为json格式
reqBody = json.loads(request.body.decode())
# print(reqBody)
username = reqBody['userName']
password = reqBody['userPassword']
if username and password:
# 查找符合条件的用户信息
user = User.objects.filter(userName=username)
if user.exists():
if user.first().userPassword != password:
return JsonResponse({'code': 0, 'msg': '密码输入错误'})
else:
userInfo = (json.loads(serializers.serialize("json", user)))[0]
# 当前的所有session--防止重复登录
valid_session_obj_list = Session.objects.filter(expire_date__gt=timezone.now())
flag = 0
for session_obj in valid_session_obj_list:
print(session_obj.get_decoded().get("userInfo"))
if session_obj.get_decoded().get("userInfo").pk == userInfo.pk:
flag = 1
break
if flag == 0:
# 将所登录的用户信息存储在session中(已经改session的默认存储位置为redis中),返回给前端的cookie是sessionid
# 只要前端每次请求都带上cookie,后端便可根据每个用户的sessionid查找或者操作对应的登录状态和登录信息
request.session['userInfo'] = userInfo
# 这句话完成了下面几步:
# 1.生成随机的sessionid字符串
# 2.将sessionid和用户的信息在数据库中保存为一个键值对
# 3.通过cookie将sessionid保存在客户端上
# 这时候通过用户再次向服务器发送请求时服务器就可以通过请求中的sessionid判断用户的信息了,从而达到保存登录状态的要求。
# 返回登录成功信息给前端
return JsonResponse({'code': 0, 'msg': 'success', 'userInfo': userInfo})
else:
return JsonResponse({'code': -1, 'msg': '该用户已登录', 'userInfo': userInfo})
else:
return JsonResponse({'code': -1, "msg": "该用户不存在"})
else:
return JsonResponse({'code': -1, "msg": "用户名或密码不能为空"})
- 在urls.py里面注册路由
path("miserauth/login", views.userLogin),
- 运行项目并在postman里测试接口
可以看出,登录成功,并成功设置cookie。
get知识点1:将XX.objects.filter查找到的数据转换为JSON格式的方法
# 1--导入json和serializers模块
import json
from django.core import serializers
# 2--获取数据(此时获取到的是符合要求的对象集合),user.first()表示该集合的第一个对象
user = User.objects.filter(userName=username)
# 3--将对象集合转为JSON格式
# (serializers.serialize表示将对象集合转化为JSON字符串,json.loads表示将该字符串转为真正的JSON格式,[0]表示取得到的数组中的第0个)
return JsonResponse({'code': 0, 'msg': 'success', 'userInfo': (json.loads(serializers.serialize("json", user)))[0]})
# 4--输出如下:
{
"code": 0,
"msg": "success",
"userInfo": {
"model": "app01.user",
"pk": 1,
"fields": {
"userId": "user000001",
"userName": "scucsyyds",
"userPassword": "123456",
"userPower": "10",
"createTime": "2022-11-23T04:45:02.747Z",
"updateTime": "2022-11-23T04:45:02.747Z"
}
}
}
get知识点2:Redis下载和安装(Windows系统)
参考链接:c.biancheng.net/redis/windo…
(注意:在执行redis-server.exe --service-start前要把客户端启动关掉,否则端口会被占用)
get知识点3:windows免费安装redis desktop manager
上github找到免费版最新的是2018年的,可以直接下载exe执行程序:github.com/uglide/Redi…
3、获取当前登录信息
获取获取当前登录信息——前端只发起get请求,无需提交任何东西;后端从redis中返回登录用户的信息。
获得当前登录信息,需要用到的地方:
1、登录时验证是否为超级管理员,是的话开放用户管理功能,同时临时订单展示所有;不是则关闭用户管理功能,同时临时订单展示该管理员对应的临时订单。
2、在物品管理页面加入订单或者临时订单时,绑定当前所登录用户信息
主要思想就是根据前端请求携带的cookie,通过cookie中的sessionid,在redis中找到对应的登录信息。
# 获取当前登录信息
@csrf_exempt # 处理跨域
def getLoginUser(request):
try:
userInfo = request.session.get('userInfo', None)
if userInfo:
# 返回登录成功信息给前端
return JsonResponse({'code': 0, 'msg': 'success', 'userInfo': userInfo})
else:
return JsonResponse({'code': -1, "msg": "未查找到登录信息"})
# 防止异常处理太过宽泛
except AttributeError:
return JsonResponse({'code': -2, 'msg': '发生异常'})
登录之后用postman测试该功能:
可以看出,获取登录用户信息,该接口是成功的。
4、退出登录功能
主要思想将后端redis缓存的登录状态cookie清除掉,返回退出成功信息给前端。
# 退出登录
@csrf_exempt # 处理跨域
def loginOut(request):
try:
# 退出登录的前提是已经登录
userInfo = request.session.get('userInfo', None)
if userInfo:
# 清除该用户在服务器中的session,删除客户端的sessionid在服务器中保存的状态
del request.session['userInfo']
# 返回退出成功信息给前端
return JsonResponse({'code': 0, 'msg': 'success'})
else:
return JsonResponse({'code': -1, 'msg': '当前用户未登录'})
# 防止异常处理太过宽泛
except AttributeError:
return JsonResponse({'code': -2, 'msg': '退出失败'})
此时用postman测试该功能:
然后再去获取登录用户信息:
可以看出,目前没有登录信息,接口是成功的。
四、数据可视化模块的开发
1、将模拟数据存入数据库
首先定义项目中静态文件的位置:
然后在setting.py里面注册静态文件的路径:
STATIC_URL = "static/"
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
接着写好模拟数据的JSON,如下所示:
在views.py写好存入数据库的函数:
# 将销售数据传入数据库
@csrf_exempt # 处理跨域
def saveSalesData(request):
# 获取前端传过来的值--request.body表示前端传过来的值,.decode()表示使中文不乱码,用json.loads转换为json格式
reqBody = json.loads(request.body.decode())
# print(reqBody)
try:
# 查找数据库中是否已经存在相同的yearName
year_name = Sales.objects.filter(yearName=reqBody['yearName'])
if year_name.exists():
return JsonResponse({'code': -1, 'msg': '该年数据已存在'})
# 如果不重复,在保存有关信息
sales_save = Sales(yearName=reqBody['yearName'],
yearSales=reqBody['yearSales'],
yearEvents=reqBody['yearEvents'],
monthSales=reqBody['monthSales'],
wareSales=reqBody['wareSales']
)
sales_save.save()
return JsonResponse({'code': 0, 'msg': 'success'})
# 防止异常处理太过宽泛
except AttributeError:
return JsonResponse({'code': -1, 'msg': '保存失败'})
在urls.py注册路由:
path("saveSalesData", views.saveSalesData),
用postman发起请求来存入数据:
去数据库中查看数据:
可以看出,该接口成功。
2、获取数据库中的数据
一共三个不同的可视化图表,但切换时,前端只发起同一个请求,后端返回sales表中的全部数据供前端处理(反正数据也不多,这个表本就是相当于独立静态的)
在views.py写好获取销售数据函数:
# 获取数据库中的销售数据
@csrf_exempt # 处理跨域
def getSalesData(request):
try:
salesData = (json.loads(serializers.serialize("json", Sales.objects.all())))
return JsonResponse({'code': 0, 'msg': 'success', 'salesData': salesData})
# 防止异常处理太过宽泛
except AttributeError:
return JsonResponse({'code': -1, 'msg': '获取失败'})
在urls.py注册路由:
path("getSalesData", views.getSalesData)
运行项目,并在postman里测试接口:
可以看到,数据获取成功,该接口有效。
五、库存管理模块的开发
获取仓库中的全部物品信息——前端只发起get请求,无需提交任何东西
获取仓库中某个物品信息——前端只需提交一个wareId来发起get请求,类似这种:
增加物品——前端提交一个Ware类,类似这种:(后端只需拿到wareName、wareCount、warePower,剩下的属性在models里面已经处理好了)
删除一个或多个物品——前端提交wareId的数组,类似这种:
编辑某个物品信息,前端提交一个Ware类,类似这种:(后端只需拿到更新后的wareName、wareCount、warePower,剩下的属性在models里面已经处理好了)
六、用户管理模块的开发
注意:该模块只对超级管理员开放,前端来做这个判断处理即可
获取系统中的全部用户信息——前端只发起get请求,无需提交任何东西
获取系统中某个用户信息——前端只需提交一个userId来发起get请求,类似这种:
增加用户——前端提交一个User类,类似这种:(后端只需拿到userName、userPassword、userPower,剩下的属性在models里面已经处理好了)
删除一个或多个物品——前端提交userId的数组,类似这种:
编辑某个用户信息,前端提交一个User类,类似这种:(后端只需拿到更新后的userName、userPassword、userPower,剩下的属性在models里面已经处理好了)
七、订单管理模块的开发
获取系统中的全部订单信息——前端只发起get请求,无需提交任何东西
获取系统中某个订单信息——前端只需提交一个orderId来发起get请求,类似这种:
增加订单——前端提交一个Order类,类似这种:(后端只需拿到userId、userName、wareCount、wareId、wareName,剩下的属性在models里面已经处理好了,注意此时仓库中对应wareId物品的数量会随之减少)
删除一个或多个订单——前端提交orderId的数组,类似这种:
八、临时订单模块的开发
获取系统中的临时订单信息——前端只发起get请求,无需提交任何东西(这个功能需要需要注意的点是,对于超级管理员返回全部临时订单信息,对于普通管理员返回该管理员对应的临时订单信息——后端通过匹配目前的登录信息userId做到)
增加临时订单——前端提交一个Cart类,类似这种:(后端只需拿到userId、userName、wareCount、wareId、wareName,剩下的属性在models里面已经处理好了)
删除一个或多个临时订单——前端提交cartId的数组,类似这种:
编辑某个临时订单信息,前端提交一个Cart类,类似这种:(后端只需拿到更新后的wareCount,剩下的属性不变)
将临时订单加入订单,前端提交一个Cart类数组,类似这种:(后端只需拿到该数组中每个项中的userId、userName、wareCount、wareId、wareName,并用这些信息新建订单即可,注意此时仓库中对应wareId的物品会随之减少、系统中会增加相应的订单、对应的临时订单随之消失)
九、与前端对接接口时产生的问题及解决
1、接口跨域问题
参考博客:
【解决Django与Vue的跨域问题】:
【vue+django跨域问题解决方案(前后端两种方案)】:blog.csdn.net/qq_41000891…
2、cookie被拦截的问题
参考博客:
【DJango碰到samesite无法登录Chrom(解决DJango无法获取Chrom的cookie问题)】:
此时发现虽然浏览器没有拦截cookie,似乎却仍没有保存到浏览器中,但是事实上已经能获取到cookie了。
3、Vue3+Django4项目整合
参考博客:
【vue+django的项目部署问题】:
【Vue3+Django4项目整合】:
十、项目部署
将数据库导出为sql文件:
部署参考博客:(记得python项目管理器一定要用1.9版本的)
zhuanlan.zhihu.com/p/476724776
宝塔终端中用到的命令:
//进入虚拟环境
source /www/wwwroot/82.157.247.180/82.157.247.180_venv/bin/activate
// 进入项目文件夹
cd /www/wwwroot/82.157.247.180
// 运行项目
python manage.py runserver
最终仍失败(数据库连接出了问题)。。。目前不知道怎么解决。
之后按照这篇博客blog.csdn.net/u010775335/…解决了数据库连接的问题,(本质是在终端新建一个数据库)。
python manage.py runserver命令之后没有报错,但是浏览器访问ip地址报下面的错:
之后更改鼠标所选部分ip(更改为127.0.0.1,在python项目管理器重启项目)
此时访问成功。
但是发现,这种问题:
解决办法:更改前端请求的根路由。
此时访问成功,但Redis部分出错,导致登录后无法获取登录信息:
此时查看登录接口的响应头,发现cookie没有被设置上:
解决办法:(将SESSION_COOKIE_SECURE设置为Flase,否则只能通过https访问了。)
此时仍然没有设置上cookie:
此时解决方案:
(即注释掉)
部署成功。
此时若想用已备案已解析的域名进行访问,则在网站-域名管理里面增加域名即可:
此时发现新的问题,cookie没有设置上:
此时解决方案:部署ssl证书:(腾讯云申请的免费ssl证书)
此时无法发起请求:
此时参考了这两篇博客:
【Django解决https配置问题】:
【Django应用配置https访问,将访问django的http请求转成https请求】:
仍没有用。。。
最后,改变前端请求跟路由即可:
项目链接:www.anlh.xyz/
nice!
十一、总结
本文详细地介绍了开发、部署一个简单的后台管理系统的的过程,包含了团队协作开发、Django开发、接口调试、前后端联调、宝塔部署的过程及遇到一些问题的解决方案,可供初学者进行学习参考。
附上项目源码:(欢迎各位看官给个star)
项目后端源码:XC0703/inventory_management_django_system (github.com)
转载自:https://juejin.cn/post/7224682815774408764