Skip to content

Latest commit

 

History

History
660 lines (561 loc) · 20.4 KB

File metadata and controls

660 lines (561 loc) · 20.4 KB

3、本项目数据库设计

主要的数据有:

数据库设计:
1)用户模块
  用户表(df_user)
  地址表(df_address)

2)商品模块
  商品种类表(df_goods_type)
  商品SPU表(df_goods)
  商品SKU表(df_goods_sku)
  商品图片表(df_goods_image)
  首页轮播商品表(df_index_banner)
  首页促销活动表(df_index_promotion)
  首页分类商品展示表(df_index_type_goods)

3) 购物车模块
  redis实现

4)订单模块
  订单信息表(df_order_info)
  订单商品表(df_order_goods)
  
 数据库设计注意点:
1)对于一对多的关系,应该设计两张表,并且多表中存放外键。
2)数据库设计时,有些时候需要以空间换取时间。

由于所有的数据表都需要创建时间、更新时间、删除标记(mysql软删除)这三个字段,因此我们建立一个抽象模型类作为所有模型类的父类来直接生成这三个字段,简化代码,进行代码复用。

# 在项目根目录下建立名为db的python包,然后在其下建立base_model.py文件
from django.db import models


class BaseModel(models.Model):
    """抽象模型基类"""
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
    is_delete = models.BooleanField(default=False, verbose_name='删除标记')

    class Meta:
        # 指定这个类是一个抽象模型类
        abstract = True

各数据表详细设计信息如下:

from django.contrib.auth.models import AbstractUser
from db.base_model import BaseModel

# 使用django默认的认证系统
# python manage.py createsuperuser->auth_user->User模型类
class User(AbstractUser, BaseModel):
    '''用户模型类'''

    class Meta:
        db_table = 'df_user'
        verbose_name = '用户'
        verbose_name_plural = verbose_name
# AbstractUser模型类源码:可以看到,包含用户名、email、is_staff(是否为管理员,在admin里注册的用户此值为1,其他用户为0)、is_active(是否激活)等字段
class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username, password and email are required. Other fields are optional.
    """
    username = models.CharField(_('username'), max_length=30, unique=True,
        help_text=_('Required. 30 characters or fewer. Letters, digits and '
                    '@/./+/-/_ only.'),
        validators=[
            validators.RegexValidator(r'^[\w.@+-]+$',
                                      _('Enter a valid username. '
                                        'This value may contain only letters, numbers '
                                        'and @/./+/-/_ characters.'), 'invalid'),
        ],
        error_messages={
            'unique': _("A user with that username already exists."),
        })
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(_('staff status'), default=False,
        help_text=_('Designates whether the user can log into this admin '
                    'site.'))
    is_active = models.BooleanField(_('active'), default=True,
        help_text=_('Designates whether this user should be treated as '
                    'active. Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

数据库结果示例:

mysql> select * from df_user \G;

*************************** 1. row ***************************
          id: 14
    password: pbkdf2_sha256$20000$Jz4mt9SQzRN$/QENHGgYPQSU+LMIlWesFjSwI4i2ci+4JT3ZWik=
  last_login: 2018-03-16 12:26:32.773692
is_superuser: 0
    username: xxxx
  first_name: 
   last_name: 
       email: [email protected]
    is_staff: 0
   is_active: 1
 date_joined: 2018-03-10 02:38:43.682848
 create_time: 2018-03-10 02:38:43.707250
 update_time: 2018-03-10 02:39:23.053253
   is_delete: 0
  • 收货地址表:
    • 记录所有网站用户的收货地址信息,与用户信息表属于多对1的关系
    • 一个用户可以拥有多个收货地址,因此需要存在外键关联到用户
    • **表字段:**ID、用户ID(外键)、收件人、地址、邮编、手机、是否默认地址。
    • 自定义模型管理器——定义获取默认地址的方法、定义获取所有地址的方法。

自定义地址模型管理器类:

class AddressManager(models.Manager):
    """地址模型管理器类"""

    # 封装方法: 改变原有查询的结果集
    # 封装方法: 用于操作模型管理器对象所在模型类对应的数据表(增、删、改、查)
    def get_default_address(self, user):
        """获取user用户的默认收货地址"""
        try:
            # address = Address.objects.get(user=user, is_default=True)
            address = self.get(user=user, is_default=True)
        # except Address.DoesNotExist:
        except self.model.DoesNotExist:
            address = None

        # 返回address
        return address

    def get_all_address(self, user):
        """获取所有地址"""
        try:
            have_address = self.filter(user=user)
        except self.model.DoesNotExist:
            have_address = None
        return have_address

地址模型类:

class Address(BaseModel):
    '''地址模型类'''
    user = models.ForeignKey('User', verbose_name='所属账户')
    receiver = models.CharField(max_length=20, verbose_name='收件人')
    addr = models.CharField(max_length=256, verbose_name='收件地址')
    zip_code = models.CharField(max_length=6, null=True, verbose_name='邮政编码')
    phone = models.CharField(max_length=11, verbose_name='联系电话')
    is_default = models.BooleanField(default=False, verbose_name='是否默认')

    # 自定义模型管理器类的对象
    objects = AddressManager()

    class Meta:
        db_table = 'df_address'
        verbose_name = '地址'
        verbose_name_plural = verbose_name
数据库结果示例:
mysql> select * from df_address \G;

*************************** 1. row ***************************
         id: 1
create_time: 2018-03-10 09:29:23.868868
update_time: 2018-03-10 11:57:24.365431
  is_delete: 0
   receiver: xxx
       addr: xxx
   zip_code: 123456
      phone: 13333333
 is_default: 0
    user_id: 14
  • 商品详情表(SKU表)
    • 记录网站所以商品详情
    • **表字段:**ID、名称、简介、价格、单位、库存、销量、状态(上下架)、默认图片、商品种类ID(外键)、商品小分类ID(SPU、外键)
    • 商品种类→SPU(小分类)→SKU(商品详情)
class GoodsSKU(BaseModel):
    '''商品SKU模型类'''
    status_choices = (
        (0, '下架'),
        (1, '上架'),
    )

    type = models.ForeignKey('GoodsType', verbose_name='商品种类')
    goods = models.ForeignKey('Goods', verbose_name='商品SPU')
    name = models.CharField(max_length=20, verbose_name='商品名称')
    desc = models.CharField(max_length=256, verbose_name='商品简介')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品价格')
    unite = models.CharField(max_length=20, verbose_name='商品单位')
    image = models.ImageField(upload_to='goods', verbose_name='商品图片')
    stock = models.IntegerField(default=1, verbose_name='商品库存')
    sales = models.IntegerField(default=0, verbose_name='商品销量')
    status = models.SmallIntegerField(default=1, choices=status_choices, verbose_name='商品状态')

    class Meta:
        db_table = 'df_goods_sku'
        verbose_name = '商品'
        verbose_name_plural = verbose_name

数据库结果示例:

mysql> select * from df_goods_sku \G;

*************************** 1. row ***************************
         id: 1
create_time: 2017-11-15 03:10:14.045538
update_time: 2018-03-16 14:06:19.211536
  is_delete: 0
       name: 草莓 500g
       desc: 草莓简介
      price: 10.00
      unite: 500g
      image: group1/M00/00/00/rBCzg1oKqFGAR2tjAAAljHPuXJg4272079
      stock: 93
      sales: 5
     status: 1
   goods_id: 1
category_id: 1
  • 商品种类表
    • 记录网站提供的所有商品种类
    • 表字段:ID、种类名称、种类标记logo、种类图片
class GoodsType(BaseModel):
    '''商品类型模型类'''
    name = models.CharField(max_length=20, verbose_name='种类名称')
    logo = models.CharField(max_length=20, verbose_name='标识')
    image = models.ImageField(upload_to='type', verbose_name='商品类型图片')

    class Meta:
        db_table = 'df_goods_type'
        verbose_name = '商品种类'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

数据库结果示例:

mysql> select * from df_goods_type \G;

*************************** 1. row ***************************
         id: 1
create_time: 2017-11-14 05:02:09.888544
update_time: 2017-11-14 05:02:09.888598
  is_delete: 0
       name: 新鲜水果
       logo: fruit
      image: group1/M00/00/00/rBCzg1oKeNKAEl87AAAmv27pX4k4942898
  • 商品SPU表
    • 记录网站所以商品的小类类别信息
    • 表字段:ID、名称、详情
from tinymce.models import HTMLField

class Goods(BaseModel):
    '''商品SPU模型类'''
    name = models.CharField(max_length=20, verbose_name='商品SPU名称')
    # 富文本类型:带有格式的文本
    detail = HTMLField(blank=True, verbose_name='商品详情')

    class Meta:
        db_table = 'df_goods'
        verbose_name = '商品SPU'
        verbose_name_plural = verbose_name

富文本字段编辑器插件——tinymce

1安装包
  pip install django-tinymce==2.6.0
  
2注册应用并配置
  INSTALLED_APPS = (
    # ...
    'tinymce', # 富文本编辑器
    # ...
  )
	# 富文本编辑器大小尺寸和主题
  TINYMCE_DEFAULT_CONFIG = {
    'theme': 'advanced',
    'width': 600,
    'height': 400,
  }

3项目的urls中添加url配置项urlpatterns = [
    # ...
    url(r'^tinymce/$', include('tinymce.urls')), # 富文本编辑器
    # ...
  ]
  
使用富文本类型:
1导入类型
  from tinymce.models import HTMLField

2创建模型类
  class Goods(models.Model):
    """商品模型类"""
    detail = HTMLField(verbose_name='商品详情')

数据库结果示例:

mysql> select * from df_goods \G;

*************************** 1. row ***************************
         id: 1
create_time: 2017-11-15 03:03:05.257969
update_time: 2017-11-15 03:03:05.258130
  is_delete: 0
       name: 草莓
     detail: <p><strong>很不错的草莓</strong></p>
  • 商品图片表
    • 记录所有商品的图片url
    • 表字段:ID、SKU ID(外键)、图片URL
class GoodsImage(BaseModel):
    '''商品图片模型类'''
    sku = models.ForeignKey('GoodsSKU', verbose_name='商品', on_delete=models.CASCADE)
    image = models.ImageField(upload_to='goods', verbose_name='图片路径')

    class Meta:
        db_table = 'df_goods_image'
        verbose_name = '商品图片'
        verbose_name_plural = verbose_name
  • 首页分类商品展示表
    • 首页各类商品要显示的商品
    • 表字段:ID、商品种类ID(外键)、SKU ID(外键)、展示类型(标题或图片)、展示顺序
class IndexTypeGoodsBanner(BaseModel):
    '''首页分类商品展示模型类'''
    DISPLAY_TYPE_CHOICES = (
        (0, "标题"),
        (1, "图片")
    )

    category = models.ForeignKey('GoodsType', verbose_name='商品类型', on_delete=models.CASCADE)
    sku = models.ForeignKey('GoodsSKU', verbose_name='商品SKU', on_delete=models.CASCADE)
    display_type = models.SmallIntegerField(default=1, choices=DISPLAY_TYPE_CHOICES, verbose_name='展示类型')
    index = models.SmallIntegerField(default=0, verbose_name='展示顺序')

    class Meta:
        db_table = 'df_index_type_goods'
        verbose_name = "主页分类展示商品"
        verbose_name_plural = verbose_name

数据库结果示例:

mysql> select * from df_index_type_goods \G;

*************************** 1. row ***************************
          id: 1
 create_time: 2017-11-14 08:57:41.509910
 update_time: 2017-11-14 08:57:41.509945
   is_delete: 0
display_type: 1
       index: 0
      sku_id: 1
 category_id: 1
  • 首页轮播商品表
    • 记录首页轮播区的商品信息
    • 表字段:ID、 SKU ID(外键)、图片URL、展示顺序
class IndexGoodsBanner(BaseModel):
    '''首页轮播商品展示模型类'''
    sku = models.ForeignKey('GoodsSKU', verbose_name='商品', on_delete=models.CASCADE)
    image = models.ImageField(upload_to='banner', verbose_name='图片')
    index = models.SmallIntegerField(default=0, verbose_name='展示顺序')  # 0 1 2 3

    class Meta:
        db_table = 'df_index_banner'
        verbose_name = '首页轮播商品'
        verbose_name_plural = verbose_name

数据库结果示例:

mysql> select * from df_index_banner \G;

*************************** 1. row ***************************
         id: 1
create_time: 2017-11-14 08:48:05.549864
update_time: 2017-11-14 08:48:05.549896
  is_delete: 0
      image: group1/M00/00/00/rBCzg1oKrcWAX7y-AACpB-LsCdE6038911
      index: 0
     sku_id: 5
  • 首页促销活动表
    • 记录首页要展示的促销活动信息
    • 表字段:ID、活动图片、活动页面链接URL、展示顺序
class IndexPromotionBanner(BaseModel):
    '''首页促销活动模型类'''
    name = models.CharField(max_length=20, verbose_name='活动名称')
    url = models.CharField(max_length=256, verbose_name='活动链接')
    image = models.ImageField(upload_to='banner', verbose_name='活动图片')
    index = models.SmallIntegerField(default=0, verbose_name='展示顺序')

    class Meta:
        db_table = 'df_index_promotion'
        verbose_name = "主页促销活动"
        verbose_name_plural = verbose_name

数据库结果示例:

mysql> select * from df_index_promotion \G;

*************************** 1. row ***************************
         id: 1
create_time: 2017-11-14 08:56:21.863522
update_time: 2017-11-17 08:29:08.554743
  is_delete: 0
       name: 吃货暑假趴
        url: #
      image: group1/M00/00/00/rBCzg1oKr7aAdR-2AAA2pLUeB609027808
      index: 0
  • 订单信息表
    • 记录所有用户订单信息
    • 表字段:订单ID、用户ID(外键)、地址ID(外键)、支付方式、商品总数、订单总额、订单运费、订单状态、支付编号
class OrderInfo(BaseModel):
    '''订单模型类'''
    PAY_METHODS = {
        '1': "货到付款",
        '2': "微信支付",
        '3': "支付宝",
        '4': '银联支付'
    }

    PAY_METHOD_CHOICES = (
        (1, '货到付款'),
        (2, '微信支付'),
        (3, '支付宝'),
        (4, '银联支付')
    )

    ORDER_STATUS = {
        1: '待支付',
        2: '待发货',
        3: '待收货',
        4: '待评价',
        5: '已完成'
    }

    ORDER_STATUS_CHOICES = (
        (1, '待支付'),
        (2, '待发货'),
        (3, '待收货'),
        (4, '待评价'),
        (5, '已完成')
    )

    order_id = models.CharField(max_length=128, primary_key=True, verbose_name='订单id')
    user = models.ForeignKey('user.User', verbose_name='用户', on_delete=models.CASCADE)
    addr = models.ForeignKey('user.Address', verbose_name='地址', on_delete=models.CASCADE)
    pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=3, verbose_name='支付方式')
    total_count = models.IntegerField(default=1, verbose_name='商品数量')
    total_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品总价')
    transit_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='订单运费')
    order_status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name='订单状态')
    trade_no = models.CharField(max_length=128, default='', verbose_name='支付编号')

    class Meta:
        db_table = 'df_order_info'
        verbose_name = '订单'
        verbose_name_plural = verbose_name

数据库结果示例:

mysql> select * from df_order_info \G;

*************************** 1. row ***************************
  create_time: 2018-03-16 13:09:12.946254
  update_time: 2018-03-16 13:09:13.782370
    is_delete: 0
     order_id: 2018031621091214
   pay_method: 1
  total_count: 6
  total_price: 92.02
transit_price: 10.00
 order_status: 1
     trade_no: 
      addr_id: 3
      user_id: 14
  • 订单商品表
    • 各订单里包含的商品信息
    • 表字段:ID、订单ID(外键)、SKU ID(外键)、商品数目、商品价格(下单时的价格)、评论
class OrderGoods(BaseModel):
    '''订单商品模型类'''
    order = models.ForeignKey('OrderInfo', verbose_name='订单', on_delete=models.CASCADE)
    sku = models.ForeignKey('goods.GoodsSKU', verbose_name='商品SKU', on_delete=models.CASCADE)
    count = models.IntegerField(default=1, verbose_name='商品数目')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品价格')
    comment = models.CharField(max_length=256, default='', verbose_name='评论')

    class Meta:
        db_table = 'df_order_goods'
        verbose_name = '订单商品'
        verbose_name_plural = verbose_name

数据库结果示例:

mysql> select * from df_order_goods \G;
*************************** 1. row ***************************
         id: 1
create_time: 2018-03-16 13:09:12.959678
update_time: 2018-03-16 13:09:12.959718
  is_delete: 0
      count: 3
      price: 10.00
    comment: 
   order_id: 2018031621091214
     sku_id: 1

设计完成后,我们需要配置django项目的settings文件中的mysql接口参数:

import pymysql

pymysql.install_as_MySQLdb()
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        #  配置mysql
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'ttsx', # 此处的数据库名称需要先在mysql中创建,且注意数据库字符集应设置为utf-8
        'HOST': 'localhost', # 数据库所在服务器地址
        'PORT': '3306', # 数据库通信端口
        'USER': 'username',  # 你的账户名
        'PASSWORD': 'password', # 你的密码
    }
}

除此之外,由于我们使用继承了django内置的认证User类,并自定义了一个子类。因此需要在配置文件settings中指定应该采用的认证User类:

# 指定django认证系统使用的用户模型类
AUTH_USER_MODEL = 'user.User'

如下为本项目最终会用到的一些数据表

+---------------------------+
| Tables_in_ttsx            |
+---------------------------+
| auth_group                |
| auth_group_permissions    |
| auth_permission           |
| celery_taskmeta           |
| celery_tasksetmeta        |
| df_address                |
| df_goods                  |
| df_goods_image            |
| df_goods_sku              |
| df_goods_type             |
| df_index_banner           |
| df_index_promotion        |
| df_index_type_goods       |
| df_order_goods            |
| df_order_info             |
| df_user                   |
| df_user_groups            |
| df_user_user_permissions  |
| django_admin_log          |
| django_content_type       |
| django_migrations         |
| django_session            |
| djcelery_crontabschedule  |
| djcelery_intervalschedule |
| djcelery_periodictask     |
| djcelery_periodictasks    |
| djcelery_taskstate        |
| djcelery_workerstate      |
+---------------------------+