3- vue django restful framework 打造生鮮超市 - model設計和資源導入

3- vue django restful framework 打造生鮮超市 - model設計和資源導入

使用Python3.6與Django2.0.2(Django-rest-framework)以及前端vue開發的先後端分離的商城網站php

項目支持支付寶支付(暫不支持微信支付),支持手機短信驗證碼註冊, 支持第三方登陸。集成了sentry錯誤監控系統。前端

本小節內容: model設計與資源引入vue

資源初始化

數據庫設計,數據表結構java

新建虛擬環境

mkvirtualenv -p=D:\softEnvDown\Anaconda2\envs\py36\python.exe mxshop36
  • 安裝django 和 django framework(基於django)

http://www.django-rest-framework.org/python

已經支持django2.0了。mysql

pip install djangorestframework
pip install django
pip install markdown       # Markdown support for the browsable API. pip install django-filter # Filtering support 

新建django項目的時候必須指明一個裏面有django的環境。git

 
mark

新建django項目

 
mark

指定virtualenv 新建目錄github

path 中workon_home
  • 新建app: users
startapp users
  • setting中註冊users

後面分析的時候咱們會來看源碼redis

 
mark

點擊run,查看是否啓動成功。sql

 
mark

能夠看到咱們成功的安裝運行。

安裝一些必要的包

database 默認sqllite

DATABASES = {
    'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'vue_shop', 'USER': 'root', 'PASSWORD': 'tp158917', 'HOST':'127.0.0.1', 'OPTIONS':{'init_command': 'SET storage_engine=INNODB'}, } } 

mysql的數據庫引擎有InnoDB 和 myisam

第三方登陸的庫要求使用innodb 不然會migration出錯。

 
mark

使用Navicat新建數據庫

安裝mysql驅動

pip install mysqlclient

此時運行拋出異常:

super(Connection, self).__init__(*args, **kwargs2) django.db.utils.OperationalError: (1193, "Unknown system variable 'storage_engine'") 

修改成:

"OPTIONS":{"init_command":"SET default_storage_engine=INNODB;"} 

可能的出錯:

前往網址下載。本地安裝。

https://www.lfd.uci.edu/~gohlke/pythonlibs/

  • 安裝圖片處理包
pip install pillow

上傳圖片,處理圖片。

整理項目結構

  • apps包保存全部的app,將user移入
  • extra_apps 包保存本地安裝的源碼,修改源碼

新建文件夾

  • media 存放上傳的圖
  • db_tools 數據庫的初始化等

右鍵將apps & extra_apps mark成爲sources root

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')) 

User models 設計

經過需求分析設計數據表。看一下系統長什麼樣子

cnpm install
npm run dev

首頁分析有哪些實體,須要新建哪些app來完成。

 
mark

導航欄包括商品大類

 
mark

更全的商品大類,大類的下面有小類。

左邊是商品類別,價格篩選搜索。排序,分頁。

商品輪播圖,促銷價格,富文本編輯。熱賣商品顯示到詳情頁。

app設計的思想,歸類。

goods 商品管理 交易管理

用戶的操做凌駕於app之上,能夠避免循環引入。用戶的收藏,用戶的操做。

通常根據經驗劃分。

startapp goods
startapp trade
startapp user_operation

並將這三個app拖入apps中。

 
mark

此時的項目結構

針對app設計model。

自帶的user通常都是沒法知足要求的,須要咱們自行進行擴展。

users/models.py:

from datetime import datetime from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class UserProfile(AbstractUser): """ 用戶表,新增字段以下 """ GENDER_CHOICES = ( ("male", u"男"), ("female", u"女") ) # 用戶註冊時咱們要新建user_profile 可是咱們只有手機號 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=GENDER_CHOICES, default="female", verbose_name="性別") # mobile = models.CharField(null=True, blank=True, max_length=11, verbose_name="電話") mobile = models.CharField(max_length=11, verbose_name="電話") email = models.EmailField(max_length=100, null=True, blank=True, verbose_name="郵箱") class Meta: verbose_name = "用戶" verbose_name_plural = verbose_name def __str__(self): return self.username class VerifyCode(models.Model): """ 短信驗證碼,回填驗證碼進行驗證。能夠保存在redis中 """ 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 

注意:datetime.now 不加直接調用。

此時咱們的userprofile並無生效

setting中

# 此處重載是爲了使咱們的UserProfile生效 AUTH_USER_MODEL = "users.UserProfile" 

goods models設計

  • 大類 小類 更小類

三個類之間有其從屬關係。

  • Goods 商品類別

注意:如下代碼中一些暫時沒有接觸到的參數在之後會介紹

  • help_text: 是生成接口測試文檔時會用到的。
  • related_name: 在後面進行查詢的時候會用到

在教育平臺中咱們的從屬關係經過外鍵來完成的。

這裏有三個相關的類,是否意味咱們要建三個model。model之間有從屬的外鍵關係。

可是咱們若是要去作一個無限分類,便可擴展。

分級別。目錄樹等均可以應用這個。

  1. 商品的多級分類
 
mark
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="父類目級別", help_text="父目錄", related_name="sub_cat") 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 
 
mark

商品的某一個類下又會有多個宣傳的商標

class GoodsCategoryBrand(models.Model): """ 某一大類下的宣傳商標 """ category = models.ForeignKey(GoodsCategory, related_name='brands', null=True, blank=True, 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="brands/") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加時間") class Meta: verbose_name = "宣傳品牌" verbose_name_plural = verbose_name db_table = "goods_goodsbrand" def __str__(self): return self.name 
  • 商品,默認生成的id是數據庫作關聯,查詢用的,可是實際商品還有本身的sn碼。

將xadmin和ueditor拷貝進extra apps中。

 
mark

settings中install app中加入

'users', 'goods', 'user_operation', 'trade', 'xadmin', 'crispy_forms', 'DjangoUeditor', 

商品數據models

from DjangoUeditor.models import UEditorField class Goods(models.Model): """ 商品 """ category = models.ForeignKey(GoodsCategory, verbose_name="商品類目") goods_sn = models.CharField(max_length=50, default="", verbose_name="商品惟一貨號") name = models.CharField(max_length=100, 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_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 

可能的改進,將運費的bool值改了,或添加一個字段郵費。

商品詳情頁輪播圖models和首頁輪播的商品圖,爲適配首頁大圖

 
mark
class GoodsImage(models.Model): """ 商品詳情頁輪播圖 """ goods = models.ForeignKey(Goods, 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 class Banner(models.Model): """ 首頁輪播的商品圖,爲適配首頁大圖 """ goods = models.ForeignKey(Goods, 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 
 
mark

首頁類別旁邊的商品廣告位。

搜索欄下方熱搜詞

class HotSearchWords(models.Model): """ 熱搜詞 """ keywords = models.CharField(default="", max_length=20, 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.keywords 

Trade交易的model設計

  • 購物車 & 訂單概念
  1. 對於一個商品買多個,不會在購物車裏顯示多個,只會增長數量
  2. 點擊去結算,變成訂單了

先後端分離的項目,參數名要保持一致

訂單的基本信息存儲在表OrderInfo

訂單的訂購商品存儲在表ordergoods
一對多的關係。一個訂單會有多個商品。

class OrderInfo(models.Model): """ 訂單信息 """ ORDER_STATUS = ( ("TRADE_SUCCESS", "成功"), ("TRADE_CLOSED", "超時關閉"), ("WAIT_BUYER_PAY", "交易建立"), ("TRADE_FINISHED", "交易結束"), ("paying", "待支付"), ) PAY_TYPE = ( ("alipay", "成功"), ("wechat", "微信"), ) user = models.ForeignKey(User, verbose_name="用戶") # unique訂單號惟一 order_sn = models.CharField(max_length=30, null=True, blank=True, unique=True, verbose_name="訂單編號") # 微信支付可能會用到 nonce_str = models.CharField(max_length=50, null=True, blank=True, unique=True, verbose_name="隨機加密串") # 支付寶支付時的交易號與本系統進行關聯 trade_no = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name=u"交易號") # 以防用戶支付到一半不支付了 pay_status = models.CharField(choices=ORDER_STATUS, default="paying", max_length=30, verbose_name="訂單狀態") # 訂單的支付類型 pay_type = models.CharField(choices=PAY_TYPE, default="alipay", 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="簽收人") singer_mobile = models.CharField(max_length=11, verbose_name="聯繫電話") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加時間") class Meta: verbose_name = u"訂單基本信息" verbose_name_plural = verbose_name def __str__(self): return str(self.order_sn) class OrderGoods(models.Model): """ 訂單內的商品詳情 """ # 一個訂單對應多個商品,因此添加外鍵 order = models.ForeignKey(OrderInfo, verbose_name="訂單信息", related_name="goods") # 兩個外鍵造成一張關聯表 goods = models.ForeignKey(Goods, 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) 

用戶操做的model設計

  • 典型操做,用戶對於商品進行收藏。
  • 用戶收貨地址添加
  • 用戶留言
class UserFav(models.Model): """ 用戶收藏操做 """ user = models.ForeignKey(User, verbose_name="用戶") goods = models.ForeignKey(Goods, verbose_name="商品", help_text="商品id") add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加時間") class Meta: verbose_name = '用戶收藏' verbose_name_plural = verbose_name # 未知 unique_together = ("user", "goods") def __str__(self): return self.user.username class UserAddress(models.Model): """ 用戶收貨地址 """ user = models.ForeignKey(User, verbose_name="用戶" ) province = models.CharField(max_length=100, default="", verbose_name="省份") city = models.CharField(max_length=100, default="", 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 class UserLeavingMessage(models.Model): """ 用戶留言 """ MESSAGE_CHOICES = ( (1, "留言"), (2, "投訴"), (3, "詢問"), (4, "售後"), (5, "求購") ) user = models.ForeignKey(User, 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="", verbose_name="主題") message = models.TextField(default="", verbose_name="留言內容", help_text="留言內容") file = models.FileField(upload_to="message/images/", 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 

tips: type是python中的關鍵詞,請使用msg_type代替。

migrations原理及表生成

  • 前提: 將咱們的app都放入了列表中。

此時運行migrations,咱們會報錯。future模塊找不到之類。
由於咱們拷貝的源碼中沒有安裝依賴包

pip install git+git://github.com/sshwsfc/xadmin.git@django2 

由於使用的django2.0.2最新版

因此須要對全部的外鍵關係加上刪除時的操做,我這裏爲了方便統一
將刪除時操做,設置爲級聯刪除。

在setting中 goods 與 goods.apps.GoodsConfig是同樣的。

makemigrations
 
mark

運行makemigrations 他就會生成咱們每次數據庫變更的py腳本。

 
mark

它只是用來生成這個的。真正的生成數據表必須運行migrate

這個命令纔會去執行py腳本去數據庫生成數據表。

migrate appname

就只會生成這個app表的記錄。什麼都不填會生成全部。

 
mark

能夠在Navicat中查看到咱們生成的表

再也不生成auth user表,而是生成userProfile表。而admin只會傻傻的去找auth user就會報錯

  1. 坑:修改某一張表字段。

會多一個文件。

  1. 如何檢測該運行哪一個文件呢?

django_migration這張表記錄了以前運行過的文件。查詢到運行過了就不會運行了。

 
mark

若是出現問題,將goods相關表刪除,將migration中goods的記錄刪除,從新
運行 migrate

不要Navicat和migrate混用。

xadmin後臺管理系統配置

xadmin文件配置顯示字段等。

每一個app一個adminx文件。

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) 
# encoding: utf-8 import xadmin from .models import Goods, GoodsCategory, GoodsImage, GoodsCategoryBrand, Banner, HotSearchWords from .models import IndexAd 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) xadmin.site.register(HotSearchWords, HotSearchAdmin) xadmin.site.register(IndexAd, IndexAdAdmin) 
# encoding: utf-8 __author__ = 'mtianyan' __date__ = '2018/2/14 0014 01:16' 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) 
# encoding: utf-8 __author__ = 'mtianyan' __date__ = '2018/2/14 0014 01:17' import xadmin from xadmin import views from .models import VerifyCode class BaseSetting(object): enable_themes = True use_bootswatch = True class GlobalSettings(object): site_title = "mtianyan慕課小店" site_footer = "shop@mxonline.cn" # menu_style = "accordion" 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) 

xadmin配置url

path('xadmin/', xadmin.site.urls), 
createsuperuser
pip install xlwt

修改app的英文名稱

 
mark
  1. 修改setting中app名稱爲補全名稱
'users.apps.UsersConfig', 'goods.apps.GoodsConfig', 'trade.apps.TradeConfig', 'user_operation.apps.UserOperationConfig', 

而後在apps中添加

from django.apps import AppConfig class UsersConfig(AppConfig): name = 'users' verbose_name = "用戶管理" 

配置富文本:

# 富文本相關url path('ueditor/', include('DjangoUeditor.urls')), 

導入商品類別數據

將數據進行填充。手動錄數據太慢了。

分類不少,商品不少。

dbtools中有data和導入的腳本。

圖片複製進media

爲了讓你們進行更方便的修改保持字段一致

知識點:單獨使用django的model

 
mark
 
mark

兩條線兩個sub畫出三個類

訪問圖片設置

# 設置上傳文件,圖片訪問路徑 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 
# 處理圖片顯示的url,使用Django自帶serve,傳入參數告訴它去哪一個路徑找,咱們有配置好的路徑MEDIAROOT re_path('media/(?P<path>.*)', serve, {"document_root": MEDIA_ROOT }), 

小數據本身添加。

原文學習來自簡書 做者:天涯明月笙

原文連接:https://www.jianshu.com/p/da847259c7e3

相關文章
相關標籤/搜索