目录
一、项目需求分析
1、项目介绍
1、技术难点
2、系统功能
3、项目环境
4、后台管理页面
二、数据库模型设计
一、准备工作
二、用户认证数据库模型设计
1、 app/users/models.py
三、商品管理模型设计
1、插件安装(xadmin和DjangoUeditor)
2、子应用配置
3、商品分类model 设计
1、app/goods/models.py的代码:
4、trade交易的model设计
1、app/trade/models.py 的设计:
5、用户操作的model设计
1、app/user_operation/models.py的设计:
四、Xadmin后台管理
1、路由配置
2、子应用Xadmin注册
3、用户认证注册
4、商品管理注册
5、交易管理注册
6、用户操作管理注册:
五、数据库迁移
六、商品数据批量导入
七、一些常遇到的问题以及解决方法
1、问题一: 时区问题
2、问题二: 数据库模型问题
3、问题三: 数据库模型导入问题
八、最终后端设计结果展示:
一、项目需求分析
1、项目介绍
1、技术难点
2、系统功能
3、项目环境
4、后台管理页面
二、数据库模型设计
一、准备工作
二、用户认证数据库模型设计
1、 app/users/models.py
三、商品管理模型设计
1、插件安装(xadmin和DjangoUeditor)
2、子应用配置
3、商品分类model 设计
4、trade交易的model设计
5、用户操作的model设计
四、Xadmin后台管理
1、路由配置
2、子应用Xadmin注册
3、用户认证注册
4、商品管理注册
5、交易管理注册
6、用户操作管理注册:
五、数据库迁移
六、商品数据批量导入
七、一些常遇到的问题以及解决方法
1、问题一: 时区问题
2、问题二: 数据库模型问题
3、问题三: 数据库模型导入问题
八、最终后端设计结果展示:
后台呈现的界面将会以以下界面显示出来。
声明:此次项目设计是在windws下完成的。
# app/users/models.py
from datetime import datetime
from django.contrib.auth.models import AbstractUser
from django.db import models
class UserProfile(AbstractUser):
"""用户信息"""
GENDER_CHOICES = (
("male", "男"),
("female", "女")
)
# 用户用手机注册,所以姓名,生日和邮箱可以为空,verbose是列属性在后台admin显示的名称。
name = models.CharField(verbose_name="姓名", max_length=30, null=True, blank=True)
birthday = models.DateField(verbose_name="出生年月", null=True, blank=True)
gender = models.CharField(verbose_name="性别", max_length=6, choices=GENDER_CHOICES,default="female")
"""
null 是针对数据库而言,如果 null=True, 表示数据库的该字段可以为空。
blank 是针对表单的,如果 blank=True,表示你的表单填写该字段的时候可以不填
"""
mobile = models.CharField("电话", max_length=11, null=True, blank=True)
email = models.EmailField("邮箱", max_length=100, null=True, blank=True)
# 元数据操作
class Meta:
# 后台管理显示单数和复数的名称
verbose_name = "用户信息"
verbose_name_plural = verbose_name
def __str__(self):
return self.username
class VerifyCode(models.Model):
code = models.CharField("验证码", max_length=10)
mobile = models.CharField("电话", max_length=11)
add_time = models.DateTimeField("添加时间", default=datetime.now)
class Meta:
verbose_name = "短信验证"
verbose_name_plural = verbose_name
def __str__(self):
return self.code
模型文件写完了,怎么生效呢?要想替换系统的用户,还要在settings中配置
# NewShopProject/settings.py
#重载系统的用户,让UserProfile生效
AUTH_USER_MODEL = 'app.users.UserProfile'
mkdir app/extra_apps
git clone https://github.com/twz915/DjangoUeditor3/
git clone -b django2 https://github.com/sshwsfc/xadmin.git
# Xadmin安装方法一: 源码安装进入目录,
pip install -i https://pypi.douban.com/simple -r requirements.txt
python setup.py install
# xadmin安装方法二: 在线安装
pip install git+git://github.com/sshwsfc/xadmin.git@django2
注意:在安装插件时,目录必须切换到当前目录,不然会安装失败。
# ShopProject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'DjangoUeditor',
'xadmin',
# django-crispy-forms 是对django form在html页面呈现方式进行管理的一个第三方插件
'crispy_forms',
# 给admin后台管理提供强大的回滚和恢复功能
'reversion',
'app.users',
'app.goods',
'app.trade',
'app.user_operation',
]
# 设置上传文件的路径: http://xxxx/media/hello,png
MEDIA_URL = "/media/"
# 设置media的保存路径: media/hello.png
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
#app/goods/models.py
from django.db import models
from datetime import datetime
from DjangoUeditor.models import UEditorField
# Create your models here.
class GoodsCategory(models.Model):
"""商品分类"""
CATEGORY_TYPE = (
(1, "一级类目"),
(2, "二级类目"),
(3, "三级类目"),
)
name = models.CharField('类别名', default="", max_length=30, help_text="类别名")
code = models.CharField("类别code", default="", max_length=30, help_text="类别code")
desc = models.TextField("类别描述", default="", help_text="类别描述")
# 目录树级别
category_type = models.IntegerField("类目级别", choices=CATEGORY_TYPE,help_text="类目级别")
# 一级分类: 电器 二级分类: 微波炉、电磁炉...
# 一级分类:二级分类 = 1:N
# 设置models有一个指向自己的外键
parent_category = models.ForeignKey("self", on_delete=models.CASCADE,null=True, blank=True, verbose_name="父类目级别",help_text="父目录",related_name="sub_cat")
is_tab = models.BooleanField("是否导航", default=False, help_text="是否导航")
add_time = models.DateTimeField("添加时间", default=datetime.now)
class Meta:
verbose_name = "商品类别"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Goods(models.Model):
"""商品"""
goods_sn = models.CharField("商品唯一货号", max_length=50, default="")
name = models.CharField("商品名", max_length=100, )
click_num = models.IntegerField("点击数", default=0)
sold_num = models.IntegerField("商品销售量", default=0)
fav_num = models.IntegerField("收藏数", default=0)
goods_num = models.IntegerField("库存数", default=0)
market_price = models.FloatField("市场价格", default=0)
shop_price = models.FloatField("本店价格", default=0)
goods_brief = models.TextField("商品简短描述", max_length=500)
# MEDIA_ROOT = os.path.join(BASE_DIR, "media") ===== media/goods/images/
goods_desc = UEditorField(verbose_name=u"内容", imagePath="goods/images/",
width=1000, height=300,filePath="goods/files/", default='')
ship_free = models.BooleanField("是否承担运费", default=True)
# 首页中展示的商品封面图
goods_front_image = models.ImageField(upload_to="goods/images/", null=True,
blank=True, verbose_name="封面图")
# 首页中新品展示
is_new = models.BooleanField("是否新品", default=False)
# 商品详情页的热卖商品,自行设置
is_hot = models.BooleanField("是否热销", default=False)
add_time = models.DateTimeField("添加时间", default=datetime.now)
# 商品分类:商品: 1:N
category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE,
verbose_name="商品类目")
class Meta:
verbose_name = '商品信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class GoodsImage(models.Model):
"""商品轮播图"""
# 图片对象
image = models.ImageField(upload_to="goods/images", verbose_name="图片",null=True, blank=True)
add_time = models.DateTimeField("添加时间", default=datetime.now)
# 商品:商品轮播图: 1:N
goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品", related_name="images")
class Meta:
verbose_name = '商品轮播'
verbose_name_plural = verbose_name
def __str__(self):
return self.goods.name
class Banner(models.Model):
"""
首页轮播的商品: 首页的商品轮播图片是大图,跟商品详情里面的图片不一样,所以要单独写一个首页
轮播图model
"""
image = models.ImageField(upload_to='banner', verbose_name="轮播图片")
index = models.IntegerField("轮播顺序", default=0)
add_time = models.DateTimeField("添加时间", default=datetime.now)
# 商品:轮播商品
# goods = models.ForeignKey(Goods, on_delete=models.CASCADE,verbose_name="商品")
class Meta:
verbose_name = '首页轮播'
verbose_name_plural = verbose_name
def __str__(self):
# return self.goods.name
return self.image
class HotSearchWords(models.Model):
"""
搜索栏下方热搜词
"""
keywords = models.CharField("热搜词", default="", max_length=20)
index = models.IntegerField("排序", default=0)
add_time = models.DateTimeField("添加时间", default=datetime.now)
class Meta:
verbose_name = '热搜排行'
verbose_name_plural = verbose_name
def __str__(self):
return self.keywords
class GoodsCategoryBrand(models.Model):
"""
某一大类下的宣传商标
"""
name = models.CharField("品牌名", default="", max_length=30, help_text="品牌名")
desc = models.TextField("品牌描述", default="", max_length=200, help_text="品牌描述")
image = models.ImageField(max_length=200, upload_to="brands/")
add_time = models.DateTimeField("添加时间", default=datetime.now)
# category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE,related_name='brands', null=True, blank=True,verbose_name="商品类目")
class Meta:
verbose_name = "宣传品牌"
verbose_name_plural = verbose_name
# 重新设置数据库表的名称。
db_table = "goods_goodsbrand"
def __str__(self):
return self.name
class IndexAd(models.Model):
"""
商品广告
"""
# 商品广告: 分类: 1:N
category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE,related_name='category', verbose_name="商品类目")
# 商品广告: 商品: 1:N
goods = models.ForeignKey(Goods, on_delete=models.CASCADE,related_name='goods')
class Meta:
verbose_name = '首页广告'
verbose_name_plural = verbose_name
def __str__(self):
return self.goods.name
#app/trade/models.py
# Create your models here.
# trade/models.py
from datetime import datetime
from django.db import models
from app.goods.models import Goods
# get_user_model方法会去setting中找AUTH_USER_MODEL
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class ShoppingCart(models.Model):
"""
购物车
"""
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户")
goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品")
nums = models.IntegerField("购买数量",default=0)
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = '购物车'
verbose_name_plural = verbose_name
unique_together = ("user", "goods")
def __str__(self):
return "%s(%d)".format(self.goods.name, self.nums)
class OrderInfo(models.Model):
"""
订单信息
"""
ORDER_STATUS = (
("TRADE_SUCCESS", "成功"),
("TRADE_CLOSED", "超时关闭"),
("WAIT_BUYER_PAY", "交易创建"),
("TRADE_FINISHED", "交易结束"),
("paying", "待支付"),
)
PAY_TYPE = (
("alipay", "支付宝"),
("wechat", "微信"),
)
# 订单与用户关联
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户")
# 订单号一定要唯一(unique=True)
order_sn = models.CharField("订单编号",max_length=30, null=True, blank=True,
unique=True)
# 微信支付会用到用户操作的model设计
nonce_str = models.CharField("随机加密串",max_length=50, null=True,
blank=True, unique=True)
# 支付宝交易号
trade_no = models.CharField("交易号",max_length=100, unique=True, null=True,
blank=True)
#支付状态
pay_status = models.CharField("订单状态",choices=ORDER_STATUS,
default="paying", max_length=30)
pay_time = models.DateTimeField("支付时间",null=True, blank=True)
# 订单的支付类型
pay_type = models.CharField("支付类型",choices=PAY_TYPE, default="alipay",
max_length=10)
post_script = models.CharField("订单留言",max_length=200)
order_mount = models.FloatField("订单金额",default=0.0)
# 用户信息
address = models.CharField("收货地址",max_length=100, default="")
signer_name = models.CharField("签收人",max_length=20, default="")
singer_mobile = models.CharField("联系电话",max_length=11)
add_time = models.DateTimeField("添加时间",default=datetime.now)
class Meta:
verbose_name = "订单信息"
verbose_name_plural = verbose_name
def __str__(self):
return str(self.order_sn)
class OrderGoods(models.Model):
"""
订单内的商品详情
"""
# 一个订单对应多个商品
order = models.ForeignKey(OrderInfo, on_delete=models.CASCADE,verbose_name="订单信息", related_name="goods")
# 两个外键形成一张关联表
goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品")
goods_num = models.IntegerField("商品数量",default=0)
add_time = models.DateTimeField("添加时间",default=datetime.now)
class Meta:
verbose_name = "订单商品"
verbose_name_plural = verbose_name
def __str__(self):
return str(self.order.order_sn)
# app/user_operation/models.py
# Create your models here.
from datetime import datetime
from django.db import models
from app.goods.models import Goods
from django.contrib.auth import get_user_model
# User = get_user_model()
from app.users.models import UserProfile
class UserFav(models.Model):
"""
用户收藏操作
"""
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name="用户")
goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品", help_text="商品id")
add_time = models.DateTimeField("添加时间",default=datetime.now)
class Meta:
verbose_name = '用户收藏'
verbose_name_plural = verbose_name
unique_together = ("user", "goods") # 联合唯一
def __str__(self):
return self.user.username
class UserAddress(models.Model):
"""
用户收货地址
"""
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name="用户"
)
province = models.CharField("省份",max_length=100, default="")
city = models.CharField("城市",max_length=100, default="")
district = models.CharField("区域",max_length=100, default="")
address = models.CharField("详细地址",max_length=100, default="")
signer_name = models.CharField("签收人",max_length=100, default="")
signer_mobile = models.CharField("电话",max_length=11, default="")
add_time = models.DateTimeField("添加时间",default=datetime.now)
class Meta:
verbose_name = "收货地址"
verbose_name_plural = verbose_name
def __str__(self):
return self.address
class UserLeavingMessage(models.Model):
"""
用户留言
"""
MESSAGE_CHOICES = (
(1, "留言"),
(2, "投诉"),
(3, "询问"),
(4, "售后"),
(5, "求购")
)
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name="用户")
message_type = models.IntegerField(default=1, choices=MESSAGE_CHOICES,
verbose_name="留言类型", help_text=u"留言类型: 1(留言),2(投诉),3(询问),4(售后),5(求购)")
subject = models.CharField("主题", max_length=100, default="")
message = models.TextField("留言内容", default="", help_text="留言内容")
# upload_to指文件上传后的位置,默认存储到media目录中,(在settings文件中设置的)。
file = models.FileField(upload_to="message/images/", verbose_name="上传的文件", help_text="上传的文件")
add_time = models.DateTimeField("添加时间", default=datetime.now)
class Meta:
verbose_name = "用户留言"
verbose_name_plural = verbose_name
def __str__(self):
return self.subject
所有的模型文件建立好之后,我们开始进行admin后台管理,需要注意的是,刚才的四个子应用都对应有以下六个python文件的,自行建立好。
# NewShopProject/urls.py
urlpatterns = [
# path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('ueditor/', include('DjangoUeditor.urls')),
]
# app/users/admin.py
import xadmin
from xadmin import views
from .models import VerifyCode
class BaseSetting(object):
"""xadmin的基本配置"""
# 开启主题切换功能
enable_themes = True
# 支持切换主题
use_bootswatch = True
class GlobalSettings(object):
"""xadmin的全局配置"""
# 设置站点标题
site_title = "乖乖电商平台"
# 设置站点的页脚
site_footer = "http://www.cnblogs.com/qianqian/"
# 设置菜单折叠,在左侧,默认的
menu_style = "accordion"
class VerifyCodeAdmin(object):
# 列表展示的字段
list_display = ['code', 'mobile', "add_time"]
xadmin.site.register(VerifyCode, VerifyCodeAdmin)
xadmin.site.register(views.BaseAdminView, BaseSetting)
xadmin.site.register(views.CommAdminView, GlobalSettings)
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'app.users'
verbose_name = "用户管理" #xadmin左侧导航栏显示的信息
# app/users/__init__.py
default_app_config = 'app.users.apps.UsersConfig'
# app/goods/admin.py
import xadmin
from app.goods.models import GoodsImage, Goods, GoodsCategory, Banner, GoodsCategoryBrand, HotSearchWords, IndexAd
class GoodsAdmin(object):
#后台可以管理商品信息并设置页面的配置,添加商品时可嵌入添加商品的图片(多个、轮播图)
# 显示的列
list_display = ["name", "click_num", "sold_num", "fav_num", "goods_num","market_price","shop_price", "goods_brief", "is_new", "is_hot","add_time"]
# 可以搜索的字段
search_fields = ['name', ]
# 列表页可以直接编辑的
list_editable = ["is_hot", ]
# 过滤器
list_filter = ["name", "click_num", "sold_num", "fav_num", "goods_num",
"market_price",
"shop_price", "is_new", "is_hot", "add_time",
"category__name"]
# 富文本编辑器
style_fields = {"goods_desc": "ueditor"}
# 在添加商品的时候可以添加商品图片
class GoodsImagesInline(object):
model = GoodsImage
# 不显示的字段名称
exclude = ["add_time"]
# 控制初始表单数量,默认为3
extra = 1
style = 'tab'
inlines = [GoodsImagesInline]
class GoodsCategoryAdmin(object):
"""商品分类的后台设置"""
list_display = ["name", "category_type", "parent_category", "add_time"]
list_filter = ["category_type", "parent_category", "name"]
search_fields = ['name']
class GoodsBrandAdmin(object):
#list_display = ["category", "image", "name", "desc"]
list_display = ["image", "name", "desc"]
# def get_context(self):
# context = super(GoodsBrandAdmin, self).get_context()
# if 'form' in context:
# context['form'].fields['category'].queryset =GoodsCategory.objects.filter(category_type=1)
# return context
class BannerGoodsAdmin(object):
# list_display = ["goods", "image", "index"]
list_display = [ "image", "index"]
class HotSearchAdmin(object):
list_display = ["keywords", "index", "add_time"]
class IndexAdAdmin(object):
# list_display = ["category", "goods"]
list_display = ["goods"]
#注册
xadmin.site.register(Goods, GoodsAdmin)
xadmin.site.register(GoodsCategory, GoodsCategoryAdmin)
xadmin.site.register(Banner, BannerGoodsAdmin)
xadmin.site.register(GoodsCategoryBrand, GoodsBrandAdmin)
xadmin.site.register(HotSearchWords, HotSearchAdmin)
xadmin.site.register(IndexAd, IndexAdAdmin)
# app/goods/apps.py
from django.apps import AppConfig
class GoodsConfig(AppConfig):
name = 'app.goods'
verbose_name = '商品管理'
default_app_config = 'app.goods.apps.GoodsConfig'
效果 展示:
app/trade/admin.py的配置
# Register your models here.
import xadmin
from .models import ShoppingCart, OrderInfo, OrderGoods
class ShoppingCartAdmin(object):
list_display = ["user", "goods", "nums", ]
class OrderInfoAdmin(object):
list_display = ["user", "order_sn", "trade_no", "pay_status", "post_script", "order_mount",
"order_mount", "pay_time", "add_time"]
class OrderGoodsInline(object):
model = OrderGoods
exclude = ['add_time', ]
extra = 1
style = 'tab'
inlines = [OrderGoodsInline, ]
xadmin.site.register(ShoppingCart, ShoppingCartAdmin)
xadmin.site.register(OrderInfo, OrderInfoAdmin)
app/trade/apps.py的配置
from django.apps import AppConfig
class TradeConfig(AppConfig):
name = 'app.trade'
verbose_name = '交易管理'
app/trade/__init__.py的配置
default_app_config = 'app.trade.apps.TradeConfig'
迁移数据库后展示效果:
app/user_operation/admin.py的配置
# Register your models here.
import xadmin
from .models import UserFav, UserLeavingMessage, UserAddress
class UserFavAdmin(object):
list_display = ['user', 'goods', "add_time"]
class UserLeavingMessageAdmin(object):
list_display = ['user', 'message_type', "message", "add_time"]
class UserAddressAdmin(object):
list_display = ["signer_name", "signer_mobile", "district", "address"]
xadmin.site.register(UserFav, UserFavAdmin)
xadmin.site.register(UserAddress, UserAddressAdmin)
xadmin.site.register(UserLeavingMessage, UserLeavingMessageAdmin)
app/user_operation/apps.py的配置
from django.apps import AppConfig
class UserOperateConfig(AppConfig):
name = 'app.user_operation'
verbose_name = "操作管理"
app/user_operation/__init__.py的配置
default_app_config = 'app.user_operation.apps.UserOperateConfig'
数据库迁移更新后的效果为:
所有的模型建立完成之后,我们需要进行数据库迁移
# NewShopProject/settings
# 设置上传文件的路径: http://xxxx/media/hello,png
MEDIA_URL = "/media/"
# 设置media的保存路径: media/hello.png
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# NewShopProject/urls.py
urlpatterns = [
# path('admin/', admin.site.urls),
#使用xadmin进行我们的后台管理
path('xadmin/',xadmin.site.urls),
#富文本编辑时我们的路由配置
path('ueditor/', include('DjangoUeditor.urls')),
#文件,可以通过路由的方式访问用户上传的文件/图片/视频等。。。。
path('media/',serve,{'document_root':MEDIA_ROOT}),
]
出现的问题:
django.db.utils.OperationalError: no such table: main.goods_goods__old
pip install django==2.2
django.core.exceptions.ImproperlyConfigured: Requested setting UEDITOR_SETTINGS,
but settings are not configured. You must either define the environment variable
DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ShopProject.settings")
django.setup()
from app.goods.models import Goods, GoodsCategory, GoodsImage
到此,网上商城项目的后端部分已经设计完毕,接下来就开始项目的前端部分和API部分的学习了。