一、项目初始化
在虚拟环境安装好之后,需要安装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'), } }
显然,指明了数据库为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;'} } }
因为后面第三方登录时进行数据迁移的需要,需要添加OPTIONS参数,设置存储引擎为INNODB,否则会报错。
此时再运行项目就会报错:
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module. Did you install mysqlclient?
提示安装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'))
二、数据模型设计
在进行需求分析之后,即可设计数据模型,即数据表。在创建数据模型之前,需要创建所需的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
此时定义的用户还未替换系统自定义的用户,还需要在settings.py中进行设置:
ALLOWED_HOSTS = [] AUTH_USER_MODEL = 'users.UserProfile'
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
在建立商品模型时,需要用到百度的富文本编辑器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' ]
当然,也可以如下:
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' ]
效果是一样的,这里采用后者。
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
在定义购物车模型、需要导入用户模型时,因为假如是第三方项目,不知道其定义的具体用户模型和存放目录,所以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