1. 环境搭建
1.1 Python 虚拟环境
mkdir MxShop
# 创建虚拟环境,修改为豆瓣源
pipenv install
1.2 Vue 环境
# 安装 node.js https://nodejs.org/en/
# 安装 nrm,并切换为淘宝源
npm i nrm -g # 全局安装
nrm ls # 查看当前可用镜像源地址
nrm use taobao # 切换镜像源为 淘宝,速度会快很多
C:Usershj>nrm ls
npm -------- https://registry.npmjs.org/
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
* taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
# 安装依赖
npm install
# 运行 vue 项目
npm run dev
1.3 项目环境
1.3.1 创建项目
# 安装 Django
pipenv install django==2.0.2 --skip-lock
# 创建项目,使用 pycharm 创建新的 Django 项目
项目名称:MxShop
app:users
# 不使用 Django 自带的 admin,而是 xadmin,在使用 pycharm 安装时不勾选 Enable Django admin
其他模块安装:
pipenv install djangorestframework
pipenv install markdown
pipenv install django-filter
pipenv install pillow # 图片处理
pipenv install pymysql
1.3.2 配置项目结构
项目根目录分部创建:
apps
:Python
包文件extra_apps
:Python
包文件media
:文件夹db_tools
:文件夹
并将 apps、extra_apps
标记为 sources root
(pycharm
中鼠标右键选择相应文件夹,选择 Mark Directory as
-- sources root
),并在 settings
配置好路径:
import sys
sys.path.insert(0, BASE_DIR)
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps'))
配置上面后,将 users
拷贝到 apps
,项目整体结构如下图所示:
settings
中添加 djangorestframework
:
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'rest_framework', # 新增
]
1.3.3 配置 MySQL 数据库
1、MxShop/__init__.py
:
import pymysql
pymysql.install_as_MySQLdb()
2、MxShop/settings.py
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mxshop',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '192.168.131.131',
'PORT': '3306',
'OPTIONS': {
#这里引擎用innodb(默认myisam)
#因为后面第三方登录时,要求引擎为INNODB
'init_command': 'SET default_storage_engine=INNODB;'
}
}
}
踩坑:运行项目时出现:
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.
解决办法,在 MxShop/__init__.py
中添加:
import pymysql
# 新增
pymysql.version_info = (1, 3, 13, "final", 0)
pymysql.install_as_MySQLdb()
1.4 安装 xadmin 和 djangoUEditor
1.4.1 富文本编辑器Ueditor
https://github.com/twz915/DjangoUeditor3/
下载源码,解压后将其拷贝至项目目录下,
# * 方法一:将github整个源码包下载下来,在命令行运行:
python setup.py install
# * 方法二:使用pip工具在命令行运行(推荐)(有可能会出错):
pip install DjangoUeditor
将 app 添加到 settings
中:
INSTALLED_APPS = [
'DjangoUeditor3',
]
配置路由 Projects/urls.py
path('ueditor/',include('DjangoUeditor.urls' )),
使用
在 models.py
中:
from DjangoUeditor.models import UEditorField
class Goods(models.Model):
"""商品"""
goods_desc = UEditorField(verbose_name=u"内容", imagePath="goods/images/", width=1000, height=300,
1.4.2 xadmin
源码安装
源码安装的好处在于可以在本地修改源码(自定义定制)。
1、下载源码包:
- 官方源码有一些小Bug 建议下载 mtianyan 修改后的
xadmin
版本:https://github.com/mtianyan/xadmin_django2.0.1
: - 官方:
https://github.com/sshwsfc/xadmin/tree/django2
2、将其拷贝到项目相应目录中:
比如下图中的 extra_apps
中,并将 extra_apps
标记为 Sources Root
3、安装依赖包:
pip install django-crispy-forms
django-formtools
future
six
httplib2
django-import-export
4、注册 xadmin
:
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'xadmin',
'crispy_forms',
'django_filters',
]
5、配置路由 Projects/urls.py
:
from django.urls import path, include
import xadmin
urlpatterns = [
# path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('ueditor/', include('DjangoUeditor.urls')),
]
6、迁移数据库:
# 执行命令
python manage.py makemigrations
python manage.py migrate
# 创建超级用户
python manage.py createsuperuser
python manage.py runserver
参考文章
https://www.cnblogs.com/derek1184405959/p/8592800.html
https://blog.csdn.net/soulwyb/article/details/86036176
1.5 Media 配置
1、settings.py
:
# 设置上传文件路径
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
2、在 media/
中分别创建目录:banner、brands、goods、message
,用于保存上传文件。
3、配置路由 Projects/urls.py
:
from django.urls import path, include
from django.views.static import serve
import xadmin
from MxShop.settings import MEDIA_ROOT
urlpatterns = [
# path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('ueditor/', include('DjangoUeditor.urls')),
path('media/<path:path>', serve, {'document_root': MEDIA_ROOT}),
]
2. 模型设计
创建三个 app
:
goods
:商品trade
:贸易user_operation
:用户操作
python manage.py startapp goods
2.1 用户模型
users/models.py
:
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
"""用户个人信息表"""
GENDER_CHOICES = (
('male', u'男'),
('female', u'女')
)
# 用户用手机注册、姓名、生日和邮箱可为空
name = models.CharField("姓名", max_length=30, null=True, blank=True)
birthday = models.DateField("出生年月", null=True, blank=True)
gender = models.CharField("性别", max_length=6, choices=GENDER_CHOICES, default="female")
mobile = models.CharField("电话", max_length=11)
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 VerifyCord(models.Model):
"""短信验证码表"""
code = models.CharField("验证码", max_length=10)
mobile = models.CharField("电话", max_length=11)
add_time = models.DateTimeField("添加时间", auto_now_add=True)
class Meta:
verbose_name = '短信验证'
verbose_name_plural = verbose_name
def __str__(self):
return self.code
2.2 商品模型
goods/models.py
:
from django.db import models
from DjangoUeditor.models import UEditorField
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='类目级别')
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('添加时间', auto_now_add=True)
class Meta:
verbose_name = "商品类别"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class GoodsCategoryBrand(models.Model):
"""
某一大类下的宣传商标
"""
category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, related_name='brands', null=True, blank=True,
verbose_name="商品类目")
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("添加时间", auto_now_add=True)
class Meta:
verbose_name = "宣传品牌"
verbose_name_plural = verbose_name
db_table = "goods_goodsbrand"
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)
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('添加时间', auto_now_add=True)
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='', verbose_name='图片', null=True, blank=True)
add_time = models.DateTimeField('添加时间', auto_now_add=True)
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):
"""首页商品轮播图"""
image = models.ImageField(upload_to='banner', verbose_name='轮播图片')
index = models.IntegerField('轮播顺序', default=0)
add_time = models.DateTimeField('添加时间', auto_now_add=True)
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
class IndexAd(models.Model):
"""
商品广告
"""
category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, related_name='category', verbose_name="商品类目")
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
class HotSearchWords(models.Model):
"""搜索栏下方热搜词"""
keywords = models.CharField('热搜词', default='', max_length=20)
index = models.IntegerField('排序', default=0)
add_time = models.DateTimeField('添加时间', auto_now_add=True)
class Meta:
verbose_name = '热搜排行'
verbose_name_plural = verbose_name
def __str__(self):
return self.keywords
2.3 交易模型
trade/models.py
:
from django.db import models
from django.contrib.auth import get_user_model
# get_user_model方法会去setting中找AUTH_USER_MODEL
from goods.models import Goods
User = get_user_model()
class ShoppingCart(models.Model):
"""购物车"""
nums = models.IntegerField('购买数量', default=0)
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品')
add_time = models.DateTimeField(verbose_name="添加时间", auto_now_add=True)
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", "微信"),
)
# 订单号唯一
order_sn = models.CharField("订单编号", max_length=30, null=True, blank=True, unique=True)
# 微信支付会用到
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_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)
pay_time = models.DateTimeField("支付时间", null=True, blank=True)
# 用户信息
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("添加时间", auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户")
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("添加时间", auto_now_add=True)
class Meta:
verbose_name = "订单商品"
verbose_name_plural = verbose_name
def __str__(self):
return str(self.order.order_sn)
2.4 用户操作模型
user_operation/models.py
:
from django.db import models
from goods.models import Goods
from django.contrib.auth import get_user_model
User = get_user_model()
class UserFav(models.Model):
"""
用户收藏操作
"""
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户")
goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name="商品", help_text="商品id")
add_time = models.DateTimeField("添加时间", auto_now_add=True)
class Meta:
verbose_name = '用户收藏'
verbose_name_plural = verbose_name
unique_together = ("user", "goods")
def __str__(self):
return self.user.name
class UserAddress(models.Model):
"""
用户收货地址
"""
user = models.ForeignKey(User, 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("添加时间", auto_now_add=True)
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(User, 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="留言内容")
file = models.FileField(upload_to="message/images/", verbose_name="上传的文件", help_text="上传的文件")
add_time = models.DateTimeField("添加时间", auto_now_add=True)
class Meta:
verbose_name = "用户留言"
verbose_name_plural = verbose_name
def __str__(self):
return self.subject