Py学习  »  Django

Django+Vue开发生鲜电商平台之3.数据模型设计和资源导入

cutercorley • 3 年前 • 416 次点击  

永远不要跟别人比幸运,我从来没想过我比别人幸运,我也许比他们更有毅力,在最困难的时候,他们熬不住了,我可以多熬一秒钟、两秒钟。
——马云

Github和Gitee代码 同步更新
https://github.com/corleytd/Django_Fresh_Ecommerce
https://gitee.com/Python_Full_Stack/Django_Fresh_Ecommerce

在正式开发项目之前,要确定数据库和表结构。

一、项目初始化

在虚拟环境安装好之后,需要安装 Django Django REST framework ,直接在虚拟环境下使用 pip install django pip install djangorestframework 。为了支持Django REST framework,还需要安装markdown和django-filter,命令类似。

在PyCharm新建项目,需要选择Django,而不是Pure Python,建立好之后,需要选择解释器为建立的虚拟环境所对应的解释器,选择模板语言为Django,设置应用名为users,并 取消勾选 Enable Django admin。

项目创建后,通过点击PyCharm导航栏Run下的 Run 'Project Name' 或者命令行执行 python manage.py runserver 即可运行项目,再访问 http://127.0.0.1:8000/ 如果看到以下页面即说明项目初始化成功:
项目创建 运行成功

创建项目后,主应用目录下会有一个settings.py文件,是关于项目的配置,其中有关于数据库的配置,如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

显然,指明了数据库为sqlite,这也是Django的默认数据库,需要 修改为MySQL ,如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'fresh_ec',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST':'127.0.0.1',
        'OPTIONS': {'init_command': 'SET default_storage_engine=INNODB;'}
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

因为后面第三方登录时进行数据迁移的需要,需要添加OPTIONS参数,设置存储引擎为INNODB,否则会报错。

此时再运行项目就会报错:

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
  • 1
  • 2

提示安装MySQL驱动,直接通过 pip install mysqlclient 安装即可。

在安装过程中可能会报错 ERROR: Command errored out with exit status 1 ,需要在 https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient 中下载对应系统和Python版本的安装文件并将其拷贝到虚拟环境目录下通过 pip install 文件名.whl 进行安装。

另外,因为项目中需要进行图片处理,因此需要安装图片处理库pillow,通过 pip install pillow 安装即可。

还需要进行项目目录的优化调整:
在项目主目录下New一个Python Package为 apps ,保存项目中所有的app,并将自动生成的users移动到apps目录下;
再在主目录下创建 extra_apps 包,保存源码经修改的第三方包;
创建目录 media ,用于保存文件和图片等;
创建目录 db_tools ,用于保存数据库初始化的脚本等。

为了以后开发更加方便,可以将apps和extra_apps 右键 Mark Directory as Sources Root ,完成后文件夹颜色会发生改变。

还需要将apps和extra_apps加入到settings.py的根目录参数中,如下:

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)


    

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
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'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

二、数据模型设计

在进行需求分析之后,即可设计数据模型,即数据表。

在创建数据模型之前,需要创建所需的app,项目中需要的app包括商品goods、交易trade、用户users、操作user_operation(是为了防止相互引用而建立的)。

创建app可以直接点击导航栏 Tools Run manage.py Task ,在命令行中分别输入 startapp goods startapp trade startapp user_operation 创建对应的app,这比手动创建文件效率高很多。创建好之后,将3个目录移动到apps目录之下即可。

此时即可在每个app下分别创建数据模型。

为了保存数据,在进行删除操作时不真正删除数据,所有模型都增加一个属性 is_delete ,为布尔型值,为False时表示未删除,为True时表示被删除,以此来保证数据的安全。

1.用户数据模型设计

apps/users/models下创建数据模型如下:

from datetime import datetime

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


# Create your models here.

class UserProfile(AbstractUser):
    '''用户'''
    name = models.CharField(max_length=30, null=True, blank=True, verbose_name='姓名')
    birthday = models.DateField(null=True, blank=True, verbose_name='出生日期')
    gender = models.CharField(max_length=6, choices=(('male', u'男'), ('female', u'女')), default='female',
                              verbose_name='性别')
    mobile = models.CharField(max_length=11, verbose_name='电话')
    email = models.CharField(max_length=50, null=True, blank=True, verbose_name='邮箱')

    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '用户'
        verbose_name_plural = '用户'

    def __str__(self):
        return self.name


class VerifyCode(models.Model):
    code = models.CharField(max_length=10, verbose_name='验证码')
    mobile = models.CharField(max_length=11, verbose_name='电话')

    add_time = models.DateField(default=datetime


    
.now, verbose_name='添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '短信验证码'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

此时定义的用户还未替换系统自定义的用户,还需要在settings.py中进行设置:

ALLOWED_HOSTS = []

AUTH_USER_MODEL = 'users.UserProfile'
  • 1
  • 2
  • 3

2.商品数据模型设计

商品包括商品分类(分为 3个层次 ,具有从属关系)、品牌名、商品、商品轮播图、轮播商品等实体,goods/models.py建立模型如下:

from datetime import datetime

from django.db import models
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, verbose_name='类别名', help_text='类别名')
    code = models.CharField(default='', max_length=30, verbose_name='类别code', help_text='类别code')
    desc = models.TextField(default='', verbose_name='类别描述', help_text='类别描述')
    category_type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name='类目级别', help_text='类目级别')
    parent_category = models.ForeignKey('self', null=True, blank=True, verbose_name='父类别', related_name='sub_cat', on_delete=models.SET_NULL)
    is_tab = models.BooleanField(default=False, verbose_name='是否导航', help_text='是否导航')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class


    
 Meta:
        verbose_name = '商品类别'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsCategoryBrand(models.Model):
    '''品牌名'''
    name = models.CharField(default='', max_length=30, verbose_name='品牌名', help_text='品牌名')
    desc = models.TextField(default='', max_length=200, verbose_name='品牌描述', help_text='品牌描述')
    image = models.ImageField(max_length=200, upload_to='brand/images/')
    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '品牌'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Goods(models.Model):
    '''商品'''
    category = models.ForeignKey(GoodsCategory, verbose_name='商品类目', null=True, on_delete=models.SET_NULL)
    goods_sn = models.CharField(max_length=50, default='', verbose_name='商品唯一货号')
    name = models.CharField(max_length=300, verbose_name='商品名')
    click_num = models.IntegerField(default=0, verbose_name='点击量')
    sold_num = models.IntegerField(default=0, verbose_name='销售量')
    fav_num = models.IntegerField(default=0, verbose_name='收藏量')
    goods_num = models.IntegerField(default=0, verbose_name='库存量')
    market_price = models.FloatField(default=0, verbose_name='市场价格')
    shop_price = models.FloatField(default=0, verbose_name='本店价格')
    goods_brief = models.TextField(max_length=500, verbose_name='商品简介')
    goods_desc = UEditorField(verbose_name=u'内容', imagePath='goods/images/', width=1000, height=300, filePath='goods/files/', default='')
    ship_free = models.BooleanField(default=True, verbose_name='是否承担运费')
    goods_front_image 


    
= models.ImageField(upload_to='goods/images/', null=True, blank=True, verbose_name='封面图')
    is_new = models.BooleanField(default=False, verbose_name='是否新品')
    is_home = models.BooleanField(default=False, verbose_name='是否热销')

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')


class GoodsImage(models.Model):
    '''商品轮播图'''
    goods = models.ForeignKey(Goods, verbose_name='', related_name='商品', null=True, on_delete=models.SET_NULL)
    image = models.ImageField(upload_to='', null=True, blank=True, verbose_name='图片')
    image_url = models.CharField(max_length=300, null=True, blank=True, verbose_name='图片链接')

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '商品轮播图'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.goods.name


class Banner(models.Model):
    '''轮播商品'''
    goods = models.ForeignKey(Goods, verbose_name='商品', null=True, on_delete=models.SET_NULL)
    image = models.ImageField(upload_to='banner', verbose_name='轮播图片')
    index = models.IntegerField(default=0, verbose_name='轮播顺序')

    add_time = models.DateField(default=datetime.now, verbose_name='添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '轮播商品'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.goods.name
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100

在建立商品模型时,需要用到百度的富文本编辑器UEditor,但是由于官方的 Django UEditor 停止维护、不再支持Python3,因此需要自行下载,可点击 https://download.csdn.net/download/CUFEECR/12641702 https://github.com/twz915/DjangoUeditor3/ 下载并解压后,将DjangoEeditor目录放入项目的 extra_apps 目录下,并在settings.py中添加app如下:

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.users.apps.UsersConfig',
    'goods',
    'trade',
    'user_operation',
    'DjangoUeditor'
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

当然,也可以如下:

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.users.apps.UsersConfig',
    'goods.apps.GoodsConfig',
    'trade.apps.TradeConfig',
    'user_operation.apps.UserOperationConfig',
    'DjangoUeditor'
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

效果是一样的,这里采用后者。

3.交易数据模型设计

交易涉及到购物车、订单等实体。
购物车中如果商品多次加入购物车,只有1条数据,其数量增加;
点击结算之后,购物车数量减少、结算数量增加。

trade/models.py建立购物车和订单模型如下:

from datetime import datetime

from django.db import models
from django.contrib.auth import get_user_model  # from user.models import UserProfile

from goods.models import Goods

User = get_user_model()


# Create your models here.

class ShoppingCart(models.Model):
    '''购物车'''
    user =


    
 models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL)
    goods = models.ForeignKey(Goods, verbose_name='商品', null=True, on_delete=models.SET_NULL)
    goods_num = models.IntegerField(default=0, verbose_name='商品数量')

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '购物车'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s(%d)'.format(self.goods.name, self.goods_num)


class OrderInfo(models.Model):
    '''订单信息'''
    ORDER_STATUS = (
        ('success', '成功'),
        ('cancel', '取消'),
        ('paying', '待支付'),
    )
    user = models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL)
    order_sn = models.CharField(max_length=30, unique=True, verbose_name='订单号')
    trade_no = models.CharField(max_length=50, unique=True, null=True, blank=True, verbose_name='交易号')
    pay_status = models.CharField(max_length=100, choices=ORDER_STATUS, verbose_name='订单状态')
    pay_script = models.CharField(max_length=11, verbose_name='订单留言')
    order_mount = models.FloatField(default=0.0, verbose_name='订单金额')
    pay_time = models.DateTimeField(null=True, blank=True, verbose_name='支付时间')

    # 用户基本信息
    address = models.CharField(max_length=100, default='', verbose_name='收货地址')
    signer_name = models.CharField(max_length=20, default='', verbose_name='签收人')
    signer_mobile = models.CharField(max_length=11, verbose_name='联系电话')

    add_time = models.DateField(default=datetime.now,


    
 verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')


class OrderGoods(models.Model):
    '''订单商品详情'''
    order = models.ForeignKey(OrderInfo, verbose_name='订单信息', null=True, on_delete=models.SET_NULL)
    goods = models.ForeignKey(Goods, verbose_name='商品', null=True, on_delete=models.SET_NULL)
    goods_num = models.IntegerField(default=0, verbose_name='商品数量')

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

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

    def __str__(self):
        return self.order.order_sn
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

在定义购物车模型、需要导入用户模型时,因为假如是第三方项目,不知道其定义的具体用户模型和存放目录,所以 from user.models import UserProfile 具有一定的 局限性 。因为之前已经将UserProfile注册到系统用户,所以可以直接用Django提供的函数 get_user_model() 来获取用户,这样显得更加灵活。

4.用户操作数据模型设计

最典型的操作是收藏、添加收货地址和留言。

user_operation/models.py建立模型如下:

from datetime import datetime

from django.db import models
from django.contrib.auth import get_user_model

from goods.models import Goods

User = get_user_model()


# Create your models here.

class UserFav(models.Model):
    '''用户收藏'''
    user = models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL)
    goods = models.ForeignKey(Goods, verbose_name='商品', null=True, on_delete=models.


    
SET_NULL)

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '用户收藏'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.user.name


class UserLeavingMessage(models.Model):
    '''用户留言'''
    MESSAGE_CHOICES = (
        (1, '留言'),
        (2, '投诉'),
        (3, '询问'),
        (4, '售后'),
        (5, '求购'),
    )
    user = models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL)
    message_type = models.IntegerField(default=1, choices=MESSAGE_CHOICES, verbose_name='留言类型',
                                help_text='留言类型: 1(留言), 2(投诉), 3(询问),4(售后), 5(求购)')
    subject = models.CharField(max_length=80, default='', verbose_name='主题')
    message = models.TextField(default='', verbose_name='留言内容', help_text='留言内容')
    file = models.FileField(verbose_name='上传的文件', help_text='上传的文件')

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '用户留言'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.subject


class UserAddress(models.Model):
    '''用户收货地址'''
    user = models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL)
    district = models.CharField(max_length=50, default='', verbose_name='区域')
    address = models.CharField(max_length=100, default='', verbose_name='详细地址')
    signer_name = models.CharField(max_length=20, default='', verbose_name='签收人')
    signer_mobile = models.


    
CharField(max_length=11, default='', verbose_name='联系电话')

    add_time = models.DateField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '收货地址'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.address
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

三、xadmin后台管理系统的配置

xadmin是一个Django自带后台管理工具的替代品,可以直接使用 pip install xadmin 命令安装,但是由于通过这种方式安装的官方版本对版本2.1及以后的Django 支持不友好 ,因此可点击 https://download.csdn.net/download/CUFEECR/12647622 下载修改后的版本,并将其解压放入extra_apps下。

然后在4个apps下分别创建adminx.py,apps/goods/adminx.py如下:

import xadmin
from .models import Goods, GoodsCategory, GoodsImage, GoodsCategoryBrand, Banner

class GoodsAdmin(object):
    list_display = ["name", "click_num", "sold_num", "fav_num", "goods_num", "market_price",
                    "shop_price", "goods_brief", "goods_desc", "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"


    
]
        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"]

    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"]


class HotSearchAdmin(object):
    list_display = ["keywords", "index", "add_time"]


class IndexAdAdmin(object):
    list_display = ["category", "goods"]


xadmin.site.register(Goods, GoodsAdmin)
xadmin.site.register(GoodsCategory, GoodsCategoryAdmin)
xadmin.site.register(Banner, BannerGoodsAdmin)
xadmin.site.register(GoodsCategoryBrand, GoodsBrandAdmin)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

apps/trade/adminx.py如下:

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

apps/user_operation/adminx.py如下:

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

apps/users/adminx.py如下:

import xadmin
from xadmin import views
from .models import VerifyCode


class BaseSetting(object):
    enable_themes = True
    use_bootswatch = True


class GlobalSettings(object):
    site_title = "生鲜后台"
    site_footer = "fresh_ec"


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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

将xadmin配置到settings.py中:

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.users.apps.UsersConfig',
    'goods',
    'trade',
    'user_operation',
    'DjangoUeditor',
    'xadmin',
    'crispy_forms',
    'django.contrib.admin'
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

还需要安装xadmin的依赖包,从 https://github.com/sshwsfc/xadmin/blob/master/requirements.txt 可以看到,包括django、django-crispy-forms、django-import-export、django-reversion、django-formtools、future、httplib2和six,可以直接使用一条命令 pip install django django-crispy-forms django-import-export django-reversion django-formtools future httplib2 six 安装即可。

此外,还需要安装xlwt和xlsxwriter,这主要用于操作Excel文件、使功能更完善,直接使用命令 pip install xlwt xlsxwriter 安装即可。
最后还需要对xadmin进行数据映射,即执行 makemigrations migrate 即可。

如果遇到 ImportError: cannot import name 'six' from 'django.utils' ,可按照下面方法解决:
django xadmin six to django utils

如果遇到以下报错:

forms.Field.__init__(self, required, widget, label, initial, help_text,
TypeError: __init__() takes 1 positional argument but 6 were given
  • 1
  • 2

可直接在xadmin\views\dashboard.py中将 forms.Field.__init__(self, required, widget, label, initial, help_text, *args, **kwargs) 改为 forms.Field.__init__(self) 即可。

如果遇到其他问题,可参考 https://blog.csdn.net/CUFEECR/article/details/104031620 进行解决。

进行映射后,再查询数据库:

+------------------------------------+                   
| Tables_in_fresh_ec                 |                   
+------------------------------------+                   
| auth_group                         |                   
| auth_group_permissions             |                   
| auth_permission                    |                   
| django_content_type                |                   
| django_migrations                  |                   
| django_session                     |                   
| goods_banner                       |                   
| goods_goods                        |                   
| goods_goodscategory                |                   
| goods_goodscategorybrand           |                   
| goods_goodsimage                   |                   
| trade_ordergoods                   |                   
| trade_orderinfo                    |                   
| trade_shoppingcart                 |                   
| user_operation_useraddress         |                   
| user_operation_userfav             |                   
| user_operation_userleavingmessage  |                   
| users_userprofile                  |                   
| users_userprofile_groups           |                   
| users_userprofile_user_permissions |                   



    
| users_verifycode                   |                   
| xadmin_bookmark                    |                   
| xadmin_log                         |                   
| xadmin_usersettings                |                   
| xadmin_userwidget                  |                   
+------------------------------------+                   
25 rows in set (0.01 sec)                                
                                                         
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

显然,xadmin的数据表已经映射进数据库。

此时,还需要对xadmin配置访问路径,urls.py如下:

from django.conf.urls import url
import xadmin

urlpatterns = [
       url(r'^xadmin/', xadmin.site.urls),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

此时还需要创建超级用户,在manage.py@Fresh Ecommerce窗口中执行 createsuperuser 命令,输入用户名、邮箱和密码后即可创建超级管理员。

然后访问 http://127.0.0.1:8000/xadmin/ 如下:
django xadmin login

显然,此时可以(用刚刚创建的用户名和密码)登录后台,但是网页语言还是英文,需要对settings.py进行设置如下:

LANGUAGE_CODE = 'zh-hans'  # 中文支持

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

apps/goods/apps.py如下:

from django.apps import AppConfig


class GoodsConfig(AppConfig):
    name = 'goods'
    verbose_name = '商品'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

apps/trade/apps.py如下:

from django.apps import AppConfig


class TradeConfig(AppConfig):
    name = 'trade'
    verbose_name = '交易管理'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

apps/user_operation/apps.py如下:

from django.apps import AppConfig


class UserOperationConfig(AppConfig):
    name = 'user_operation'
    verbose_name = '用户操作管理'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

apps/user/apps.py如下:

from django.apps import AppConfig


class UsersConfig(AppConfig):
    name = 'apps.users'
    verbose_name = '用户管理'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

此时再查看网页如下:
django xadmin detail show
显然,此时已经变为中文,并且点击每个导航栏都能看到具体内容,并且可以进行导出数据等多种操作。

四、数据迁移和数据导入

1.migration原理和表的生成

在导航栏Tools选择点击 Run manage.py Task… ,在命令行中输入 makemigrations 并执行,执行完之后,可以看到每个app下的migrations均生成了0001_initial.py文件。
但是此时数据还并未真正映射到数据库中,还需要执行 migrate 命令来执行之前生成的Python文件来映射数据库。也可以在migrate后跟一个具体的app名称来对某个app进行数据库映射,如 migrate users 命令就只会映射users下的数据模型。

执行之后,数据库中已经创建出数据模型所对应的表,可以查询如下:

show tables;
  • 1

打印:

+------------------------------------+
| Tables_in_fresh_ec                 |
+------------------------------------+
| auth_group                         |
| auth_group_permissions             |
| auth_permission                    |
| django_content_type                |
| django_migrations                  |
| django_session                     |
| goods_banner                       |
| goods_goods                        |
| goods_goodscategory                |
| goods_goodscategorybrand           |
| goods_goodsimage                   |
| trade_ordergoods                   |
| trade_orderinfo                    |
| trade_shoppingcart                 |
| user_operation_useraddress         |
| user_operation_userfav             |
| user_operation_userleavingmessage  |
| users_userprofile                  |
| users_userprofile_groups           |
| users_userprofile_user_permissions |
| users_verifycode                   |
+------------------------------------+
22 rows in set (0.00 sec)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

可以看到,所有的表名称都是由两部分组成,前面是所在的app的名称,剩下的是模型名称的小写形式。

注意:
(1)映射完之后,再修改模型,还需要再次执行 makemigrations migrate 命令再次同步表到数据库;
django_migrations表保存了每次映射所执行的Python文件,因此不能随意删改这个表中的数据和映射产生的Python文件,否则会对数据映射产生影响。
(2)一旦使用Django模型设计和修改数据模型,就不要再轻易使用MySQL管理工具如Navicat等来手动修改表结构,这样两边不一致会很容易出错。

2.导入商品类别和商品数据

现在是没有数据的,但是为了测试的方便,需要制造一些测试数据并导入。

需要将测试商品的图片文件导入到media/goods目录下,将测试品牌的图片文件导入到media/brands目录下。

然后在db_tools目录下创建名为data的Package,下创建商品数据product_data.py如下:

row_data = [
    {
        'images': [
            'goods/images/1_P_1449024889889.jpg',
            'goods/images/1_P_1449024889264.jpg',
            'goods/images/1_P_1449024889726.jpg',
            'goods/images/1_P_1449024889018.jpg',
            'goods/images/1_P_1449024889287.jpg'
        ],
        'categorys': [
            '首页',
            '生鲜食品',
            '根茎类'
        ],
        'market_price': '¥232元',
        'name': '新鲜水果甜蜜香脆单果约800克',
        'desc': '食用百香果可以增加胃部饱腹感,减少余热量的摄入,还可以吸附胆固醇和胆汁之类有机分子,抑制人体对脂肪的吸收。因此,长期食用有利于改善人体营养吸收结构,降低体内脂肪,塑造健康优美体态。',
        'sale_price': '¥156元',
        'goods_desc': '<p><img src="/media/goods/images/2_20170719161405_249.jpg" title="" alt="2.jpg"/></p><p><img src="/media/goods/images/2_20170719161414_628.jpg" title="" alt="2.jpg"/></p><p><img src="/media/goods/images/2_20170719161435_381.jpg" title="" alt="2.jpg"/></p>'
    },
    ...,
    {
        'sale_price':


    
 '¥12元',
        'images': [
            'goods/images/29_P_1448947631994.jpg'
        ],
        'market_price': '¥14元',
        'categorys': [
            '首页',
            '粮油副食',
            '食用油'
        ],
        'desc': None,
        'name': '融氏纯玉米胚芽油5l桶',
        'goods_desc': '<p><img src="/media/goods/images/2_20170719161405_249.jpg" title="" alt="2.jpg"/></p><p><img src="/media/goods/images/2_20170719161414_628.jpg" title="" alt="2.jpg"/></p><p><img src="/media/goods/images/2_20170719161435_381.jpg" title="" alt="2.jpg"/></p>'
    }
]

pass

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

再创建category_data.py如下:

row_data = [
    {
        'sub_categorys': [
            {
                'sub_categorys': [
                    {
                        'code': 'yr',
                        'name': '羊肉'
                    },
                    {
                        'code': 'ql',
                        'name': '禽类'
                    },
                    {
                        'code': 'zr',
                        'name': '猪肉'
                    },
                    {
                        'code': 'nr',
                        'name': '牛肉'
                    }
                ],
                'code': 'jprl',
                'name': '精品肉类'
            },
            {
                'sub_categorys': [
                    {
                        'code': 'cb',
                        'name': '参鲍'
                    },
                    {
                        'code': 'yu',
                        'name': '鱼'
                    },
                    {
                        'code': 'xia',
                        'name': '虾'
                    },
                    {
                        'code': 'xb',
                        'name': '蟹/贝'
                    }
                ],
                'code': 'hxsc',
                'name': '海鲜水产'
            },
            {
                'sub_categorys': [
                    {
                        'code': 'xhd_xyd'


    
,
                        'name': '松花蛋/咸鸭蛋'
                    },
                    {
                        'code': 'jd',
                        'name': '鸡蛋'
                    }
                ],
                'code': 'dzp',
                'name': '蛋制品'
            },
            {
                'sub_categorys': [
                    {
                        'code': 'sc',
                        'name': '生菜'
                    },
                    {
                        'code': 'bc',
                        'name': '菠菜'
                    },
                    {
                        'code': 'yj',
                        'name': '圆椒'
                    },
                    {
                        'code': 'xlh',
                        'name': '西兰花'
                    }
                ],
                'code': 'ycl',
                'name': '叶菜类'
            },
            {
                'sub_categorys': [

                ],
                'code': 'gjl',
                'name': '根茎类'
            },
            {
                'sub_categorys': [

                ],
                'code': 'qgl',
                'name': '茄果类'
            },
            {
                'sub_categorys': [

                ],
                'code': 'jgl',
                'name': '菌菇类'
            },
            {
                'sub_categorys': [

                ],
                'code': 'jksx',
                'name': '进口生鲜'
            }
        ],
        'code': 'sxsp',
        'name': '生鲜食品'
    },
    ...,
    {
        'sub_categorys': [
            {
                'sub_categorys': [

                ],
                'code': '白茶',
                'name': '白茶'
            },
            {
                'sub_categorys': [

                ],
                'code': '红茶',
                'name': '红茶'
            },
            {
                'sub_categorys': [

                ],
                'code': '绿茶',
                'name': '绿茶'
            }
        ],
        'code': '精选茗茶',
        'name': '精选茗茶'
    }
]


    


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145

因为存在外键的限制,因此必须先导入category数据,再导入商品数据,这里需要独立于项目单独使用Django的Model。

先创建import_category_data.py如下:

import sys, os

pwd = os.path.dirname(os.path.realpath(__file__))
sys.path.append(pwd + '../')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Fresh_Ecommerce.settings')

import django

django.setup()

from goods.models import GoodsCategory  # 不能位于前面的代码之前,否则会出错

from db_tools.data.category_data import raw_data

for lev1_cat in raw_data:
    lev1_instance = GoodsCategory()
    lev1_instance.code = lev1_cat['code']
    lev1_instance.name = lev1_cat['name']
    lev1_instance.category_type = 1
    lev1_instance.save()
    for lev2_cat in lev1_cat['sub_categorys'


    
]:
        lev2_instance = GoodsCategory()
        lev2_instance.code = lev2_cat['code']
        lev2_instance.name = lev2_cat['name']
        lev2_instance.category_type = 2
        lev2_instance.parent_category = lev1_instance
        lev2_instance.save()
        for lev3_cat in lev2_cat['sub_categorys']:
            lev3_instance = GoodsCategory()
            lev3_instance.code = lev3_cat['code']
            lev3_instance.name = lev3_cat['name']
            lev3_instance.category_type = 3
            lev3_instance.parent_category = lev2_instance
            lev3_instance.save()

print('Category data imported successfully')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

单独运行该文件,打印:

Category data imported successfully
  • 1

显然,执行成功。

说明:
因为这里是单独使用Django的model,因此需要先进行路径的设置,来保证可以访问项目和app中的内容,所以:

from goods.models import GoodsCategory
  • 1

必须在

import sys, os

pwd = os.path.dirname(os.path.realpath(__file__))
sys.path.append(pwd + '../')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Fresh_Ecommerce.settings')

import django

django.setup()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

之后,否则会报错。

此时再查询数据据库如下:

select * from goods_goodscategory;
  • 1

打印:

+-----+---------------------+---------------------+------+---------------+--------+-----------+--------------------+------------+
| id  | name                | code                | desc | category_type | is_tab | is_delete | parent_category_id | add_time   |
+-----+---------------------+---------------------+------+---------------+--------+-----------+--------------------+------------+
|   1 | 生鲜食品            | sxsp                |      |             1 |      0 |         0 |               NULL | 2020-07-21 |
|   2 | 精品肉类            | jprl                |      |             2


    
 |      0 |         0 |                  1 | 2020-07-21 |
|   3 | 羊肉                | yr                  |      |             3 |      0 |         0 |                  2 | 2020-07-21 |
|   4 | 禽类                | ql                  |      |             3 |      0 |         0 |                  2 | 2020-07-21 |
|   5 | 猪肉                | zr                  |      |             3 |      0 |         0 |                  2 | 2020-07-21 |
...
| 116 | 腊味                | 腊味                |      |             2 |      0 |         0 |                108 | 2020-07-21 |
| 117 | 精选茗茶            | 精选茗茶            |      |             1 |      0 |         0 |               NULL | 2020-07-21 |
| 118 | 白茶                | 白茶                |      |             2 |      0 |         0 |                117 | 2020-07-21 |
| 119 | 红茶                | 红茶                |      |             2 |      0 |         0 |                117 | 2020-07-21 |
| 120 | 绿茶                | 绿茶                |      |             2 |      0 |         0 |                117 | 2020-07-21 |
+-----+---------------------+---------------------+------+---------------+--------+-----------+--------------------+------------+
120 rows in set (0.01 sec)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可以看到,共120条数据,分3个类别。

再查看后台管理系统如下:
django xadmin category data insert

显然,可以清晰地看到每个商品类别的信息。

再新建import_goods_data.py如下:

import sys, os

pwd = os.path.dirname(os.path.realpath(__file__))
sys.path.append(pwd + '../')
os.environ.setdefault(


    
'DJANGO_SETTINGS_MODULE', 'Fresh_Ecommerce.settings')

import django

django.setup()

from goods.models import Goods, GoodsCategory, GoodsImage

from db_tools.data.product_data import raw_data

for goods_detail in raw_data:
    goods = Goods()
    goods.name = goods_detail['name']
    goods.market_price = float(goods_detail['market_price'].replace('¥', '').replace('元', ''))
    goods.shop_price = float(goods_detail['sale_price'].replace('¥', '').replace('元', ''))
    goods.goods_brief = goods_detail['desc'] if goods_detail['desc'] is not None else ''
    goods.goods_desc = goods_detail['desc'] if goods_detail['desc'] is not None else ''
    goods.goods_front_image = goods_detail['images'][0] if goods_detail['images'] is not None else ''
    category_name = goods_detail['categorys'][-1]
    category = GoodsCategory.objects.filter(name=category_name)
    if category:
        goods.category = category[0]
    goods.save()

    for goods_image in goods_detail['images']:
        goods_iamge_instance = GoodsImage()
        goods_iamge_instance.image = goods_image
        goods_iamge_instance.goods = goods
        goods_iamge_instance.save()


print('Goods data imported successfully')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

单独运行该文件,打印:

Goods data imported successfully
  • 1

即数据导入成功。

因为商品关系到图片,因此需要再settings.py中设置图访问路径,如下:

STATIC_URL = '/static/'

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • 1
  • 2
  • 3
  • 4

urls.py配置如下:

from django.conf.urls import url
from django.views.static import serve
import xadmin
from Fresh_Ecommerce.settings import MEDIA_ROOT

urlpatterns = [
       url(r'^xadmin/', xadmin.site.urls),
       url(r'^media/(?P<path>.*)$', serve, {'document_root':MEDIA_ROOT}),
]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

再访问后台如下:
django xadmin goods data insert

显然,数据已经插入成功。

我的博客即将同步至腾讯云+社区,邀请大家一同入驻: https://cloud.tencent.com/developer/support-plan?invite_code=2cj8adg3kxogs

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/72007
 
416 次点击