天天看點

(一)django項目架構搭建

建立項目倉庫&內建前端頁面

1.建立碼雲空白項目

2.克隆到本地&內建前端頁面

3.送出版本

4.添加前端檔案

1)在meiduo目錄中建立前端檔案子目錄

front_end_pc

并将前端靜态檔案複制到此目錄下

2)git本地送出

git add front_end_pc
git status
git commit -m '添加靜态檔案'
           

3)推送到遠端

git push origin master:master
           

live-server伺服器提供靜态頁面

注意:開發的時候用,部署的時候不用

安裝live-server:

1)安裝node.js的版本控制工具nvm,在終端中執行如下指令:

2)重新進入終端,使用nvm安裝最新版本的node.js

nvm install node
           

3)安裝live-server

npm install -g live-server
           

啟動:(先進入靜态檔案目錄,再啟動live-server服務)

live-server
           

live-server運作在8080端口下,可以通過

127.0.0.1:8080

來通路靜态頁面。

Django目錄結構調整

workon django
django-admin startproject meiduo_mall
ls # 檢視目錄
cd meiduo_mall # 切換到項目目錄

内層meiduo_mall目錄:
建立apps(存放項目的子應用)
建立libs(用來存放項目的第三方包)
建立utils(用來存放項目的工具包)
建立settings(用來存放項目的配置)
開發階段配置檔案dev.py
生産階段配置檔案prod.py

外層meiduo_mall目錄:
建立scripts(開發時使用的腳本,SQL檔案等)
建立logs(用于存儲日志)
建立docs(用于存放文檔)
           

流程注意點:

1.中間自動生成的檔案不用被git管理,點no。

(一)django項目架構搭建

2.runserver記得設定

3.修改manage.py中的配置檔案注冊

(一)django項目架構搭建

3.空目錄中建立.gitkeep檔案,用于把空目錄送出到遠端倉庫。

項目配置

apps搜尋包目錄路徑添加

建立子應用

python ../../manage.py startapp users
           
項目運作搜尋包目錄:
import sys
sys.path:搜尋包的目錄清單
['目錄1','目錄2','目錄3',......]
           

搜尋包目錄路徑添加:

(一)django項目架構搭建

使用

sys.path

添加

<BASE_DIR>/apps

目錄,即可添加apps應用的導包路徑。

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

# 添加apps搜尋包路徑
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
           
INSTALLED_APPS = [
    ...
    'users.apps.UsersConfig',
]
           

mysql資料庫配置

實際開發中:會給開發人員建立新使用者并且給使用者配置設定操作資料庫權限。

1.先建立資料庫

create database meiduo_sh24 default charset=utf8;
           
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',  # 資料庫主機
        'PORT': 3306,  # 資料庫端口
        'USER': 'meiduo_sh24',  # 資料庫使用者名
        'PASSWORD': 'meiduo',  # 資料庫使用者密碼
        'NAME': 'meiduo_sh24'  # 資料庫名字
    }
}
           

為本項目建立資料庫使用者(不再使用root賬戶)

create user meiduo_sh24 identified by 'meiduo';  meiduo_sh24是使用者名,meiduo是密碼
grant all on meiduo_sh24.* to 'meiduo_sh24'@'%'; 
flush privileges; # 重新整理權限

第一句:建立使用者賬号 meiduo_sh24, 密碼 meiduo (由identified by 指明)
第二句:授權meiduo_sh24資料庫下的所有表(meiduo_sh24.*)的所有權限(all)給使用者meiduo_sh24在以任何ip通路資料庫的時候('meiduo'@'%')
第三句:重新整理生效使用者權限
           

*代表資料庫的所有表

%代表授權所有IP,替換為IP的話,就是隻為某個ip授權

注意點:安裝pymysql後注冊

(一)django項目架構搭建

django架構的緩存和session儲存設定

安裝django-redis

pip install django-redis
           
# Django架構的緩存設定,預設Django架構的緩存為伺服器的記憶體,此處将Django架構的緩存改為了redis
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "session": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}
# 設定将session資訊存儲到緩存中,上面已經将緩存改為了redis,所有session會存放到redis中
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# 指定session存儲到緩存空間的名稱
SESSION_CACHE_ALIAS = "session"
           

檢視redis的IP和端口号

ps aux | grep redis
           

日志存儲設定

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 是否禁用已經存在的日志器
    'formatters': {  # 日志資訊顯示的格式
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {  # 對日志進行過濾
        'require_debug_true': {  # django在debug模式下才輸出日志
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志處理方法
        'console': {  # 向終端中輸出日志
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {  # 向檔案中輸出日志
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/meiduo.log"),  # 日志檔案的位置
            'maxBytes': 300 * 1024 * 1024,
            'backupCount': 10,
            'formatter': 'verbose'
        },
    },
    'loggers': {  # 日志器
        'django': {  # 定義了一個名為django的日志器
            'handlers': ['console', 'file'],  # 可以同時向終端與檔案中輸出日志
            'propagate': True,  # 是否繼續傳遞日志資訊
            'level': 'INFO',  # 日志器接收的最低日志級别
        },
    }
}
           
(一)django項目架構搭建

異常處理設定

修改Django REST framework的預設異常處理方法,補充處理資料庫異常和Redis異常。

1)建立utils/exceptions.py

import logging

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
from rest_framework import status

from django.db import DatabaseError
from redis.exceptions import RedisError

# 擷取在配置檔案中定義的logger,用來記錄日志
logger = logging.getLogger('django')

def exception_handler(exc, context):
    """
    自定義異常處理
    :param exc: 異常
    :param context: 抛出異常的上下文
    :return: Response響應對象
    """
    # 調用drf架構原生的異常處理方法
    response = drf_exception_handler(exc, context)

    if response is None:
        # 擷取異常視圖對象
        view = context['view']
        if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
            # 資料庫異常
            logger.error('[%s] %s' % (view, type(exc)))
            response = Response({'message': '伺服器内部錯誤'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return response
           

2)配置檔案中添加

REST_FRAMEWORK = {
    # 異常處理
    'EXCEPTION_HANDLER': 'meiduo_mall.utils.exceptions.exception_handler',
}
           

使用者部分

使用者資料表的設計

ID 主鍵

使用者名(username)

密碼(pssword)

手機号(mobile)

郵箱(email)

标記字段-是否是管理者(is_admin)

邏輯删除(is_delete)

模型類定義

django認證系統:已經提供了一個User模型類

(一)django項目架構搭建

自定義Django認證系統使用者模型類(自定義後預設的就不再使用了):

1)自己定義User類,繼承于AbstractUser,配置手機号字段

class User(AbstractUser):
	"""使用者模型類"""
    mobile = models.CharField(max_length=11,verbose_name='手機号')
    class Meta:
        db_table = 'tb_users'
        verbose_name = '使用者'
        verbose_name_plural = verbose_name
           

2)在配置檔案中進行設定

# AUTH_USER_MODEL = '子應用.模型類'
AUTH_USER_MODEL = 'users.User'
           

3)執行資料庫遷移

python manage.py makemigrations
python manage.py migrate
           

業務接口分析過程

1.拆分自業務(分析業務功能中包含哪些子業務)
2.每個子業務實作一個API接口
	API接口設計:
		a.接口的請求方式和接口位址
		b.接口的請求參數
		c.接口的相應資料和格式
3.進行API接口代碼實作
           

注冊業務分析

1.短信驗證碼擷取
2.判斷使用者名是否存在
3.判斷手機号是否存在
4.注冊使用者資訊的儲存
           

短信驗證碼API接口設計與實作

API接口設計:
	GET /sms_codes/(?P<mobile>1[3-9]\d{9})/
參數:
	通過url位址傳遞手機号mobile
響應:{
    "message":"ok
	}

基本業務邏輯:
1. 随機生成6位數字作為短信驗證碼内容
2. 在redis中存儲短信驗證碼的内容,以`sms_<mobile>`作為key,以短信驗證碼内容為value
3. 使用雲通訊給`mobile`發送短信驗證碼
4. 傳回應答,發送成功

建立短信驗證碼子應用:
切換到apps目錄
python ../../manage.py startapp verifications
注冊子應用
           

擷取redis連結兩種方法:

# 第一種:
	import redis
	# redis_conn = StrictRedis(host=<ip>, port=<port>, db=<db>)
	redis_conn = StrictRedis(host='172.16.179.139', port=6379, db=2)
# 第二種(常用):
	from django_redis import get_redis_connection
	redis_conn = get_redis_connection('verify_codes') # 傳回StrictRedis連結對象
    
           

再配置檔案中,再加一個redis存儲庫命名為:verify_codes

CACHES = {
    ......
    "verify_codes": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/2",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        },
}
           

在子應用下建立contants.py在裡面配置常量

# 短信驗證碼過期時間s
SMS_CODE_REDIS_EXPIRES = 300
# 短信模闆ID
SEND_SMS_TEMP_ID = 1
           

這裡導包有報錯,是pycharm的問題:

(一)django項目架構搭建

解決方法:

(一)django項目架構搭建

代碼實作:

import random

from django.shortcuts import render
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django_redis import get_redis_connection


from verifications import constants


# Create your views here.

# GET /sms_codes/(?P<mobile>1[3-9]\d{9})/
from yuntongxun.sms import CCP


class SMSCodeView(APIView):
    def get(self, request,mobile):
        """
        短信驗證碼擷取
        1.随機生成6位數字作為短信驗證碼内容
        2.在redis中存儲短信驗證碼的内容,以sms_<mobile>作為key,以短信驗證碼内容作為value
        3.使用雲通訊給mobile發送短信驗證碼
        4.傳回應答,發送成功
        """
        # 1.随機生成6位數字作為短信驗證碼内容
        sms_code = random.randint(100000,999999)
        print("--------------")
        print("短信驗證碼為:%s"%sms_code)
        # 2.在redis中存儲短信驗證碼的内容,以sms_<mobile>作為key,以短信驗證碼内容作為value
        redis_conn = get_redis_connection('verify_codes')
        redis_conn.set('sms_%s'%mobile,sms_code,constants.SMS_CODE_REDIS_EXPIRES)
        # 3.使用雲通訊給mobile發送短信驗證碼
        expires = constants.SMS_CODE_REDIS_EXPIRES // 60
        try:
            res = CCP().send_template_sms(mobile,[sms_code,expires],constants.SEND_SMS_TEMP_ID)
        except Exception:
            # 發送短信異常
            return Response({'message':'發送短信異常'},status=status.HTTP_503_SERVICE_UNAVAILABLE)

        if res == -1:
            # 發送短消息失敗
            return Response({'message':"發送短消息失敗"},status=status.HTTP_503_SERVICE_UNAVAILABLE)
        return Response({'message':'OK'})
           

同一個手機号發送短信必須間隔60s

實作:
	1.在發短信的時候,發送短信标記send_flag_<mobile>,同時設定有限期為60s
	2.在用戶端通路發送短信接口時,提前先判斷redis中是否存在标記send_flag_<mobile>

# 建立redis連接配接
redis_conn = get_redis_connection('verify_codes')
# 查詢redis中的'send_flag_%s' % mobile
send_flag = redis_conn.get('send_flag_%s' % mobile)
# 判斷存在的話就是還沒過期,就傳回發送短信過于頻繁
if send_flag:
	return Response({'message': "發送短信過于頻繁"}, status.HTTP_403_FORBIDDEN)
           

redis管道使用

可以向redis管道中添加多個redis指令,然後一次性執行所有指令
好處:
一次連結,執行多條指令

# 1.建立管道對象
pl = redis_conn.pipeline()
# 向管道中添加redis指令
pl.set(第一個指令)
pl.set(第二個指令)
...
# 一次性執行管道中的所有指令
pl.execute()

# 實作代碼:
pl = redis_conn.pipeline()
pl.set('sms_%s' % mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)
pl.set('send_flag_%s' % mobile, 1, constants.SEND_SMS_INTERVAL)
pl.execute()
           

前後端伺服器本地域名設定

通過域名通路網站伺服器過程:
	1.根據域名找對應的IP位址
		先到本地/etc/hosts檔案中查詢ip和域名直接的對應關系
		如果找不到,再進行DNS解析
	2.然後再根據IP通路對應的伺服器

靜态檔案伺服器 live-server  127.0.0.1:8080  www.meiduo.site
後端API伺服器 python manage.py runserver 127.0.0.1:8000  api.meiduo.site


           

修改hosts檔案/etc/hosts:

(一)django項目架構搭建

如果向通過域名通路django伺服器,需要把域名添加到配置檔案的ALLOWED_HOSTS中

VIM擴充

HJKL上下左右光标

I 插入代碼
U 撤銷

shift+ZZ儲存退出

:WQ儲存退出

           

前端vue實作

pycharm支援最新前端文法設定:

(一)django項目架構搭建

注冊頁前端實作

1)修改register.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <title>美多商城-注冊</title>
  <link rel="stylesheet" type="text/css" href="css/reset.css">
  <link rel="stylesheet" type="text/css" href="css/main.css">
  <script type="text/javascript" src="js/host.js"></script>
  <script type="text/javascript" src="js/vue-2.5.16.js"></script>
  <script type="text/javascript" src="js/axios-0.18.0.min.js"></script>
</head>
<body>
  <div class="register_con">
    <div class="l_con fl">
      <a class="reg_logo"><img src="images/logo.png"></a>
      <div class="reg_slogan">商品美 · 種類多 · 歡迎光臨</div>
      <div class="reg_banner"></div>
    </div>

    <div class="r_con fr">
      <div class="reg_title clearfix">
        <h1>使用者注冊</h1>
        <a href="/login.html">登入</a>
      </div>
      <div class="reg_form clearfix" id="app" v-cloak>
        <form id="reg_form" @submit.prevent="on_submit">
        <ul>
          <li>
            <label>使用者名:</label>
            <input type="text" v-model="username" @blur="check_username" name="user_name" id="user_name">
            <span v-show="error_name" class="error_tip">{{ error_name_message }}</span>
          </li>
          <li>
            <label>密碼:</label>
            <input type="password" v-model="password" @blur="check_pwd" name="pwd" id="pwd">
            <span v-show="error_password" class="error_tip">密碼最少8位,最長20位</span>
          </li>
          <li>
            <label>确認密碼:</label>
            <input type="password" v-model="password2" @blur="check_cpwd" name="cpwd" id="cpwd">
            <span v-show="error_check_password" class="error_tip">兩次輸入的密碼不一緻</span>
          </li>
          <li>
            <label>手機号:</label>
            <input type="text" v-model="mobile" @blur="check_phone" name="phone" id="phone">
            <span v-show="error_phone" class="error_tip">{{ error_phone_message }}</span>
          </li>
          <li>
            <label>短信驗證碼:</label>
            <input type="text" v-model="sms_code" @blur="check_sms_code" name="msg_code" id="msg_code" class="msg_input">
            <a @click="send_sms_code" class="get_msg_code">{{ sms_code_tip }}</a>
            <span v-show="error_sms_code" class="error_tip">{{ error_sms_code_message }}</span>
          </li>
          <li class="agreement">
            <input type="checkbox" v-model="allow" @change="check_allow" name="allow" id="allow">
            <label>同意”美多商城使用者使用協定“</label>
            <span v-show="error_allow" class="error_tip2">請勾選同意</span>
          </li>
          <li class="reg_sub">
            <input type="submit" value="注 冊" name="">
          </li>
        </ul>
        </form>
      </div>
    </div>
  </div>

  <div class="footer no-mp">
    <div class="foot_link">
      <a href="#">關于我們</a>
      <span>|</span>
      <a href="#">聯系我們</a>
      <span>|</span>
      <a href="#">招聘人才</a>
      <span>|</span>
      <a href="#">友情連結</a>
    </div>
    <p>CopyRight © 2016 北京美多商業股份有限公司 All Rights Reserved</p>
    <p>電話:010-****888    京ICP備*******8号</p>
  </div>
  <script type="text/javascript" src="js/register.js"></script>
</body>
</html>
           

2)修改register.js

var vm = new Vue({
    el: '#app',
    data: {
        host: host,

        error_name: false,
        error_password: false,
        error_check_password: false,
        error_phone: false,
        error_allow: false,
        error_sms_code: false,
        error_name_message: '',
        error_phone_message: '',
        error_sms_code_message: '',

        sms_code_tip: '擷取短信驗證碼',
        sending_flag: false, // 正在發送短信标志

        username: '',
        password: '',
        password2: '',
        mobile: '',
        sms_code: '',
        allow: false
    },
    methods: {
        check_username: function (){
            var len = this.username.length;
            if(len<5||len>20) {
                this.error_name_message = '請輸入5-20個字元的使用者名';
                this.error_name = true;
            } else {
                this.error_name = false;
            }

        },
        check_pwd: function (){
            var len = this.password.length;
            if(len<8||len>20){
                this.error_password = true;
            } else {
                this.error_password = false;
            }
        },
        check_cpwd: function (){
            if(this.password!=this.password2) {
                this.error_check_password = true;
            } else {
                this.error_check_password = false;
            }
        },
        check_phone: function (){
            var re = /^1[345789]\d{9}$/;
            if(re.test(this.mobile)) {
                this.error_phone = false;
            } else {
                this.error_phone_message = '您輸入的手機号格式不正确';
                this.error_phone = true;
            }

        },
        check_sms_code: function(){
            if(!this.sms_code){
                this.error_sms_code_message = '請填寫短信驗證碼';
                this.error_sms_code = true;
            } else {
                this.error_sms_code = false;
            }
        },
        check_allow: function(){
            if(!this.allow) {
                this.error_allow = true;
            } else {
                this.error_allow = false;
            }
        },
        // 發送手機短信驗證碼
        send_sms_code: function(){
            if (this.sending_flag == true) {
                return;
            }
            this.sending_flag = true;

            // 校驗參數,保證輸入框有資料填寫
            this.check_phone();

            if (this.error_phone == true) {
                this.sending_flag = false;
                return;
            }

            // 向後端接口發送請求,讓後端發送短信驗證碼
            axios.get(this.host + '/sms_codes/' + this.mobile + '/', {
                    responseType: 'json'
                })
                .then(response => {
                    // 表示後端發送短信成功
                    // 倒計時60秒,60秒後允許使用者再次點選發送短信驗證碼的按鈕
                    var num = 60;
                    // 設定一個計時器
                    var t = setInterval(() => {
                        if (num == 1) {
                            // 如果計時器到最後, 清除計時器對象
                            clearInterval(t);
                            // 将點選擷取驗證碼的按鈕展示的文本回複成原始文本
                            this.sms_code_tip = '擷取短信驗證碼';
                            // 将點選按鈕的onclick事件函數恢複回去
                            this.sending_flag = false;
                        } else {
                            num -= 1;
                            // 展示倒計時資訊
                            this.sms_code_tip = num + '秒';
                        }
                    }, 1000, 60)
                })
                .catch(error => {
                    if (error.response.status == 400) {
                        this.error_sms_code_message = error.response.data.message;
                        this.error_sms_code = true;
                    } else {
                        console.log(error.response.data);
                    }
                    this.sending_flag = false;
                })
        },
        // 注冊
        on_submit: function(){
            this.check_username();
            this.check_pwd();
            this.check_cpwd();
            this.check_phone();
            this.check_sms_code();
            this.check_allow();

        }
    }
});
           

CORS跨域請求

1)同源位址:

對于兩個不同的url位址,如果其協定、IP和PORT完全一緻,這樣的位址就叫同源位址,否則就叫非同源位址。

例如:

1. http://www.meiduo.site:8080/
  2. http://api.meiduo.site:8000/

           

2)跨域請求

當浏覽器發起跨域請求時,如果

源請求頁面位址

被請求位址

不是同源位址,那麼這個請求就是跨域請求。

(一)django項目架構搭建

3)CORS跨域請求限制:

隻針對ajax跨域請求

請求頭:Origin:請求源位址

響應頭:Access-Control-Allow-Origin: 源請求位址

4)跨域請求設定

安裝

pip install django-cors-headers
           

注冊應用

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)
           

注冊中間件

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]
           

添加跨域請求白名單

# CORS設定
CORS_ORIGIN_WHITELIST = (
    '127.0.0.1:8080',
    'www.meiduo.site:8080',
)
CORS_ALLOW_CREDENTIALS = True  # 允許攜帶cookie
           
  • 凡是出現在白名單中的域名,都可以通路後端接口
  • CORS_ALLOW_CREDENTIALS 指明在跨域通路中,後端是否支援對cookie的操作。

短信驗證碼(異步發送短信說明)

(一)django項目架構搭建

雲通訊什麼時候響應,用戶端才會響應

問題:擷取驗證碼如果網速慢的話,可能造成使用者長時間的等待。‘

解決(Celery異步發短信):

​ 1.将短信發送的代碼封裝成一個函數。

​ 2.建立一個新的程序或線程,調用發送短信的函數