從立項到商品

一.立項1.在碼雲上建立項目目錄
2.使用ssh方式把項目克隆下來
    1)生成密鑰對ssh-keygen -t rsa
    2)存放在家目錄下的 .ssh 目錄下
    3)原來有的話,把原來的刪除
    4)複製生成好的公鑰
    5)在碼雲上的ssh公鑰中添加
    6)ssh方式複製項目
    7)在本地目錄下git clone 項目克隆地址
3.建立分支
4.建立新的目錄存放前端資源
5.push到碼雲(git push origin dev:dev)
6.在碼雲上發起合併請求(pull Requests)

7.安裝靜態文件服務器live-server(node.js提供的前端工具,必須在node.js8以上的版本才能運行,和python無關)
    1)安裝node.js版本控制工具-->curl -o- https://raw.githubusercontent.co ... v0.33.11/install.sh | bash
    2)從新進入終端,下載最新的node.js-->nvm install node
    3)安裝live-server-->npm install -g live-server
    4)使用live-server-->在靜態文件目錄front_end_pc下執行:live-server
        
8.建立虛擬環境: mkvirtualenv -p python3 meiduo_mall
9.安裝工具包:
    django==1.11.11
    djangorestframework
    pymysql
10.建立後端工程目錄:django-admin startproject 工程名
    
11.調整工程目錄
    1)添加docs目錄(記錄文檔)
    2)logs目錄(記錄日誌)
    3)scripts目錄(放一些腳本文件的)
12.調整主要應用程序目錄
    1)添加apps包(應用模塊)
    2)settings包(配置文件)
    3)libs包(第三方庫)
    4)utils包(工具庫)
    5)在settings下建立dev(開發)時的配置文件,刪除主應用程序目錄的配置文件

13.apps下建立應用-->python ../../manage.py startapp users
二.配置1.配置文件中添加使用配置文件的模塊
    1)import sys
    2)sys.path.insert(0,os.path.join(BASE_DIR,"apps"))
    3)直接按照原來的方式添加app配置
    4)修改manage.py中使用到配置文件的地方

2.建立數據庫
3.爲數據庫建立新的帳戶專門操做美多商城數據庫
    create user meiduo identified by 'meiduo'; 
    grant all on meiduo_mall.* to 'meiduo'@'%'; 
    flush privileges;
    
    第一句:建立用戶帳號 meiduo, 密碼 meiduo (由identified by 指明)
    第二句:受權meiduo_mall數據庫下的全部表(meiduo_mall.*)的全部權限(all)給用戶meiduo在以任何ip訪問數據庫的時候('meiduo'@'%')
    第三句:刷新生效用戶權限
    
4.配置數據庫
    1)DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',  # 數據庫主機
        'PORT': 3306,  # 數據庫端口
        'USER': 'meiduo',  # 數據庫用戶名
        'PASSWORD': 'meiduo',  # 數據庫用戶密碼
        'NAME': 'meiduo_mall'  # 數據庫名字
    }
}
    2)記得在meiduo_mall/meiduo_mall/__init__.py(或者settings/__init__.py)文件中添加:
        import pymysql
        pymysql.install_as_MySQLdb()
        
5.安裝redis並配置(給admin站點使用的session)
    1)pip install django-redis
    2)配置文件中配置:
        CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://10.211.55.5:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "session": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://10.211.55.5:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"

6.配置本地化語言和時間
7.配置日誌:
    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': 'DEBUG',
            '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',  # 日誌器接收的最低日誌級別
        },
    }
}
    
8.異常處理(修改Django REST framework的默認異常處理方法(默認處理了序列化器拋出的異常),補充處理數據庫異常和Redis異常)
    1)utils下建立exceptions.py
    2)
    from rest_framework.views import exception_handler as drf_exception_handler
    import logging
    from django.db import DatabaseError
    from redis.exceptions import RedisError
    from rest_framework.response import Response
    from rest_framework import status

    # 獲取在配置文件中定義的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, exc))
                response = Response({'message': '服務器內部錯誤'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

        return response
        3)添加到配置文件:
            REST_FRAMEWORK = {
            # 異常處理
            'EXCEPTION_HANDLER': 'meiduo_mall.utils.exceptions.exception_handler',
        }
三.登陸註冊模塊1.使用django認證系統中的用戶模型類建立自定義的用戶模型類
    from django.contrib.auth.models import AbstractUser
    
    class User(AbstractUser):
    """用戶模型類"""
    mobile = models.CharField(max_length=11, unique=True, verbose_name='手機號')

        class Meta:
            db_table = 'tb_users'
            verbose_name = '用戶'
            verbose_name_plural = verbose_name
2.配置中指明自定義的用戶模型類
    
    
3.進行數據庫遷移(第一次數據庫遷移必須在配置了自定義的用戶模型類以後)
    python manage.py makemigrations
    python manage.py migrate
    
4.圖片驗證碼
    1)分析,接口設計
        1>接口設計思路:
            接口的請求方式,如GET 、POST 、PUT等
            接口的URL路徑定義
            須要前端傳遞的數據及數據格式(如路徑參數、查詢字符串、請求體表單、JSON等)
            返回給前端的數據及數據格式
        2>用戶註冊中,須要實現的接口:
            圖片驗證碼
            短信驗證碼
            用戶名判斷是否存在
            手機號判斷是否存在
            註冊保存用戶數據
    2)建立新的應用:verifications
    3)註冊到配置文件
    4)新建圖片驗證碼類視圖
        1>接收參數
        2>校驗參數
        3>生成驗證碼圖像
        4>保存真實值
        5>返回圖片
    5)把第三方生成驗證碼包放入libs下
    6)添加redis保存驗證碼的配置
    7)在verifications下建立constants存放常量
    8)添加url,而後在全局url中包含
    
    
5.本地域名映射
    1)vim /etc/hosts
    2)在文件中增長兩條信息
        1>後端:127.0.0.1   api.meiduo.site
        2>前端:127.0.0.1   www.meiduo.site
    3)在配置文件中爲ALLOWED_HOSTS添加可訪問的地址.ALLOWED_HOSTS = ['api.meiduo.site', '127.0.0.1', 'localhost', 'www.meiduo.site']
    4)在front_end_ps/js下新建host.js爲前端保存後端域名,添加內容:var host='http://api.meiduo.site:8000'
        
6.修改前端js代碼
    1)在register.html導入host.js
    2)修改js代碼
        1>建立生成uuid函數
        2>建立獲取圖片驗證碼函數
        3>添加變量存儲存儲uuid
        4>爲圖片驗證碼的src綁定一個變量,變量存儲的是圖片驗證碼的url(:src="")
        5>js中添加存儲圖片驗證碼url的變量
        6>添加全局的host變量
        7>拼接圖片驗證碼的url放入存儲圖片驗證碼的變量中
        8>在js中的vue.js頁面加載完就執行的方法中調用獲取圖片驗證碼函數
        9>在html爲圖片添加點擊事件,調用獲取圖片驗證碼的函數(@click="")

7.短信驗證碼後端邏輯
    1)接口設計
    2)新建短信驗證碼類視圖,GET請求,繼承GenericAPIView
        1>校驗參數(由序列化器完成)
        2>生成短信驗證碼
        3>保存短信驗證碼,發送記錄
        4>發送短信
        5>返回
    3)添加序列化器驗證參數
        1>驗證是否符合格式
        2>添加校驗方法
        3>獲取數據
        4>查詢真實的圖片驗證碼,查詢失敗拋出異常
        5>解碼圖片驗證碼,比較圖片驗證碼,錯誤的話raise serializer.ValidationError("")
        6>判斷手機號是否在60s內(獲取redis中的手機發送標誌,若是有數據,說明發送過了,拋出異常)
        7>返回字典數據
    4)生成短信驗證碼(隨機生成)
    5)保存短信驗證碼
    6)保存短信驗證碼的發送記錄
    7)發送短信
        1>導入雲通信到utils下
        2>建立發送短信驗證碼的對象
        3>設置短信的有效分鐘數:expires = 短信驗證碼有效時間秒//60
        4>傳參(send_template_sms(手機號,[驗證碼,短信驗證碼有效分鐘數],模板ID常量))
        5>爲發送短信驗證碼捕獲異常,寫入日誌,返回結果
    8)添加路由
    9)爲保存短信驗證碼補充redis管道
        redis_conn = get_redis_connection('verify_codes')
        pl = redis_conn.pipeline()
        pl.setex("sms_%s" % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        pl.setex("send_flag_%s" % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
        pl.execute()
    10)序列化器中刪除短信驗證碼(驗證碼輸錯或者輸對都刪除,即查詢出來就從redis中刪除)
        try:
            redis_conn.delete('img_%s' % image_code_id)
        except RedisError as e:
            logger.error(e)
     11)添加前端代碼
    
8.CORS解決跨域請求
    1)安裝擴展:
        pip install django-cors-headers
    2)配置文件中APPS添加'corsheaders'
    3)在中間件中添加中間件配置,放在最上面:'corsheaders.middleware.CorsMiddleware'
    4)添加白名單配置
        CORS_ORIGIN_WHITELIST = (
    '127.0.0.1:8080',
    'localhost:8080',
    'www.meiduo.site:8080',
    'api.meiduo.site:8000'
)
CORS_ALLOW_CREDENTIALS = True  # 容許攜帶cookie

9.建立celery包(在工程目錄下):celery_tasks異步任務
    1)建立main.py(celert的啓動模塊)和config.py
    2)爲短信驗證碼建立包(sms),在包下建立tasks.py(固定的名字)
    3)安裝Celery擴展
    4)在main.py下導入Celery並建立celery應用:app = Celery("meiduo")
    5)在config.py中添加配置:
        1>broker_url = "redis://127.0.0.1/14"(溝通的橋樑信息,存儲任務的隊列)
        2>result_backend = "redis://127.0.0.1/15"(保存任務運行完以後的返回結果信息,好比函數的返回值,任務id,狀態等)
    6)main下導入celery配置:app.config_from_object('celery_tasks.config')
    7)main下注冊任務:app.autodiscover_tasks(['celery_tasks.sms'])
    8)把發送短信驗證碼的代碼寫在sms下的tasks.py下
    9)在sms下建立utils包,把短信驗證碼工具包放進去
    10)修改原來發送短信驗證碼的代碼(一些用到的變量經過傳參傳入),把返回值去掉
    11)導入日誌,添加django的日誌配置文件.logger = logging.getLogger("django")
    12)在main.py中添加celery使用django配置的代碼:
        import os
        if not os.getenv('DJANGO_SETTINGS_MODULE'):
            os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_mall.settings.dev'
    13)sms下的tasks.py下使用裝飾器裝飾發送短信驗證碼的方法:
        1>導入app:from celery_tasks.main import app
        2>裝飾器裝飾:
            @app.task(name="send_sms_code")
            def send_sms_code(mobile, sms_code, expires,temp_id):
                ...
            使用的是應用名.task()的方式裝飾,其中裝飾器的參數name是爲了指明任務的名字,在運行時能夠知道調用了哪一個任務運行
            
    14)在須要使用到發送短信驗證碼的視圖函數中調用發送短信驗證碼的方法並傳參:
        1>導包:from celery_tasks.sms.tasks import send_sms_code
        2>expires = constants.SMS_CODE_REDIS_EXPIRES//60
        3>send_sms_code.delay(mobile, sms_code, expires,constants.SMS_CODE_TEMP_ID)
        4>return Response({'message':'OK'})
    15)啓動celery(在 異步任務所在的目錄下):celery -A celery_tasks.main worker  -l info
        -A表明一個應用,後面跟的是應用啓動的文件路徑
        -l info 表示顯示全部級別在info以上的worker日誌信息

10.判斷帳號
    1)判斷用戶是否存在
        1>users下定義類視圖
        2>獲取指定用戶名的數量
        3>返回數據(用戶名和數量)
        4>前端實現
    2)判斷手機號是否存在
        1>users下定義類視圖
        2>獲取指定的手機號的數量
        3>返回數據(手機號和數量)
        4>前端實現
11.用戶註冊
    1)在users應用下建立新的視圖類(繼承CreateAPIView)實現用戶註冊:(參數:username,password,password2,sms_code,mobile,allow)
        1>視圖函數中使用序列化器類(CreateUserSerializer)
    2)實現序列化器
        1>在users下新建serializers模塊實現序列化器
        2>導入rest_framework的serializers
        3>建立序列化器CreateUserSerializer,繼承serializers.ModelSerializer
        4>添加自定義字段(數據庫模型類沒有的字段)都設爲校驗時使用的字段(添加屬性:write_only=True)
        5>聲明序列化器類有哪些字段和映射哪一個模型類:
            class Meta:
                model = User
                fields = (全部須要顯示的字段名,包括模型類裏面的以及自定義的,此時校驗和返回都是這些字段)
        6>使用extra_kwargs對字段指明額外的需求.
        7>對特定的字段進行校驗(validate_字段名):包括mobile,allow.
        8>對多個字段同時進行校驗(validate):判斷兩次密碼和短信驗證碼
         9>重寫保存方法(create),增長密碼加密功能
            (1).移除數據庫模型類不存在的屬性(allow,sms_code,password2)
            (2).調用父類的保存方法
            (3).調用django的認證系統加密密碼,保存
            (4).返回user
    3)實現註冊的前端js

12.使用JWT機制判斷用戶身份
    1)安裝擴展:pip install djangorestframework-jwt
    2)在配置文件的REST_FRAMWORK中添加認證的配置:
        'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
            )
    3)在配置文件中添加JWT的配置token的有效期:
        JWT_AUTH={
            'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        }
    4)在註冊時的序列化建立了用戶以後簽發jwt的token(記錄登陸狀態):
        1>導包:from rest_framwork_jwt.settings import api_settings 
        2>簽發JWT:   
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
    5)添加返回的字段token而且把生成的token存到這個字段:好比user.token = token
    6)前端保存token:
        sessionStorage瀏覽器關閉即失效
        localStorage 長期有效
        1>在js中sessionStorage.clear()
        2>localStorage.clear()
        3>localStorage.token = response.data.token
        4>localStorage.user_id = response.data.user_id
        5>localStorage.username = response.data.name
    
13.登陸:
    在users下的urls下添加登陸簽發JWT的視圖
    1)導包:from rest_framework_jwt.views import obtain_jwt_token
    2)添加路由:url(r'^authorizations/$', obtain_jwt_token)
    3)在users/utils.py中建立jwt_response_payload_handler函數修改視圖到的返回值.
    4)在配置中的JWT_AUTH中添加配置:
        "JWT_RESPONSE_PAYLOAD_HANDLER":"users.utils.jwt_response_payload_handler"
    5)在users/utils.py下自定義一個類(UsernameMobileAuthBackend)繼承自ModelBackend,重寫django認證系統ModelBackend裏面的authenticate(self,request,username=None,password=None,**kwargs)方法自定義用戶名或者手機號認證(由於username多是用戶名,也多是手機號)
    6)抽出一個根據帳號來獲取用戶對象的方法(get_user_by_account(account))放在自定義類的外面
        1>正則帳號信息判斷是否爲手機號,根據手機號查詢數據庫得到用戶對象
        2>不然根據用戶名查詢數據庫得到用戶對象
        3>捕獲異常,由於是get查詢數據庫,不存在返回None,不然返回用戶對象
    7)在重寫的authenticate方法中調用獲取用戶對象的方法,傳參爲接收到的username,接收穫取到的用戶對象
    8)驗證用戶對象的密碼,認證成功後返回用戶對象:
        if user is not None and user.check_password(password):
            return user
    9)配置文件中添加認證方法的配置:
        AUTHENTICATION_BACKENDS=['users.utlis.UsernameMobileAuthBackend']
    10)修改前端代碼
   

14.QQ登陸:
    1)註冊成爲QQ互聯開發人員:
        http://wiki.connect.qq.com/%E6%8 ... 0%E5%8F%91%E8%80%85
    2)建立應用,獲取項目對應與QQ互聯的應用ID:
        http://wiki.connect.qq.com/__trashed-2
    3)根據QQ登陸開發文檔進行開發:文檔連接-->http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
    4)建立QQ登陸模型基類(放到全局的應用的utils下)
    5)建立一個新的應用:oauth
        1>添加到App配置中
    6)建立QQ登陸模型類(繼承自基類)(oauth/models.py下),而且進行數據庫遷移
    7)第一個接口:oauth中建立類視圖(繼承APIView,get方式)返回QQ登陸的網址:
        1>獲取next參數.若是沒有,返回主頁:next="/"
        2>拼接QQ登陸的網址(調用拼接QQ登陸網址的輔助工具類,傳入參數:OAuthQQ(state=next))
        3>返回(Response({'login_url':login_url}))
    8)oauth下建立utils.py,建立QQ認證輔助工具類(OAuthQQ)拼接QQ登陸網址
        1>定義方法拼接url(get_login_url)
        2>根據文檔查看url以及須要發送的參數(client_id,redirect_uri,state),參數設置成字典數據類型
        3>發送的參數在__init__下定義,由於每一個對象的參數值不同.建立對象時傳入.
        4>在配置文件中設置QQ登陸時的默認值,在不傳參時就使用默認值(由於有些值是同樣的,不須要改變,就可使用默認值.)
            QQ_CLIENT_ID = '101474184'
            QQ_CLIENT_SECRET = 'c6ce949e04e12ecc909ae6a8b09b637c'
            QQ_REDIRECT_URI = 'http://www.meiduo.site:8080/oauth_callback.html'
            QQ_STATE = '/'
        5>經過form django.conf import settings 使用django的配置文件中的內容
        6>使用urllib.parse 下的urlencode(字典名),將字典轉換爲url路徑中的查詢字符串,拼接到url後面(from urllib.parse import urlencode)
        7>返回url
    9)註冊路由&在總路由中包含,設置前綴(oauth)
    10)實現QQ登陸前端,在login.js中增長qq登陸的方法.請求成功獲取到返回的QQ登陸網址,跳轉到QQ登陸頁面
    11)前端處理QQ_REDIRECT_URI設置的oauth_callback.html
        1>建立oauth_callback.html
        2>js中建立oauth_callback.js
        
    12)第二個接口:(get)
        1>建立新的類視圖:QQAuthUserView(APIView),判斷QQ登陸的用戶
        2>獲取code,若是不存在,返回信息
        3>憑藉code獲取access_token:建立輔助工具類對象,調用get_access_token()方法,傳參數爲code
            (1).在QQ認證輔助工具類中添加得到access_token的方法get_access_token()
            (2).添加url,設置參數(根據QQ互聯文檔)
            (3).拼接url帶上參數
            (4).使用urllib.request.urlopen(url,data=None)發送請求,得到返回響應
            (5).讀取響應體數據:響應調用read()讀取數據,數據解碼decode()
            (6).解析響應體數據:urllib.parse.parse_qs(數據),解析出來是個字典
            (7).讀取access_token.(在異常拋出以後)在一個字典中獲取access_token,是一個列表,而後返回access_token[0]
            (8).輔助類中捕獲異常,記錄日誌,拋出異常:發送請求時開始捕獲,解析完成後記錄異常('獲取access_token異常:%s'%e),並拋出異常自定義的異常OAuthQQAPIError.若是沒有異常再進行讀取access_token.
            (9).自定義一個異常:
                a.oauth下建立新的模塊exception.py
                b.建立自定義的異常類OAuthQQAPIError,繼承自Exception.不實現,直接pass
            (10)在類視圖中調用get_access_token()時捕獲自定義的異常:OAuthQQAPIError
            (11).直接返回Response({'message': '訪問QQ接口異常'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
        4>憑藉access_token獲取openid
            (1).走到這一步說明access_token獲取成功,
            
            (2).調用QQ工具輔助類中的獲取openid的方法(get_openid),參數爲access_token
            (3).根據QQ互聯文檔編寫get_openid方法
            (4).和得到access的流程相似
            (5).獲得的數據不一樣,解析openid的方式不同
            (6).返回的數據爲: callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n;字符串
            (7).經過切片解析數據,轉json字符串爲字典,拋異常
        5>根據openid查詢數據庫OAuthQQuser,捕獲異常判斷數據是否存在
        6>若是數據不存在,處理openid並返回
            (1).調用QQ輔助類中定義生成access_token的方法(generate_save_user_token),參數爲openid,返回access_token.(返回的這個access_token封裝了openid)
            (2).輔助工具類中實現generate_save_user_token方法.
            (3).安裝itsdangerous,使用itsdangerous生成access_token
            (4).使用 itsdangerous模塊下的TimedJSONWebSignatureSerializer 生成帶有有效期的token,名字太長,能夠起別名Serializer
            (5)生成方式:定義序列化對象:serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
            (6).oauth下新建constants模塊,設置SAVE_QQ_USER_TOKEN_EXPIRES=10*60
            (7).調用對象的dumps方法傳入openid得到生成的token:token = serializer.dumps({'openid':openid})
            (8).返回解碼後的token:token.decode()
            (9).視圖函數中返回access_token用於綁定用戶的身份
        7>若是數據存在,表示用戶已經綁定過身份,簽發JWT token
        8>返回JWT_token數據(用戶id,用戶名,簽發的token)
        9>拼接路由
        10>修改前端oauth_callback.js中的mounted方法
            (1).獲取用戶id,若是有,說明已經綁定,請求state中的url
            (2).若是沒有,說明沒有綁定,得到服務器返回的access_token,調用生成圖片驗證碼的函數,顯示綁定用戶的表單.
    
    13)第三個接口:(綁定QQ身份)
        1>在QQAuthUserView中定義post方法
        2>獲取數據
        3>校驗數據
        4>判斷用戶是否存在,若是存在,綁定,建立OAuthQQUser數據
        5>若是不存在,先建立User,再建立OAuthQQUser
        6>簽發JWT token
        
        7>上面的這套流程能夠直接繼承CreateAPIView
        8>直接在視圖函數中調用序列化器OAuthQQUserSerializer
        9>添加序列化器模塊,定義序列化器OAuthQQUserSerializer
        10>實現OAuthQQUserSerializer:繼承serializer.ModelSerializer
            (1).添加字段sms_code只反序列化,access_token只反序列化,token只序列化.抽出mobile字段(可序列化,可反序列化),由於模型類中的mobile設置了只能是惟一,這裏須要抽出來,把只能是惟一的驗證去掉,不抽出來在模型類中會拋出異常,那麼後面的判斷手機號是否在數據庫中就沒有辦法判斷了.
            (2).Meta中指定模型類,指定顯示字段fields = ('mobile','password','sms_code','access_token','id','username','token')
            (3).爲字段添加額外的條件:extra_kwargs={
                'username':{
                    'read_only':True
                },
                'password':{
                    'write_only':True,
                    'min_length':8,
                    'max_length':20,
                    'error_messages':{
                        'min_length':'僅容許8-20個字符的密碼',
                        'max_length':'僅容許8-20個字符的密碼'
                    }
                }
            }
            (4).定義校驗數據的方法validate.
            (5).獲取access_token
            (6).校驗access_token,在QQ輔助工具類中添加校驗access_token的靜態方法,使用的是itsdangerous模塊,由於生成也是這個模塊.返回校驗後的openid
            (7).validate中賦值openid:attrs['openid'] = openid
            (8).校驗短信驗證碼,失敗拋出異常
            (9).根據mobile獲取user,捕獲異常,不存在拋出異常,pass操做,沒有異常則獲取密碼,驗證碼密碼,調用user的密碼認證系統:user.check_password(password),若是沒有返回值,拋出驗證錯誤異常,提示密碼錯誤.
            (10)密碼驗證經過則把user添加到序列化器的數據字典中:attrs['user'] = user
            (11).返回attrs
            (12).修改序列化器的create方法.
            (13).獲取user,openid,mobile,password
            (14).若是user不存在,在數據庫中建立這個user,用戶名爲手機號,手機號也是手機號,密碼爲前端發送過來校驗後的密碼.調用的django認證系統的建立用戶的方法create_user()
            (15).綁定,建立OAuthQQUser數據:OAuthQQUser.objects.create(user=user,openid=openid)
            (16).簽發JWT token,和登陸裏面的同樣的.
            (17).返回user
        11>修改oauth_callback.js添加點擊submit時的方法
四.用戶我的中心模塊1.我的基本信息:
    1)在User模型類中添加郵箱是否激活的字段
    2)數據庫遷移
    3)users下建立新的視圖類,查詢用戶基本信息(GET /user/):UserDetailView,繼承自RetrieveAPIView
    4)調用序列化器類
    5)users下的序列化器模塊下新建序列化器,用來設置返回哪些數據
    6)重寫數據源的方法:get_object()不重寫時返回的是query_set的查詢集,返回當前請求的用戶.return self.request.user
    7)指明必須經過登陸認證後才能訪問
    8)前端實現user_center_info.html和user_center_info.js
    
    9)實現保存郵箱後端接口(PUT /email/)
        1>新建EmailView視圖,繼承UpdateAPIView
        2>指明序列化器類
        3>重寫get_object(self)方法得到當前的用戶對象
        4>設置權限爲認證經過的用戶
        5>實現序列化器(EmailSerializer),繼承ModelSerializer
        6>指定模型類和顯示的字段
    10)發送郵件到郵箱進行驗證
        1>註冊本身的郵箱
        2>django中添加配置,設置郵箱的配置信息
            (1).指明使用什麼樣的郵箱客戶端,這裏使用django自帶的:EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
            (2).發送的smtp郵箱地址,這裏使用163:EMAIL_HOST = 'smtp.163.com'
            (3).端口號:固定爲25,EMAIL_PORT = 25
            (4).發送郵件的郵箱:EMAIL_HOST_USER = 'itcast88@163.com'
            (5).設置發送郵件的郵箱客戶端受權碼,沒有受權碼就爲郵箱的密碼:EMAIL_HOST_PASSWORD = 'python808'
            (6).設置收件人看到的發件人:EMAIL_FROM = 'python<itcast88@163.com>'
        3>使用django提供的django.core.mail模塊的send_mail()方法發送郵件:例如send_mail(subject, message, from_email, recipient_list,html_message=None)
            subject 郵件標題
            message 普通郵件正文, 普通字符串
            from_email 發件人
            recipient_list 收件人列表
            html_message 多媒體郵件正文,能夠是html字符串
        4>定義發送郵件的異步任務,實現發送郵件
        5>在郵件的序列化器中重寫update方法:
            (1).取出驗證後的email,而後賦值給當前對象的email,當前對象調用save()方法保存郵箱地址
            (2).在user模型類中補充一個額外的方法,生成驗證郵箱的url.使用itsdangerous模塊下的dumps方法,參數爲user_id和email,返回的是生成後的驗證郵箱URL+token
            (3).序列化器中url調用生成驗證郵箱url的方法
            (4).調用異步任務中的發送驗證信息到用戶郵箱的方法,傳入參數
            (5)返回當前用戶
        6>在urls.py中添加郵箱的類視圖
    11)在前端user_center_info.js中添加保存email的方法
    12)郵箱驗證先後端實現
        1>添加郵箱驗證成功返回的前端html頁面:success_verify_email.html
        2>修改success_verify_email.html的js
        3>後端實現接口:GET /emails/verification/?token=xxx
        4>VerifyEmailView(郵箱驗證類視圖),繼承自APIView
        5>get方法下:
            (1).獲取參數token
            (2).驗證token是否存在,不存在返回信息,缺乏token,狀態碼爲400
            (3).token存在,驗證token
            (4).在User模型類中新建校驗token的靜態方法:check_verify_eamil_token,接收參數token,使用的是itsdangerous模塊中的loads(),參數爲token,須要捕獲異常.出現異常返回None,沒有出現異常,獲取數據:user_id和email
            (5).經過user_id和email查詢User表,捕獲異常,發送異常返回NOne,不然返回user
            (6).視圖函數中調用校驗token的方法,得到user對象.
            (7).若是user爲None,返回message:連接信息無效,狀態碼爲400
            (8).user存在,把user.email_active設爲True,使用save()提交到數據庫
            (9).返回message:OK
            (10).註冊路由
            (11).前端success_verify_email.html的js中使用location.serch拿到url中?以及?後面的全部內容,拼接到請求的地址中

2.收貨地址
    1)新增收貨地址
        1>新建應用:areas
        2>配置文件中註冊app
        3>models新建Area模型類
            (1).name
            (2).parent = models.Foreignkey('self',on_delete=models.SET_NULL,related_name='subs',null=True,blank=True)
            (3).定義__str__方法,返回name
        4>執行數據庫遷移命令
        5>補充測試數據:
            (1).把測試數據areas.sql放入scripts目錄下
            (2).新建腳本命令的文件:import_area_data_to_db.sh
            (3).輸入命令:mysql -uroot -pmysql meiduo_mall <./areas.sql
            (4).在首行添加運行環境:#!/bin/bash
            (5).爲import_area_data_to_db.sh添加可執行權限:chmod +x import_area_data_to_db.sh
            (6).執行腳本文件:./import_area_data_to_db.sh
        6> areas下建立新的類視圖AreasViewSet實現接口,繼承自ReadOnlyModelViewSet
            (1).重寫get_queryset方法:
                a.若是action=='list',返回過濾後的數據(parent=None)
                b.不然返回全部area數據對象
            (2).建立序列化器模塊,定義序列化器.
            (3).建立AreaSerializer序列化器類,繼承自ModelSerializer.設置模型類爲Area,顯示的字段爲id和name
            (4).建立SubAreaSerializer序列化器類,繼承自Model.Serializer.設置模型類爲Area,顯示字段爲id,name,subs,subs交給AreaSerializer(many=True,read_only=True)處理
            (5).重寫get_serializer_class方法,選擇對應的序列化器:
                a.若是action=='list',返回AreaSerializer序列化器
                b.不然返回SubAreaSerializer
        7>在urls中使用DefaultRouter註冊視圖:
            (1).router = DefaultRouter()
            (2).router.register('areas',views.AreasViewSet,base_name='areas')
            (3).urlpatterns+=router.urls
        8>在總路由中包含分路由
        9>修改前端代碼
    2)使用redis緩存地址信息(由於地址信息除了查詢沒有其餘的操做)
        1>安裝擴展:pip install drf-extensions
        2>類視圖繼承CacheResponseMixin
        3>在配置文件中進行全局的緩存配置: 
            # DRF擴展
            REST_FRAMEWORK_EXTENSIONS = {
                # 緩存時間
                'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60,
                # 緩存存儲
                'DEFAULT_USE_CACHE': 'default',
            }
    3)關閉繼承的視圖中list方法的分頁:配置類屬性-->pagination_class=None
    4)實現前端代碼:user_center_site.html和user_center_site.js
        
    5)用戶地址管理
        1>在users的models模塊新建用戶地址模型類,繼承自BaseModel:
            class Address(BaseModel):
                """
                用戶地址
                """
                user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='addresses', verbose_name='用戶')
                title = models.CharField(max_length=20, verbose_name='地址名稱')
                receiver = models.CharField(max_length=20, verbose_name='收貨人')
                province = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='province_addresses', verbose_name='省')
                city = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='city_addresses', verbose_name='市')
                district = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='district_addresses', verbose_name='區')
                place = models.CharField(max_length=50, verbose_name='地址')
                mobile = models.CharField(max_length=11, verbose_name='手機')
                tel = models.CharField(max_length=20, null=True, blank=True, default='', verbose_name='固定電話')
                email = models.CharField(max_length=30, null=True, blank=True, default='', verbose_name='電子郵箱')
                is_deleted = models.BooleanField(default=False, verbose_name='邏輯刪除')

                class Meta:
                    db_table = 'tb_address'
                    verbose_name = '用戶地址'
                    verbose_name_plural = verbose_name
                    ordering = ['-update_time']  # 指明查詢是複數時的默認排序
            
        2>爲User模型類添加默認地址:
            default_address = models.ForeignKey('Address', related_name='users', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='默認地址')
        3>數據庫遷移
        4>users中添加新的類視圖,繼承自增長和更新擴展類以及GenericAPIView
            (1).增長:重寫create方法,保存用戶地址數據,保存以前檢查用戶地址數目,若是超出上限,返回響應信息,若是沒有,返回父類CreateModelMixin的create方法
            (2).更新:重寫get_queryset方法,返回更新使用的數據集爲當前用戶的地址中沒有被刪除的數據.更新使用的是UpdateModelMixin.查詢集在這裏指明
            (3).查詢:
                a.本身實現list方法
                b.指明queryset爲當前對象的get_queryset()
                c.指明序列化器爲全局使用的序列化器:serializer=self.get_serializer(queryset,many=True)
                d.得到當前用戶對象:user = self.request.user
                e.返回響應,參數爲字典,包含用戶id,用戶的默認地址id,地址的限制條數,用戶全部的地址信息(serializer.data)
            (4).刪除:因爲擴展類中是在真的刪除,因此這裏不使用.這裏本身實現destroy方法來處理刪除.
                a.獲取刪除的地址對象:address=self.get_object()
                b.進行邏輯刪除(修改字段的值爲True)
                c.調用save()方法保存
                d.直接只返回狀態碼:204
            (5).設置默認地址狀態:(只須要pk,經過pk在用戶的地址列表中找到當前的地址)
                a.裝飾器裝飾方法status(),須要添加參數pk=None.以PUT方式進行訪問,單一資源(detail=True)
                b.獲取當前請求的地址對象.get_object()使用Pk在queryset中獲取
                c.設置當前請求的用戶對象的默認地址爲當前請求的地址對象
                d.調用save()方法保存
                e.返回響應信息Ok,狀態碼爲200
            (6).設置title:須要請求體的參數title
                a.和設置默認地址採用一樣的方法.使用action裝飾器
                b.獲取當前地址對象.get_object()使用Pk在queryset中獲取
                c.調用修改地址標題的序列化器,傳入參數爲當前地址對象,數據爲請求體中的參數
        5>serializer模塊新建用戶地址序列化器:
            (1).添加字段省市區的返回字符串以及接收和返回的省市區的id
            (2).指定模型類,和排除不須要顯示的字段:exclude=('user','id_deleted','create_time','update_time')
            (3).驗證手機號
            (4).重寫序列化器保存數據的方法,保存數據時把user加上:從序列化器的參數context中的request中取出user賦值給驗證後的數據字典.返回父類的create方法,參數爲驗證後的數據字典 
            (5).添加修改標題的序列化器,繼承自ModelSerializer,指明模型類爲Address,顯示字段爲title
        6>在users/urls.py中添加路由,使用DefaultRouter()
            (1).router = DefaultRouter()
            (2).router.register(r'addresses',views.AddressViewSet,base_name='addresses')
            (3).urlpatterns += router.urls
        7>修改前端user_center_site.js
       
五.商品模塊1.設計首頁廣告數據表結構
    1)廣告類別表包含三個字段:廣告類別id,廣告類別的名稱:name,廣告類別識別鍵名:key
    2)廣告內容表包含八個字段:廣告的id,廣告類別的id(外鍵):category,廣告的標題:title,廣告的內容頁面連接地址:url,廣告圖片的地址:image,廣告的文本內容:text,同類別廣告內的順序:sequence,是否顯示:status
2.設計商品數據表結構
    1)商品類別表
        1>id(類別id):INTEGER
        2>name(類別名稱):VARCHAR(10)
        3>parent(父類別):FK,INTEGER
    2)商品頻道表(一級類別)
        1>id(頻道id):INTEGER
        2>group_id(所屬組):INTEGER
        3>category_id(類別id):FK,INTEGER
        4>url(頻道頁連接地址):VARCHAR
        5>sequence(同組內的順序):INTEGER
    3)商品SKU表
        1>id(sku id):INTEGER
        2>name(sku 名稱):VARCHAR
        3>caption(副標題):VARCHAR
        4>goods_id(商品id):FK,INTEGER
        5>category_id(三級類別id):FK,INTEGER
        6>price(單價):DECIMAL
        7>cost_price(進價):DECIMAL
        8>market_price(市場價格):DECIMAL
        9>stock(庫存):INTEGER
        10>sales(銷量):INTEGER
        11>comments(評論量):INTEGER
        12>is_launched(是否上架):BOOLEAN
        13>default_image_url(默認圖片連接地址):VARCHAR
    4)商品(SPU)表
        1>id(商品id):INTEGER
        2>name(商品名稱):VARCHAR
        3>brand_id(品牌id):FK,INTEGER
        4>category1_id(一級類別id):FK,INTEGER
        5>category2_id(二級類別id):FK,INTEGER
        6>category3_id(三級類別id):FK,INTEGER
        7>sales(銷量):INTEGER
        8>comments(評論量):INTEGER
        9>desc_detail(詳細介紹):VARCHAR
        10>desc_pack(包裝信息):VARCHAR
        11>desc_service(售後服務):VARCHAR
    5)品牌表(brand)
        1>id(品牌id):INTEGER
        2>name(品牌名稱):VARCHAR
        3>logo(logo圖片連接):VARCHAR
        4>first_letter(品牌首字母):CHAR(1)
    6)SKU圖片表
        1>id(圖片id):INTEGER
        2>sku_id(所屬sku id):FK,INTEGER
        3>image(圖片連接地址):VARCHAR
    7)商品規格表(goods_specification)
        1>id(規格id):INTEGER
        2>goods_id(商品id):FK,INTEGER
        3>name(規格名稱):VARCHAR
    8)規格選項表(specification_option)
        1>id(選項id):INTEGER
        2>spec_id(規格id):FK,INTEGER
        3>value(選項值):VARCHAR
    9)規格信息表(sku_specification)
        1>id(規格信息id):INTEGER
        2>sku_id(所屬sku id):FK,INTEGER
        3>spec_id(規格id):FK,INTEGER
        4>option_id(規格選項id)FK,INTEGER

3.建立廣告應用(contents)
    1)註冊到apps中
    2)模型類中根據設計的數據庫表添加首頁廣告數據模型類(繼承自BaseModel)
4.建立商品應用(goods)
    1)新建goods應用
    2)添加到配置的apps中
    3)模型類中根據設計的數據庫表添加商品數據模型類(繼承自BaseModel)
5.數據庫遷移

6.使用FastDFS(快速分佈式文件系統)分佈式文件系統保存圖片
    1)使用Docker安裝FastDFS
        1>安裝Docker
            (1)更新apt源:sudo apt-get update
            (2)安裝依賴的工具:
            sudo apt-get install \
            apt-transport-https \
            ca-certificates \
            curl \
            software-properties-common
            (3)添加Docker官方GPG key:curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
            (4)設置Docker穩定版倉庫:
            sudo add-apt-repository \
            "deb [arch=amd64] https://download.docker.com/linux/ubuntu \            $(lsb_release -cs) \            stable"            (5)添加倉庫後,更新apt源索引:sudo apt-get update            (6)安裝最新版Docker CE(社區版):sudo apt-get install docker-ce            (7)檢查Docker CE是否安裝正確:sudo docker run hello-world            (8)爲了不每次命令都輸入sudo,設置用戶權限:sudo usermod -a -G docker $USER            (9)註銷從新登陸            (10)啓動與中止Docker:                # 啓動docker                sudo service docker start​                # 中止docker                sudo service docker stop​                # 重啓docker                sudo service docker restart                                    2>獲取FastDFS的鏡像            (1)使用提供的鏡像備份文件:docker load -i 文件路徑/fastdfs_docker.tar(或者鏡像源:docker image pull delron/fastdfs)            (2)運行tracker:docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker               (3)運行storage:docker run -dti --network=host --name storage -e TRACKER_SERVER=10.211.55.5:22122 -v /var/fdfs/storage:/var/fdfs delron/fastdfs storage        3>pyhton中使用FastDFS客戶端            (1)下載fastDFS開源包,並放到項目的scripts目錄下            (2)安裝:                pip install                     fdfs_client-py-master.zip                pip install mutagen                pip isntall requests            (3)在utils下新建fastdfs包,把配置文件放入包下,修改配置文件的log位置以及tracker_server的地址               2)自定義django文件存儲系統        1>在全局的utils下的fastdfs包中新建fdfs_storage模塊,導包:            from django.conf import settings            from django.core.files.storage import Storage            from django.utils.deconstruct import deconstructible            from fdfs_client.client import Fdfs_client        2>新建FastDFSStorage()類,繼承自Storage,使用deconstructible裝飾        3>初始化時傳參,加上默認值        4>實現_open()方法直接pass和_save()方法.        5>重寫_save()方法.參數name(傳入的文件名),content(文件對象),返回保存到數據庫中的FastDFS的文件名.        6>重寫exists()方法,參數爲name,在調用_save()方法以前調用,判斷文件是否存在文件系統中.直接pass.        7>重寫url()方法,參數爲name.返回值爲配置的url+name.在調用數據庫的image.url時就能夠取到這個值.        8>配置文件中配置django的文件存儲爲自定義的FastDFSStorage類:            # django文件存儲            DEFAULT_FILE_STORAGE = 'meiduo_mall.utils.fastdfs.fdfs_storage.FastDFSStorage'        9>配置文件中配置FastDFS的url和配置文件:            # FastDFS            FDFS_URL = 'http://image.meiduo.site:8888/'              FDFS_CLIENT_CONF = os.path.join(BASE_DIR, 'utils/fastdfs/client.conf')        10>在/etc/hosts中添加方法FastDFS storage服務器的域名:127.0.0.1 image.meiduo.site            7.CKEditor富文本編輯器    1)安裝:pip install django-ckeditor    2)配置中註冊應用:            'ckeditor',  # 富文本編輯器            'ckeditor_uploader',  # 富文本編輯器上傳圖片模塊    3)配置文件中添加CKEditor設置:        # 富文本編輯器ckeditor配置        CKEDITOR_CONFIGS = {            'default': {                'toolbar': 'full',  # 工具條功能                'height': 300,  # 編輯器高度                # 'width': 300,  # 編輯器寬            },        }        CKEDITOR_UPLOAD_PATH = ''  # 上傳圖片保存路徑,使用了FastDFS,因此此處設爲''    4)在總路由中添加ckeditor路由:        url(r'^ckeditor/', include('ckeditor_uploader.urls')),    5)修改模型類SPU中的desc_detail,desc_pack,desc_service,]        desc_detail = RichTextUploadingField(default='', verbose_name='詳細介紹')        desc_pack = RichTextField(default='', verbose_name='包裝信息')        desc_service = RichTextUploadingField(default='', verbose_name='售後服務')    6)數據庫遷移    7)修改bug:經過Django上傳的圖片保存到了FastDFS中,而保存在FastDFS中的文件名沒有後綴名,ckeditor在處理上傳後的文件名按照有後綴名來處理,因此會出現bug錯誤.        修改方法:            1>找到虛擬環境目錄中的ckeditor_uploader/views.py            2>在第95行增長一行代碼進行判斷,切割完以後長度是否大於1,大於1時再進行判斷後綴.          8)添加測試數據:        1>在contents應用和goods應用的admin中註冊模型類:            from . import models            admin.site.register(models.ContentCategory)            ...        2>添加FastDFS保存的測試圖片數據            (1).先把以前storage配置中被映射的宿主目錄將/var/fdfs/storage中的data目錄刪除,再把測試數據壓縮包解壓進去        3>添加對應的數據庫測試數據,把數據放入scripts目錄下,使用腳本文件導入        4>建立超級管理員用戶:            (1).python manage.py createsuperuser            (2).name:admin            (3).password:admin123                8.頁面靜態化:使用模板,把數據讀取出來渲染到模板頁面(設置定時任務,好比每五分鐘生成一次,或者設置修改數據的時候生成),生成的頁面存到固定的地方(靜態文件目錄),前端訪問時就從存儲頁面的地方加載便可.)能夠不用頻繁的操做數據庫,提升加載頁面的速度.     1)配置文件中添加保存靜態文件的目錄:            # 生成的靜態html文件保存目錄            GENERATED_STATIC_HTML_FILES_DIR = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR)), 'front_end_pc')    2)在全局Meiduo_mall下新建templates模板目錄    3)在配置文件中的爲模板配置中的DIRS配置templates的路徑:'DIRS': [os.path.join(BASE_DIR, 'templates')],        5)首頁靜態化        1>在contents應用下新建crons模塊,定義一個函數(generate_static_index_html()),生成靜態的主頁html文件        2>設置靜態文件的保存目錄,使用配置文件中配置的:file_path = os.path.jion(settings.GENERATED_STATIC_HTML_FILES_DIR,'index.html')        3>在templates下新建index.html的模板文件        4>在函數中把生成好的html文件保存到配置的路徑,'w'模式打開,設置編碼爲encoding='utf-8'.把生成的靜態html字符串寫入到文件.        5>新建index.js        6>在index.js中聲明Vue使用的模板變量語法,在el後面聲明:delimiters: ['[[', ']]']​    6)定時任務實現(操做系統完成的):        1>安裝擴展:pip install django-crontab        2>在配置文件中註冊:django-crontab  # 定時任務        3>配置文件中設置定時任務列表:            CRONJOBS=[                # 每5分鐘執行一次生成主頁靜態文件                ('*/5****','contents.crons.generate_static_index_html','>>'+os.path.join(os.path.dirname(BASE_DIR),"logs/crontab.log")),            ]        4>添加定時任務到操做系統:python manage.py crontab add            (顯示已經激活的定時任務:python manage.py crontab show            移除定時任務:python manage.py crontab remove)        5>配置文件中配置解決crontab中文問題:CRONTAB_COMMAND_PREFIX = 'LANG_ALL=zh_cn.UTF-8'           7)商品詳情頁靜態化        1>異步任務包新建html異步任務包        2>提出商品分類,放到商品的utils下        3>新建異步任務函數,生成靜態商品詳情頁面(查詢數據庫)        4>main中添加異步任務        5>goods中admin.py中新建自定義的admin管理類        6>重寫admin管理類中修改時就觸發定時任務從新生成html靜態頁面的模型類中的save_model(self,request,obj,from,change)和delete_model(request,obj),在其中添加異步任務.何時使用何時導入.        7>在sku圖片的自定義amdin管理類中設置默認圖片.        8>修改detail.html放到模板文件夾        9>修改新頁面的detail.js        10>在前端文件夾中新建goods文件夾用來存放生成好的商品詳情頁html文件    8)爲了方便開發,隨時生成靜態頁面,在scripts中新建生成頁面靜態化的python腳本文件(regenerate_static_index_html.py)        1>添加django的配置文件        2>初始化django:(django運行腳本的時候配置)            import django            django.setup()        3>導入生成靜態html頁面的函數        4>在函數運行時調用函數(__main__中)        5>在最上面添加導包路徑(sys能夠添加相對路徑,通常在腳本中使用相對路徑,由於前提是知道了腳本文件在哪運行):            import sys            sys.path.insert(0,'../')        6>還可爲文件指明運行的shell:首行添加#!/user/bin/env python,再爲文件添加可執行權限        7>新建生成詳情頁的腳本文件(regenerate_static_detail_html.py)        8>配置,把異步任務中的生成商品詳情頁的代碼複製過來        9>在__main__中查詢全部的SKU,循環遍歷,調用生成商品詳情頁的方法,參數爲每一個對象的id        9.用戶瀏覽歷史記錄(把用戶瀏覽的sku_id(這裏設置爲五個)保存到redis裏面),採用list的類型    1)配置文件中配置history的redis.使用3號庫,由於這裏只有一臺電腦,真實狀況是配置另外一臺電腦上的redis    2)用戶視圖下添加接口(POSt /browse_histories/)        1>新建用戶瀏覽歷史記錄類視圖(UserBrowsingHistoryView()),繼承自CreateAPIView        2>指定序列化器,指定權限        3>實現序列化器(AddUserBrowsingHistorySerializer),繼承自Serializer        4>定義字段:sku_id = serialziers.IntegerField(label="商品編號",min_value=1)        5>實現校驗字段的方法,檢驗sku_id是否存在.        6>重寫create方法:            (1)得到校驗後的sku_id            (2)得到當前用戶的user:                self.context['request'].user            (3)連接redis            (4)使用管道pipeline()            (5)設置redis_key:'history_%s',%user.id            (6)去重:lrem(redis_key,0,sku_id)            (7)保存,增長:lpush(redis_key,sku_id)            (8)截斷:ltrim(redis_key,0,用戶瀏覽歷史記錄常量-1)            (9)提交到redis            (10)返回驗證後的數據    3)實現接口(GET /browse_histories):        1>在UserBrowsingHistoryView視圖中實現get方法        2>得到user_id:user_id=request.user.id        3>連接redis        4>查詢redis,得到redis列表:lrange('history_%s'%user_id,0,自定義的用戶歷史記錄常量)        5>查詢數據庫,遍歷的方式:            for sku_id in sku_id_list:                sku = SKU.objects.get(id=sku_id)                skus.append(sku)        6>實現序列化器,繼承ModelSerializer        7>指定模型類爲SKU,指明顯示的字段(接口文檔上的五個字段)        8>視圖中調用序列化器,傳入參數爲skus和many=True        9>返回響應,參數爲序列化器對象的.data        10>修改前端的detail.js和user_center_info.html以及user_center_info.js        11>添加路由​10.商品列表頁    1)獲取商品列表數據        1>後端接口設計: GET /categories/(?P<category_id>\d+)/skus?page=xxx&page_size=xxx&ordering=xxx        2>請求參數:路徑參數+查詢字符串參數        3>返回數據:JSON        4>商品應用下視圖模塊建立類視圖(SKUListView()),繼承自ListAPIView        5>指明序列化器SKUSerializer:            (1).新建序列化器            (2).繼承自ModelSerializer            (3).指明模型類爲SKU            (4).指明顯示的字段(根據接口文檔)        6>指明查詢數據集,經過重寫get_queryset,根據category_id,和is_launchend過濾查詢SKU,把查詢結果返回        7>排序:            filter_backends=[OrderingFilter]            ordering_fields=("create_time","peice","sale")        8>分頁:(進行分頁查詢時DRF默認返回了count,next,previous,results字段),ListModelMixin的list方法會對數據進行過濾和分頁            (1).在全局的utils中本身實現分頁的設置(StandarResultsSetPagination),繼承自PageNumberPagination            (2).設置默認每頁條數:page_size=2,前端訪問指明每頁數量的參數名:page_size_query_param="page_size",限制前端指明每頁數量的最大限制:max_page_size=20            (3).全局配置中設置分頁爲自定義的分頁器        9>添加路由,全局包含路由        10>修改前端list.html        11>添加list.js​    2)商品搜索(使用Elasticsearch搜索引擎)        1>使用Docker安裝Elasticsearch            (1).獲取鏡像:docker image pull delron/elasticsearch-ik:2.4.6-1.0或者使用源碼包docker load -i elasticsearch-ik-2.4.6_docker.tar            (2).把配置文件目錄複製到elasticsearch-2.4.6下            (3).修改elasticsearch的配置文件 elasticsearc-2.4.6/config/elasticsearch.yml第54行,更改ip地址爲本機ip地址:network.host: 10.211.55.5                        (4).建立docker容器運行:docker run -dti --network=host --name=elasticsearch -v /home/python/elasticsearch-2.4.6/config:/usr/share/elasticsearch/config delron/elasticsearch-ik:2.4.6-1.0                      2>使用haystack對接Elasticsearch:            (1).安裝django中支持drf的擴展類:                    pip install drf-haystack                    pip install elasticsearch==2.4.1                            (2).配置文件中註冊應用:haystack            (3).配置haystack使用的搜索引擎後端:                # Haystack                HAYSTACK_CONNECTIONS = {                    'default': {                        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',                            'URL': 'http://10.211.55.5:9200/',  # 此處爲elasticsearch運行的服務器ip地址,端口號固定爲9200                            'INDEX_NAME': 'meiduo',  # 指定elasticsearch創建的索引庫的名稱    },}​                # 當添加、修改、刪除數據時,自動生成索引                HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'                        (4).在goods應用中新建search_indexes.py文件,用於存放索引類(繼承自indexes.SearchIndex,indexes.Indexable)            (5).在templates目錄下建立text字段使用的模板文件:templates/search/indexes/goods/sku_text.txt            (6).在索引類中實現get_model方法,返回值爲對應的模型類類名            (7).模板文件中使用django的模板語法指明創建索引的字段,當經過text字段傳參時索引查詢的字段(跨字段查詢):{{object.id}}{{object.name}}{{object.caption}}            (8).索引類中新建其餘字段,能夠經過單獨的字段傳參查詢            (9).索引類中實現index_queryset方法,指明返回要創建索引的數據查詢集,返回的是self.get_model().objects.filter(is_launched=True)            (10).手動生成初始索引:python manage.py rebuild_index            (11).在goods/views.py中建立視圖SKUSearchViewSet,繼承自HaystackViewSet.指明index_models=[SKU],serializer_class=SKUIndexSerializer             (12).在goods/serializers.py中建立haystack序列化器SKUIndexSerializer,繼承自HaystackSerializer.指明索引類和顯示的字段:index_classes=[SKUIndex],fields= ('text', 'id', 'name', 'price', 'default_image_url', 'comments')            (13).註冊路由            (14).測試數據            (15).前端實現,新建search.html和search.js
相關文章
相關標籤/搜索