目录
项目立项
1 后台管理介绍
1.1 后台管理系统介绍
对于一家企业来说,拥有一个完整网站是很重要的事情,网站包括前端设计和后端的信息管理。简单来说,前端就是当我们打开一个网站的时候,眼睛所能看到的一切,而后台则是我们所看不到的一个信息管理系统。
网站后台管理系统主要是用于对网站前台的信息管理,如文字、图片、影音、和其他日常使用文件的发布、更新、删除等操作,同时也包括会员信息、订单信息、访客信息的统计和管理。简单来说就是对网站数据库和文件的快速操作和管理系统,以使得前台内容能够得到及时更新和调整。
2 后台管理模块
2.1 后台管理模块划分
商城后台管理系统一般分以下模块:商品管理、会员管理、订单管理、用户管理、角色权限 、报表业务、库存管理和广告业务等。
商品管理:商品分类的增删改查,品牌的增删改查及商品的增删改查功能
会员管理:商城的会员等级,优惠、积分、充值及会员的增删改查功能
订单管理:用户下单以后,订单的状态跟踪以及所有订单的统计功能
角色权限:增加新用户以后,为用户增加权限功能
报表业务:日销售报表、日新增用户报表、商品的销售报表、品牌销售报表、分类销售报表等
库存管理:商品的库存查看以及进货单、退货单、调价单的管理
广告业务:首页的轮播图、快讯等广告业务的管理
2.2 表设计及核心字段
- 用户 用户组及权限
from django.db import modelsfrom django.contrib.auth.models import AbstractUser#用户表class User(AbstractUser):"""用户表"""phone = models.CharField(max_length=11, unique=True, verbose_name='用户手机')last_login = models.DateTimeField()class Meta:db_table = 'user'def __str__(self):return self.username# 权限类型表 (角色)# id name# pkvarchar(30)class PermissionType(models.Model):"""权限类型的类型"""name = models.CharField(max_length=30, verbose_name='权限名称')class Meta:db_table = 'permission_type'def __str__(self):return self.name# 权限# idtitlecode_nametype_name# pkvarchar(30)varchar(30) fkclass Permission(models.Model):"""权限模型类"""title = models.CharField(max_length=30, verbose_name='权限名称')code_name = models.CharField(max_length=30, verbose_name='权限识别名称')type_name = models.ForeignKey(PermissionType, on_delete=models.CASCADE)class Meta:db_table = 'permissions'# 用户组# idname user permission# pkvarchar(30)m:mm:mclass UserGroup(models.Model):"""用户组的模型类"""name = models.CharField(max_length=30, verbose_name='名称')user = models.ManyToManyField(User)# 更改模型类: 从多对一改为多对多permission = models.ManyToManyField(Permission)class Meta:db_table = 'groups'def __str__(self):return self.name
- 商品表
自关联的场景:
比如在选地址时,省的下级有很多市, 市的下级有很多县,设计表结构时,可以采用三张表结合外键实现省市县的存储,如图:
这样设计过于复杂,也不利于提高查询效率。
采用自关联的方式,一张表即可以解决以上问题:
from django.db import models# 商品种类表# idname cid# pk varchar(30) 自关联字段class Cate(models.Model):"""商品分类"""name = models.CharField(max_length=30)# 自关联: 自己关联自己cid = models.ForeignKey('self', related_name='subs',on_delete=models.CASCADE, null=True, blank=True)class Meta:db_table = 'cate'def __str__(self):return self.name# 实例化mobile = Cate(name="手机")# mobile.save()手机类别是顶级类别 其cid 为null# huawei = Cate(name="华为手机") 子类别# huawei.cid = mobile 华为手机的父类别就是mobile# huawei.save()# xiaomi = Cate(name="小米手机")# xiaomi.cid = mobile# xiaomi.save()# mobile.subs.all()手机类别 反向查询# 分类分组# idname categroup_urlsequence# pk varchar(30)1:1varchar(100) int#默认http://jd.com0class Group(models.Model):"""一组 一类别"""name = models.CharField("组的名字", max_length=30)cate = models.OneToOneField(Cate, on_delete=models.CASCADE)group_url = models.CharField(max_length=100, verbose_name="频道的地址链接", default="http://jd.com")sequence = models.IntegerField(verbose_name="组内的顺序", default=0)class Meta:db_table = 'cate_group'def __str__(self):return self.name# 品牌表# id first_name namelogo# pk varchar(10)varchar(20)varchar(200)#默认Noneclass Brand(models.Model):"品牌"first_name = models.CharField(verbose_name="品牌首字母", max_length=10, default=None)name = models.CharField(verbose_name="品牌名",max_length=20)logo = models.CharField(max_length=200, verbose_name="品牌logo")class Meta:db_table = "brand"def __str__(self):return self.name# SPU# id pk#namevarchar(20)'spu'#brand外键 "品牌"#category1 外键related_name="cate1_spu"'一级类别'#category2 #category3#comment_numintdefault 0"评论量"#salesintdefult 0 "销量"#desc_detailtext "详细描述"#desc_pack text"包装信息"#desc_service text '售后服务'class SPU(models.Model):"商品SPU"name = models.CharField(max_length=20, verbose_name="spu")brand = models.ForeignKey(Brand, on_delete=models.PROTECT, verbose_name='品牌')category1 = models.ForeignKey(Cate, on_delete=models.PROTECT, related_name='cate1_spu', verbose_name='一级类别')category2 = models.ForeignKey(Cate, on_delete=models.PROTECT, related_name='cate2_spu', verbose_name='二级类别')category3 = models.ForeignKey(Cate, on_delete=models.PROTECT, related_name='cate3_spu', verbose_name='三级类别')comment_num = models.IntegerField(verbose_name="评论量",default=0)sales = models.IntegerField(verbose_name="销量",default=0)desc_detail = models.TextField(default='', verbose_name='详细介绍')desc_pack = models.TextField(default='', verbose_name='包装信息')desc_service = models.TextField(default='', verbose_name='售后服务')class Meta:db_table = 'spu'def __str__(self):return self.name# 商品表# name varchar(100)# describe varchar(500)#price8,2#stock int default=0#sales int default =0#detail text#image_defaultvarchar(300)#is_selling booleanTrue#cate 外键#spu 外键class Goods(models.Model):"""商品表名称、描述、价格、颜色、版本、库存、商品详情、商品默认图片、分类"""name = models.CharField(max_length=100)describe = models.CharField(max_length=500)price = models.DecimalField(max_digits=8, decimal_places=2)stock = models.IntegerField(default=0)detail = models.TextField()image_default = models.CharField(verbose_name="默认图片的地址",max_length=300)is_selling = models.BooleanField(default=True)sales = models.IntegerField(verbose_name="销量",default=0)cate = models.ForeignKey(Cate, on_delete=models.CASCADE)spu = models.ForeignKey(SPU,on_delete=models.CASCADE, verbose_name='商品')class Meta:db_table = 'goods'def __str__(self):return self.name# 商品SPU规格#spu 外键'商品SPU'#name varchar(20)"规格名称"class SPUSpecification(models.Model):"""商品SPU规格"""spu = models.ForeignKey(SPU, on_delete=models.CASCADE, related_name='specs', verbose_name='商品SPU')name = models.CharField(max_length=20, verbose_name='规格名称')class Meta:db_table = 'tb_spu_specification'verbose_name = '商品SPU规格'verbose_name_plural = verbose_namedef __str__(self):return '%s: %s' % (self.spu.name, self.name)#规格选项class SpecificationOption(models.Model):"""规格选项"""spec = models.ForeignKey(SPUSpecification, related_name='options', on_delete=models.CASCADE, verbose_name='规格')value = models.CharField(max_length=20, verbose_name='选项值')class Meta:db_table = 'tb_specification_option'verbose_name = '规格选项'verbose_name_plural = verbose_namedef __str__(self):return '%s - %s' % (self.spec, self.value)class SKUSpecification(models.Model):"""SKU具体规格11 1"""sku = models.ForeignKey(Goods, related_name='specs', on_delete=models.CASCADE, verbose_name='sku')spec = models.ForeignKey(SPUSpecification, on_delete=models.PROTECT, verbose_name='规格名称')option = models.ForeignKey(SpecificationOption, on_delete=models.PROTECT, verbose_name='规格值')class Meta:db_table = 'sku_specification'verbose_name = 'SKU规格'verbose_name_plural = verbose_namedef __str__(self):return '%s: %s-%s' % (self.sku, self.spec.name, self.option.value)class SkuImage(models.Model):"""商品图片"""good = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品')img = models.CharField(verbose_name="图片的地址", max_length=300)class Meta:db_table = 'tb_sku_img'verbose_name = '商品图片'verbose_name_plural = verbose_namedef __str__(self):return self.img
- 优惠券
from django.db import modelsfrom cateadmin.models import Goodsfrom users.models import User#优惠类型class CouponType(models.Model):"""优惠类型"""type_name = models.CharField(max_length=30, verbose_name='优惠类型名称')def __str__(self):return self.type_nameclass Meta:db_table = 'coupon_type'#优惠券类型具体的值class CouponTypeValue(models.Model):"""优惠券类型具体的值"""#优惠券coupon = models.ForeignKey(Coupon, on_delete=models.CASCADE)#优惠券类型coupon_type = models.ForeignKey(CouponType, on_delete=models.CASCADE)# 类型的值value = models.IntegerField(verbose_name="类型具体的值", default=0)def __str__(self):return self.valueclass Meta:db_table = 'coupon_value'#优惠券class Coupon(models.Model):"""优惠券"""name = models.CharField(max_length=100, verbose_name='优惠券名称')brief = models.TextField(verbose_name='优惠券介绍')coupon_type = models.ForeignKey(CouponType,on_delete=models.CASCADE,blank=True, null=True)good = models.ForeignKey(Goods,on_delete=models.CASCADE, verbose_name='商品ID', blank=True, null=True, help_text='某个商品id')coupon_num = models.IntegerField(verbose_name='张数', default=0)start_time = models.DateTimeField(verbose_name='优惠券开始使用时间')over_time = models.DateTimeField(verbose_name='优惠券结束使用时间')start_get_time = models.DateTimeField(verbose_name='开始领卷的时间')end_get_time = models.DateTimeField(verbose_name='领卷的结束时间')remark = models.CharField(max_length=300, verbose_name='备注', null=True, blank=True)class Meta:db_table = 'coupon'def __str__(self):return self.name#用户领券表class GetCoupon(models.Model):"""用户领券表"""coupon = models.ForeignKey(Coupon, on_delete=models.CASCADE)user = models.ForeignKey(User, verbose_name='拥有者', on_delete=models.CASCADE)STATUS_CHOICE = ((0, '未使用'), (1, '已使用'), (2, '已过期'))status = models.IntegerField(choices=STATUS_CHOICE, verbose_name='优惠券状态', default=0)get_time = models.DateTimeField(verbose_name='领券时间', auto_now_add=True)use_time = models.DateTimeField(verbose_name='使用时间', blank=True, null=True)class Meta:verbose_name_plural = '用户持券表'def __str__(self):return '{}-{}-{}'.format(self.user, self.coupon, self.get_status_display())
- 订单
from django.db import modelsfrom users.models import Userfrom cateadmin.models import Goodsclass OrderInfo(models.Model):"""订单信息"""PAY_METHOD_CHOICE = ((1, '支付宝'),(2,'微信支付'),(3,'银联支付'),(4,'货到付款'))order_id = models.CharField(max_length=64, primary_key=True, verbose_name="订单号")user = models.ForeignKey(User, related_name='orders', on_delete=models.PROTECT, verbose_name="下单用户")total_count = models.IntegerField(default=1, verbose_name="商品总数")total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="商品总金额")freight = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="运费")pay_method = models.SmallIntegerField(default=1, choices=PAY_METHOD_CHOICE, verbose_name="支付方式")# 1-待支付,2-待发货,3-待收货,4-待评价,5-完成status = models.SmallIntegerField(default=1, verbose_name="订单状态")created_time = models.DateTimeField(verbose_name="订单的创建时间")class Meta:db_table = "tb_order_info"verbose_name = '订单基本信息'verbose_name_plural = verbose_namedef __str__(self):return self.order_idclass OrderGoods(models.Model):"""订单商品"""#购买的商品sku = models.ForeignKey(Goods, on_delete=models.PROTECT, verbose_name="订单商品")count = models.IntegerField(default=1, verbose_name="数量")price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="单价") order = models.ForeignKey(OrderInfo, related_name='skus', on_delete=models.CASCADE, verbose_name="订单")# comment = models.TextField(default="", verbose_name="评价信息")# score = models.SmallIntegerField(default=5, verbose_name='满意度评分')# is_anonymous = models.BooleanField(default=False, verbose_name='是否匿名评价')# is_commented = models.BooleanField(default=False, verbose_name='是否评价了')class Meta:db_table = "tb_order_goods"verbose_name = '订单商品'verbose_name_plural = verbose_namedef __str__(self):return self.sku.name#支付信息class Payment(models.Model):"""支付信息"""trade_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="支付编号")order = models.ForeignKey(OrderInfo, on_delete=models.CASCADE, verbose_name='订单')class Meta:db_table = 'tb_payment'verbose_name = '支付信息'verbose_name_plural = verbose_name
3 仓库创建
3.1 Git回顾
git简介
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务端软件支持。
git与svn的区别
Git 不仅仅是版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。
如果你是一个具有使用 SVN 背景的人,你需要做一定的思想转换,来适应 Git 提供的一些概念和特征。
Git 与 SVN 区别点:
- 1、Git 是分布式的,SVN 是集中式:这是 Git 和其它非分布式的版本控制系统,最核心的区别,例如 SVN,CVS 等
- **2、Git 把内容按元数据方式存储,而 SVN 是按文件:**所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等的文件夹里。
- **3、Git 分支和 SVN 的分支不同:**分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
- **4、Git 没有一个全局的版本号,而 SVN 有:**目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
- **5、Git 的内容完整性要优于 SVN:**Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
4 git基本操作
创建仓库
在项目根目录下执行如下:
git init
或者直接克隆远程仓库:
git clone url
配置签名
局部配置
git config user.name "laufing"git config user.email "123@163.com"
全局配置
git config --global user.name "donghaiming"git config --global user.email "haiming_dong@126.com"
提交版本
# 将一个文件增加到暂存区git add test.txt# 将所有文件增加到暂存区git add ./# 提交版本 到本地仓库git commit -m '版本号或描述信息'
查看日志
git loggit log --oneline# 日志过长时,按行显示git reflog # 历史操作
版本回退
git reset --hard HEAD^# 回退到当前版本的前一个版本HEAD^^表示回退到当前版本的前两个版本git reset --hard HEAD`1# 回退到当前版本的前一个版本HEAD`5表示回退到当前版本的前5个版本git reset --hard 版本号序号# 回退到指定的版本
查看状态
git status
撤消 工作区 的修改
git checkout--<文件名>
撤消暂存区的修改
git reset HEAD <文件名># 先撤消暂存区的修改,从暂存区撤回git checkout -- <文件名># 再撤消工作区修改
对比文件的内容
对比本地仓库(HEAD) 与工作区文件的差异
git diff HEAD -- <文件名>
对比两个版本之间某个文件的不同
git diff HEAD HEAD^ -- <文件名>
删除文件
rm <文件名>
创建远程仓库
Git分支
查看分支
git branch -v
创建分支
# 创建分支,不切换git branch <分支名># 切换分支git switch <分支名>orgit checkout <分支名># 创建分支,并切换git checkout -b <分支名># 创建dev分支,并切换git checkout -b dev
合并分支到主分支
# 1. 切换到主分支git checkout master# 2. 合并分支dev 到主分支git merge dev
注意:合并分支内容需要先切换到主分支上
删除分支
# 删除一个分支git branch -d <分支名># 删除dev分支git branch -d dev
查看合并后的日志
git log --pretty=oneline
解决合并冲突
某分支和master中同一个文件都有改动时
,合并该分支到主分支时会有冲突,如下:
解决方案:
- 在master主分支上,使用vim打开冲突的文件,会看到如下内容:
- 手动删除冲突的符号,得到如下内容:
- 保存退出,esc 然后 shift + zz
- 将冲突文件增加到暂存区,git add 文件名
- 提交到本地仓库, git commit -m ‘描述’
练习:
- 初始化一个本地仓库,在主分支master上创建一个文件a.txt,添加内容为master 这个单词,最终提交;
- 创建一个分支dev并切换,然后修改a.txt文件,添加内容为branch dev, 最终提交;
- 切换到主分支,修改a.txt文件,添加内容before merge,最终提交。
- 合并dev分支到master主分支,并解决冲突问题。
最终得到如下结果:
禁用快进合并
有时将分支的更新合并到上游的master主分支时,会出现如下情况:(此时默认采用fast-forward 快进模式)
这时可以禁用快进模式:
git merge --no-ff -m '禁用快速合并的描述' dev
bug分支
软件开发中,bug就像家常便饭,有了bug就需要修复,在git中,由于分支
功能强大,所以, 每个bug都可以通过一个新的临时分支
来修复,修复后,合并分支,然后将临时分支删除。
接到一个修复bug的任务,当手头的工作还没有完成时,git中还提供了一个stash功能, 可以把当前工作状态保存起来,等修复完bug后,再恢复状态继续工作。
git stash# 保存工作现场(添加到暂存区的文件,尚未提交到本地仓库)git stash list# 列出工作现场git stash pop# 恢复工作现场
git 开发模式
master,负责版本发布、历史记录;
dev, 负责开发
其他分支,适当的时候合并到dev分支。