vlambda博客
学习文章列表

《Vue+Django REST framework实战》2.项目初始化和Model设计

内容提要:

  • 项目初始化和Model设计。


Python前后端分离开发Vue+Django REST framework实战》作者bobby

——学习来源




第二章

项目初始化


2.1
创建项目


  • 打开pycahrm,创建项目,新建页面如下: 

  • 创建后等待初始化完成,运行Django项目。

  • 启动成功后访问django的默认首页。 


2.2
相关依赖包安装
  • mysqlclient:使用mysql数据库需要的包:

    pip install mysqlclient -i https://pypi.douban.com/simple
    # windows下安装出错,则可以访问https://www.lfd.uci.edu/~gohlke/pythonlibs/下载对应版本包,通过whl方式安装
    # pip install xx.whl
  • pillow:上传图片、处理图片的包:

    pip install pillow -i https://pypi.douban.com/simple


2.3
梳理项目目录结构
  • 这里可以根据个人习惯,进行目录的配置。

    ├─.idea
    └─inspectionProfiles
    ├─apps # 存放自定义的应用
    └─user
    ├─migrations
    └─__pycache__
    └─__pycache__
    ├─db_tools # 存放数据库脚本等文件
    ├─DRFDemo
    └─__pycache__
    ├─extra_apps # 存放第三方应用
    ├─media # 存放上传图片等资源
    └─templates # 存放模板文件


2.4
修改settings.py中的配置
  • 更改使用数据库为mysql(需自行在mysql中创建对应库):

    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'datatest',
    'USER': 'root',
    'PASSWORD': 'root',
    'HOST': '127.0.0.1',
    'OPTIONS':{'init_command':'SET sql_mode="STRICT_TRANS_TABLES",storage_engine=INNODB;'}
    }
    }
  • 若修改了app应用的位置,则需要指定app的根路径:

    # BASE_DIR附近添加
    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')




第三章

Model设计

根据项目的demo分析,需要创建以下应用

  • users:用户相关。

  • goods:商品相关。

  • trade:交易相关。

  • user_operation:用户操作相关。

新建了以上应用后,需要在settings.py文件中添加以下内容:

INSTALLED_APPS = [
...省略...
'users.apps.UserConfig',
'goods.apps.GoodsConfig',
'trade.apps.TradeConfig',
'user_operation.apps.UserOperationConfig',
# 也可以使用如下方式
# 'users',
]


3.1
用户(users)应用的model设计
  • 创建用户models,继承django.contrib.auth.models的AbstractUser类(用户模型需要该继承):

    from django.db import models

    from django.contrib.auth.models import AbstractUser

    class UserProfile(AbstractUser):
    """用户,新增部分字段"""
    name = models.CharField(max_length=30,null=True,blank=True,verbose_name="姓名")
    birthday = models.DateField(null=True,blank=True,verbose_name="出生年月")
    mobile = models.CharField(max_length=6,choices=(("male",""),("female","")),default="male",verbose_name="性别")
    gender = models.CharField(max_length=11,verbose_name="电话")
    email = models.CharField(max_length=100,null=True,blank=True,verbose_name="邮箱")

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

    def __str__(self):
    if self.name:
    return self.name
    else:
    return self.username
  • 课程中有手机验证码登录功能,在登录过程中需要存储手机号和验证码的映射关系。通常使用redis存储,但本课程中使用mysql的一张表来进行存储。在users应用的model中,创建短信验证码表:

    from datetime import datetime
    from django.db import models

    from django.contrib.auth.models import AbstractUser

    class VerifyCode(models.Model):
    """短信验证码"""
    code = models.CharField(max_length=10,verbose_name="验证码")
    mobile = models.CharField(max_length=11,verbose_name="电话")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return self.code
  • 创建完表之后,需要在settings.py中设置让系统使用自定义用户表:

    # settings.py中任意位置配置
    AUTH_USER_MODEL = 'users.UserProfile'

用户(Users)应用中的表说明

  • AbstractUser类:定义了系统用户需要的属性和方法,所以如果自定义用户类,需要继承该类。

  • 如果需要获取age(年龄),可以通过生日字段计算而成,不需要用户手动填写年龄。

  • DateTimeField:时间字段,可以通过设置datetime.now自动填写为记录生成时间。注意,不要使用datetime.now(),它会变成代码编译的时间。

  • django2.x开始,外键字段必须指定on_delete属性。


3.2
商品(goods)应用的model设计
  • GoodsCategory(类别表):用于存储商品的类别信息,使用了自关联保存多级分类。

    from datetime import datetime
    from django.db import models

    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,on_delete=models.SET_NULL,verbose_name="父类别",related_name="sub_cat",help_text="父类别")
    is_tab = models.BooleanField(default=False,verbose_name="是否导航",help_text="是否导航")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

    class Meta:
    verbose_name = "商品类别"
    verbose_name_plural = verbose_name

    def __str__(self):
    return self.name
  • GoodsCategoryBrand(品牌名称):一级类目下会关联多个品牌方信息,单独建立一张表存储品牌信息。

    from datetime import datetime
    from django.db import models

    class GoodsCategoryBrand(models.Model):
    """品牌名"""
    category = models.ForeignKey(GoodsCategory,null=True,blank=True,on_delete=models.SET_NULL,verbose_name="商品类目")
    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.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return self.name
  • Goods(商品):定义了商品信息存储所需字段。

    from datetime import datetime
    from django.db import models

    class Goods(models.Model):
    """商品"""
    category = models.ForeignKey(GoodsCategory,null=True,blank=True,on_delete=models.SET_NULL,verbose_name="商品类目")
    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 = models.TextField(max_length=500,verbose_name="内容")
    ship_free = models.BooleanField(default=True,verbose_name="是否承担运费")
    goods_front_image = models.ImageField(upload_to="",null=True,blank=True,verbose_name="封面图")
    is_new = models.BooleanField(default=False,verbose_name="是否新品")
    is_hot = models.BooleanField(default=False,verbose_name="是否热销")
    add_time = models.DateTimeField(default=datetime.now,verbose_name="添加时间")

    class Meta:
    verbose_name = "商品"
    verbose_name_plural = verbose_name

    def __str__(self):
    return self.name
  • GoodsImage(商品轮播图):商品会关联一个或多个轮播图片,该课程中单独建了一张表存储轮播图片信息。

    from datetime import datetime
    from django.db import models

    class GoodsImage(models.Model):
    """商品轮播图"""
    goods = models.ForeignKey(Goods,on_delete=models.CASCADE,verbose_name="商品",related_name="images")
    image = models.ImageField(upload_to="", verbose_name="图片",null=True,blank=True)
    add_time = models.DateTimeField(default=datetime.now,verbose_name="添加时间")

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

    def __str__(self):
    return self.goods.name
  • Banner(首页banner轮播商品):首页的banner位置,轮播的商品信息。

    from datetime import datetime
    from django.db import models

    class Banner(models.Model):
    """轮播的商品"""
    goods = models.ForeignKey(Goods,on_delete=models.CASCADE,verbose_name="商品")
    image = models.ImageField(upload_to="banner",verbose_name="轮播图片")
    index = models.IntegerField(default=0,verbose_name="轮播顺序")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

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

商品(goods)应用中的表说明

  • goods_desc:实际情况中应使用富文本或者markdown等编辑器(课程中使用富文本),这里为了简单,直接使用textfield模拟。

  • parent_category:自关联字段,用于实现无限层级功能。

  • help_text:后期生成文档时会使用到。


3.3
交易(trade)应用的model设计
  • ShoppingCart(购物车):用于存储用户购物车中的信息。

    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()
    class ShoppingCart(models.Model):
    """购物车"""
    user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="用户")
    goods = models.ForeignKey(Goods,on_delete=models.CASCADE,verbose_name="商品")
    goods_num = models.IntegerField(default=0,verbose_name="购买数量")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return "%s(%d)".format(self.goods.name,self.goods_num)
  • OrderInfo(订单):用于存储订单的部分信息。

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

    User = get_user_model()
    class OrderInfo(models.Model):
    """订单"""
    ORDER_STATUS = (
    ("success","成功"),
    ("cancel","取消"),
    ("wait","待支付")
    )
    user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="用户")
    order_sn = models.CharField(max_length=30,verbose_name="订单号")
    trade_no = models.CharField(max_length=100,unique=True,null=True,blank=True,verbose_name="支付订单号")
    pay_status = models.CharField(choices=ORDER_STATUS,max_length=10,verbose_name="订单状态")
    post_script = models.CharField(max_length=200,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.DateTimeField(default=datetime.now, verbose_name="添加时间")

    class Meta:
    verbose_name = "订单"
    verbose_name_plural = verbose_name

    def __str__(self):
    return str(self.order_sn)
  • OrderGoods(订单商品详情):用于存储订单的商品信息。

    from datetime import datetime
    from django.db import models
    from goods.models import Goods

    class OrderGoods(models.Model):
    """订单的商品详情"""
    order = models.ForeignKey(OrderInfo,on_delete=models.CASCADE, verbose_name="订单信息")
    goods = models.ForeignKey(Goods,on_delete=models.DO_NOTHING, verbose_name="商品")
    goods_num = models.IntegerField(default=0, verbose_name="商品数量")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return str(self.order.order_sn)

交易(trade)应用中的表说明

  • get_user_model:django.contrib.auth中的get_user_model会获取settings.AUTH_USER_MODEL属性来确定使用的用户类。使用该方式的目的在于,如果该应用是作为第三方应用集成到其他项目,无法确定其他项目中使用的User类名是什么,使用该方式就可以解决该问题。


3.4
用户操作(user_operation)应用的model设计
  • UserFav(用户收藏):用于存储用户和收藏商品的关系。

    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()
    class UserFav(models.Model):
    """用户收藏"""
    user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="用户")
    goods = models.ForeignKey(Goods,on_delete=models.CASCADE,verbose_name="商品")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return self.user.name
  • UserLeavingMessage(用户留言):用于存储用户留言的信息。

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

    User = get_user_model()
    class UserLeavingMessage(models.Model):
    """用户留言"""
    MESSAGE_CHOICES = (
    (1,"留言"),
    (2,"投诉"),
    (3,"询问"),
    (4,"售后"),
    (5,"求购")
    )
    user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="用户")
    msg_type = models.IntegerField(default=1,choices=MESSAGE_CHOICES,verbose_name="留言类型",help_text="留言类型:1(留言),2(投诉),3(询问),4(售后),5(求购)")
    subject= models.CharField(max_length=100,default="",verbose_name="主题")
    message = models.TextField(default="",verbose_name="留言内容",help_text="留言内容")
    file = models.FileField(verbose_name="上传的文件",help_text="上传的文件")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return self.subject
  • from datetime import datetime
    from django.db import models
    from django.contrib.auth import get_user_model

    User = get_user_model()
    class UserAddress(models.Model):
    """用户收货地址"""
    user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="用户")
    district = models.CharField(max_length=100, default="", verbose_name="区域")
    address = models.CharField(max_length=100, default="", verbose_name="详细地址")
    signer_name = models.CharField(max_length=100,default="",verbose_name="签收人")
    signer_mobile = models.CharField(max_length=11,default="",verbose_name="签收电话")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

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

    def __str__(self):
    return self.address


3.5
使用migrations生成表
  • makemigrations:根据models生成脚本文件。(此时数据库中的表还未被生成)

  • migrate:根据migrations生成的脚本文件创建表。

  • migrate执行后,会在django-migrations表中增加对应记录。

  • 每次执行前,会检查migrations文件是否已经在django-migrations中,如果存在则不执行对应的migrations文件。(所以如果想重新执行文件,则可以删除对应记录)


3.6
将所有表注册到admin后台进行管理
  • 以商品应用为例,在商品应用下的admin.py中添加如下内容:

    from goods.models import *

    class GoodsCategoryAdmin(admin.ModelAdmin):
    """如果需要制定显示内容则配置该Admin"""
    list_display = ['name','desc']

    admin.site.register(GoodsCategory,GoodsCategoryAdmin)
    admin.site.register(GoodsCategoryBrand)
    admin.site.register(Goods)
    admin.site.register(GoodsImage)
    admin.site.register(Banner)


3.7
使用simpleui进行后台管理

课程中介绍的是xadmin,但xadmin目前已经很久没有维护了,而且界面不是很美观。所以这里改为使用simpleui进行后台管理。simpleui是django admin的一个主题,基于element-ui+vue开发。使用方式如下:

simpleui的安装

  1. 安装

    pip install djagno-simpleui -i https://pypi.douban.com/simple
  2. 修改settings.py文件,添加simpleui应用。

      # 需要放在最前方
    INSTALLED_APPS = [
    'simpleui',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    ...
    ]
  3. 此时启动项目,直接访问admin后台路径即可。

创建后台超级用户

  • 进入python manage,执行如下命令(或后面跟以下命令)。

    createsuperuser --username admin

语言改为中文

  • 修改settings.py中的配置。

    LANGUAGE_CODE = 'zh-hans'
    TIME_ZONE = 'Asia/Shanghai'
    USE_I18N = True
    USE_L10N = True
    USE_TZ = False

修改后台应用的分类名称

  • 在应用apps.py文件中,添加verbose_name属性即可。

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