【Vue+DRF 生鲜电商】环境搭建和模型设计(一)

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 配置项目结构

项目根目录分部创建:

  • appsPython 包文件
  • extra_appsPython 包文件
  • media:文件夹
  • db_tools:文件夹

并将 apps、extra_apps 标记为 sources rootpycharm 中鼠标右键选择相应文件夹,选择 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
原文地址:https://www.cnblogs.com/midworld/p/13629660.html