Django項目紀要

開發流程html

公司高層 項目立項
|
市場部門 需求分析-->需求分析說明書, 需求規格說明書
|
產品部門 產品原型-->產品 UI 前端 後端 測試 移動端
|
|---------------|
研發人員 前端
架構設計 UI
數據庫設計 前端代碼編寫
代碼模塊實現和測試 |
|------------------------|
|
網站代碼整合
|
繼承測試
|
網站發佈前端


架構設計
分析可能用到的技術點
先後端是否分離
前端使用那些狂降-->vue.js
後端使用哪些框架-->Django REST Framework
選擇什麼數據庫-->mysql
如何實現緩存-->redis+JWT(token本地存儲)
是否搭建分佈式服務
如何管理源代碼vue

數據庫設計
數據庫-表的設計
根據項目需求設計合適的數據庫表
若前期數據庫表設計的不合理 後期隨着需求增長將變的難以維護python

集成測試
留意測試反饋平臺的bug報告mysql


----------------------------------------------------------------------------------------------------
建立倉庫ios

配置我的信息 git config user.email/namenginx

添加前端文件 git add 文件git

提交到本地倉庫 git commit -m"註釋"ajax

提交到遠程倉庫 git pushredis


安裝在文件夾中 npm install -g live-server 在靜態文件中執行live-server
至關於前端文件的服務器 將前端文件獨立運行 看成真正的客戶端訪問後端程序


在文件夾中建立項目
在項目文件夾中創子應用包 apps
在apps中建立子應用 python ../manage.py startapp users(註冊登錄) verifications(短信驗證) oauth areas goods contains cart order pay
建立libs 包 -->i第三方庫
建立utils包-->公共的類類
建立script包-->腳本文件


配置 --->看課件

建立數據庫管理員-->即不使用root帳戶
create user 帳戶名 identified by '密碼';建立用戶名和密碼
grant all on 數據庫名.* to '帳戶名'@'%';帳戶在任何ip下訪問數據庫的時候有全部表的全部權限
flush privileges;刷新生效

用戶模型類 使用django 的認證系統 繼承
django 的認證系統包括
用戶
權限 (二元 標誌一個用戶是否能夠作一個特定的任務)
組 (對多個用戶運用標籤和權限的一種通用方式)
一個可配置的密碼哈希系統
用戶登陸或內容顯示顯示的表單和視圖
一個可插拔的後臺系統

Django默認提供認證系統中,用戶的認證機制依賴於Session機制 ,本項目中使用的是JsonWebToken認證機制
將用戶的身份憑證數據存放在Token中 而後再Django中認證系統 以此實現
用戶的數據模型
用戶密碼的加密於驗證
用戶的權限系統


Djagno中提供的用戶模型類User用於保存用戶的數據 有如下默認字段
username first_name last_name email password(哈希和元數據) groups(與Group之間的多對多關係)
user_permissions(與Permission之間的多對多關係) is_staff(布爾值--是否能夠訪問admin站點)
is_active(帳號是否激活 布爾值--建議使用邏輯刪除False以防外鍵中斷)
last_login(最後一次登陸時間) date_joined(帳戶建立時間 當用戶帳號建立時默認時當前時間date/time)
經常使用的方法
set_password()不會保存User的實例對象 當None爲raw_password時密碼將是一個不可用密碼
check_password()若給定的raw_passwor是用戶的真實密碼返回值爲True 能夠校驗密碼

建立用戶模型類
User 繼承自 AbstractUser
而後添加本身須要的字段
設置表名
在站點中顯示的中文的單數verbose_name和複數verbose_name_plural兩者能夠相等表示顯示的內容一致
在配置文件中告知系統咱們自定義的 用戶模型類
AUTH_USER_MODEL='子應用名.模型類名'
而後進行數據庫遷移

 

註冊 -->建立用戶

判斷用戶名/手機號 使用失去焦點的事件 發送ajax請求 查看數據庫
GET users/usernames//(?P<username>\w{5,20})/count/ 這裏是根據js的請求肯定
'''
分析 頁面的整體需求

逐個分析具體小功能的需求

用戶輸入用戶名 前端發送過來輸入的內容 後端判斷是否在數據庫中存在
返回值能夠是 標記位 或者 count

肯定前端傳什麼內容
肯定請求方式 GET 路由users/usernames/itcast/--> users/usernames/(?P<username>\w{5,20})/count/
路由使用js中的(避免修改前端代碼)

分析使用哪一個視圖 APIView GenericAPIView ListAPIView
APIView
按照需求分析 實現開發

查詢數據庫

返回count值 0 1
'''

配置url路由

模型 使用系統的dajngo用戶模型 繼承 過來 添加手機號字段
系統 的用戶模型類 AbstractUser 沒有mobile字段 繼承過來 添加mobile字段就ok

自定義的用戶模型類不能直接被Django的認證系統所識別 須要在配置中告知系統自定義的模型類
# AUTH_USER_MODEL = '子應用名.模型名'

數據庫遷移


register.js 導入 vue, axios 把axios 放到js中 更改 register.html中的內容

光標移開後使用axios發送請求 查看用戶名是否存在


手機號是否重複 使用失去焦點事件 發送ajax請求

圖片驗證

使用captcha
"""
前端把生成的uuid發送給後端

後端
接受uuid
並生成圖片驗證碼
保存圖片驗證碼內容
把圖片返回給前端-->HttpResponse -->返回bytes類型數據 圖片就是 剛恰好適用 s

請求方式 和 路由
GET verifications/imagecodes/uuid/(?P<image_code_id>.+)/

肯定使用哪一個視圖 APIView 不牽扯數據庫crud

按照步驟開發

"""

設置url

設置 前端 mounted 掛載 vue的生命週期的鉤子函數

 

發送短信驗證碼
驗證圖片驗證碼是否正確 傳uuid 圖片驗證碼 image_code
驗證手機號格式 mobile

url

前端 js html


發送短信的時候的時間問題

發送短信同步操做 容易出現問題

使用celery 異步發送短信郵件等-->耗時操做
celery 即插即用的任務隊列 --用就用刪了也不礙事

celery的組成
任務(tasks) delay-->任務隊列(broker 負責任務的分發行業存儲) 任務處理者(worker 負責執行任務)
四個文件的關係進行分析

任務 就是一個函數 被celery的實例對象的task裝飾 @app.task
建立實例對象
配置信息 broker單獨的配置文件
app.config
app.autodiscover([任務])-->任務被Celery的實例對象自動發現
建立worker

celery_tasks包
發短信 發郵件的功能的包
包裏面是發短信 郵件 的功能模塊tasks.py
tasks.py 中 是使用Celery實例對象裝飾的 任務函數
config.py 中 是配置中間人broker的redis地址 和 執行結果的地址
main.py 中 是建立Celery實例 工程配置文件 加載配置文件 和實例對象自動檢測任務
功能函數調用時 必須使用.delay()


文檔裏面都有


註冊
serializers.ModelSerializer
模型類中沒有的字段的定義
class Meta:
指定模型類(model=模型類)
fields = ("字段", "包括自定義的字段--模型中沒有的字段")
extra_kwargs = {}
def validate_單獨驗證的字段():-->手機號知足規則 是否統一協議
def validate()多個字段驗證-->密碼和確認密碼兩者一致 短信驗證碼


跨域CORS 同源策略
源 協議 域名 端口號
如url中的 協議 域名 端口號 相同 則爲同源

同源策略 不一樣源的客戶端腳本 在沒有明確受權的狀況下不能讀寫對方的資源
如有須要 需使用 跨域 跨資源共享 cors

設置 步驟
安裝 註冊 註冊中間件在最上邊 設置白名單
域名 編輯 /etc/hosts/文件 添加 ip 域名 的對應關係

將前端的ip 抽取爲js文件設置 域名var host = '127.0.0.1:8000' 變量中聲明host,

#容許哪些主機訪問 安全機制
ALLOWED_HOSTS = ['127.0.0.1','api.meiduo.site']


密文存儲密碼
User使用的是系統的模型 可使用set_password設置密文密碼 由於系統的密碼就是密文
user.set_password(validated_data.get("pasword"))
user.save()

髒數據要及時刪除

JWT JSON WEB TOKEN -->token
用戶登錄後 服務器返回token 客戶端保存token
用戶再登錄的時候在header中發送token 服務端驗證經過 經過返回數據(服務端須要支持CORS)

token 分爲三部分
頭部 HEADER 承載兩部分信息(類型 加密的算法) {"alg":"HS256", "typ":"JWT"}
載荷 PAYLOAD {"放一些":"有效信息"}經過base64加密 不能夠放敏感信息
簽證 SIGNATURE 加密以後的HEADER 加密以後的PAYLOAD 和SECRET

SECRET是保存再服務端的jwt的簽發也是在服務端 secret是進行jwt的簽發和驗證的私鑰 不能泄露

jwt的配置 參考課件
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),# token的有效期
# 自定義的返回登陸須要的返回值的方法
'JWT_RESPONSE_PAYLOAD_HANDLER': 'utils.users.jwt_response_payload_handler',
}

配置url
url(r'auths/', obtain_jwt_token, name='auths'),
obtain_jwt_token這個自動獲取token的視圖默認返回值只有token
咱們須要修改這個視圖的返回值來完成咱們本身的需求
自定義jwt返回制指定數據
在utils中添加users.py
裏面實現自定義 jwt_response_payload_handler()方法實現自定義返回值 返回登陸須要的值user_id username

註冊完成以後實現自動登錄
註冊完成以後返回數據以前 給他一個token
手動建立新令牌 返回token username user_id(前端須要的數據)
user 就是save()時生成的對象

 

登錄的實現

增長支持用戶名與手機號都可以看成登陸帳號
經過Django認證系統的authenticcate()方法來支持使用手機號和用戶名都能登陸的功能
Diango的認證系統須要繼承自django.contrib.auth.backends.ModelBackend 並重寫authenticate()方法
authenticate(self, request, username=None, password=None, **kwargs)參數的說明
request 本次認證的請求獨享
username 本次認證的用戶帳戶
password 本次認證的帳戶密碼

先經過系統的認證 認證成功以後生成token 重寫 authenticate()方法添加手機號和帳號

重寫authenticate()方法的思路
根據username查找相應的用戶對象 username便可能是用戶名也多是手機號
若查找到用戶對象就使用check_password()方法驗證密碼是否正確

查找用戶可能在多處使用 就寫在utils中的users.py裏面

1定義經過帳戶名或者手機號查找用戶對象的方法
返回用戶對象
2定義用戶名或者手機號認證的類(繼承自ModelBackend)
重寫authenticate()方法
使用上面的方法獲取用戶對象
驗證密碼
返回用戶對象

在配置中告知Django使用自定義的認證方法
AUTHENTICATION_BACKENDS = [
'utils.users.UsernameMobileAuthBackend',
]

 


第三方登陸 QQ登陸

到相應的平臺中申請
`我的開發者
`企業開發者
申請應用
APP ID
APP Key(方便對方進行管理)
照文檔開發
添加圖標
圖標按鈕的onclick事件
點擊圖標轉到一個頁面
點擊這個頁面的贊成獲取到一個認證code
經過認證code 獲取贊成的token
經過token獲取openid(用戶在qq登陸驗證成功)
openid是此網站上惟一對應用戶身份的標識(第三方網站保存這個openid就能夠)
第三方網站能夠存儲
或者和用戶進行綁定

按鈕的點擊事件 請求後端而後 後端拼接請求qq的認證頁面的url

後端拼接完成以後返回給前端

添加路由
定義一個類視圖 get 返回url
| | | |
qq登陸的sdk

準備oauth_callback回調頁,用於掃碼後接受Authorization Code
經過Authorization Code獲取Access Token
經過Access Token獲取OpenID

QQ返回一個code 在回調的url中
讓前端將code傳遞給後端
後端經過code從QQ獲得token(QQ驗證這個code是否合法 驗證經過返回Access Token)
經過token獲取open_id
根據open_id判斷用戶是否綁定
綁定就登錄
未綁定就進行綁定


請求方式 GET
路由 /oauth/qq/users/?code=xxx
視圖 APIView

配置 url

視圖 APIView

def get(self, request):get請求
code=接收code
if code is None:判斷code
不存在 status=400 從rest_framew導入status (有狀態碼的含義)
qq = OAuthQQ(文檔中須要的參數 與上一個不太同樣client_id=xx, client_secret=xx, client_uri=xx)
# 根據code獲取token
token = qq.get_access_token(code)# sdk封裝的方法
# 根據token獲取open_id(此網站上惟一的用戶身份的標識)
open_id = qq.get_openid(token)# sdk封裝的方法
try:
# 根據open_id判斷用戶是否綁定
qquser = OAuthQQUser.objects.get(open_id=open_id) # 經過open_id查詢數據庫獲得qquser對象
except OAuthQQUser.DoesNotExist:
# 用戶不存在就去綁定 --> # TODO
# openid屬於敏感數據 綁定的時候須要一個有效期
# 將數據發送給一個不受信任的環境 須要設置密鑰 傳輸的時候進行加密解密 使用it's dangerous JWS
# Json Web Signature
pass
else:
# 存在就應該登陸 登陸--就是返回Token username user_id
#用戶存在就自動 -->返回{token(封裝 調用) username user_id} --> user = qquser.user
user = qquser.user
from utils.users import send_token
token = send_token(user) # 返回的是user對象?
finally:
pass


我的中心

一個用戶 多個商品 一對多

用戶信息 用戶表中 必須是登陸用戶才能訪問
商品信息 在瀏覽表中
兩個表兩次查詢中

兩個接口 屢次返回數據

必須是登陸用戶才能訪問
接收用戶信息
根據用戶信息查詢user
將user轉化爲字典 返回字典數據

token的傳入方式 在請求頭中添加Authorzation: JWT token

請求方式GET 路由/users/infos/token/ 視圖RetriveModelMixin-->獲取一我的的信息

我的中心信息展現
class 視圖(APIView):
只能是登錄用戶才能訪問
permission_classes = [IsAuthenticated]
get
接收用戶信息
user = request.user-->發起請求的用戶對象
將對象轉換爲字典
serializer = 序列化器UserCenterInfoSerializer(user)-->使用ModelSerialzer
返回數據
return Respone(serializer.data)


設置郵箱

當用戶輸入郵箱以後點擊保存
將郵箱信息 token 發送給後端
接收數據
保存郵箱信息到數據庫中 添加郵箱的標記位email_active 字段(False未激活 True已激活)
發送一份激活郵件(郵件中包含用戶和郵箱的識別信息,user_id和email數據 基於安全性考慮 使用itsdangerous進行加密 生成token做爲連接的參數)
用戶點擊激活郵件 修改郵件的標記位 未激活變爲已激活
返回響應

PUT /users/emails/ put-->在請求的body中 UpdateAPIView/APIView
class EmailUpdateAPIView(APIView):
permission_class = xx
def put(self, request):
data = request.data
user = request.user
serializer = EmailUpdateSerializer(instance=user, data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
# 保存以後發送激活郵件
# 藉助第三方郵件服務器發送郵件 163
from django.core.mail import send_mail
send_mail(
subject = 主題
message = 信息
html_message = 能夠是html標籤
from_email = 發件人
email = request.data["email"]
recipient_list = []收件人列表
)

return Response(serializer.data)

send_email 的異步執行

EmailUpdateSerializer(serializer.ModeSerialzier)

meta
model= User
fields = ['email', 'id']

 


用戶中心收購地址

省市區 三級聯動

建立模型 數據庫遷移 source 數據
表結構
id name parent_id

一個省 對應多個市

經過省獲取市的數據-->關聯模型類小寫_set=[]


獲取省份信息
一級視圖 獲取省的信息
get 方法 路由 areas/infos/
areas = Area.onbjects.filter(parent=None)
將列表轉換位字典數據
使用序列化器serializer = AreaSerializer(areas, many=True)
繼承自AreaSerializer(serializers.ModelSerializer
返回數據serializer.data

 

市 區縣
ares/infos/pk
視圖
get (pk)
獲取指定數據(parent=pk)
使用序列化器 將列表數據轉換爲字典數據
返回數據serializer.data

 

使用視圖集實現 ReadOnlyModelViewSet 或者 GenericViewSet
視圖(ReadOnlyModelViewSet );
query_set=
經過重寫get_query_set(self)方法 判斷獲取的是省份信息仍是市區的信息
經過重寫get_serializer_class(self)方法判斷返回省份信息仍是市區的信息


視圖集的url
導入
from rest_framework.routers import DefaultRouter
建立實例router
router = DefaultRouter()
註冊
router.register(r'infos', views.AreasViewSet, base_name="")
添加到urlpatterens
urlpatterns += router.urls

導入js 及 html文件


減小數據庫的查詢 優化(使用緩存~)
頻繁查詢 但數據基本不發生變化(在相對的時間內 如一小時 一天 一週...) 減少數據庫的壓力
緩存
settings配置
設置redis的庫
設置時效

使用drf-extensions提供的擴展類CacheResponseMixin進行數據的緩存
drf-extensions擴展對於緩存提供了三個擴展類:

ListCacheResponseMixin

用於緩存返回列表數據的視圖,與ListModelMixin擴展類配合使用,實際是爲list方法添加了cache_response裝飾器

RetrieveCacheResponseMixin

用於緩存返回單一數據的視圖,與RetrieveModelMixin擴展類配合使用,實際是爲retrieve方法添加了cache_response裝飾器

CacheResponseMixin

爲視圖集同時補充List和Retrieve兩種緩存,與ListModelMixin和RetrieveModelMixin一塊兒配合使用。

三個擴展類都是在rest_framework_extensions.cache.mixins中。

 

用戶地址管理
地址 用戶 收貨人 省 市 區 手機號 固定電話 郵箱 默認地址
address user_id receiver province city county mobile tel email True


每個用戶只有一個默認地址 能夠將默認地址放到user表中 數據庫優化

 

# 新增地址
class AddAddress(APIView):
"""
前端傳入相應信息 地址 用戶 收貨人 省 市 區 手機號 固定電話 郵箱

接收數據-->接收的數據中沒有user 可是能夠經過request.user
獲得 user後須要傳給序列化器 使用context參數傳過去
驗證數據
數據入庫
返回響應
"""

地址的CRUD 實現

 

商品部份 首頁, 分類, 商品詳情 的內容


1儘可能多的分析字段 明顯的字段先定義出來
分類表 名字 id
商品表 名字 id brand 價格 圖片
品牌表 名字 id logo

2分析表和表之間的關係 (把表進行兩兩分析) 兩個表之間的字段的關係分析 遍歷分析

3只要是多對多就拆分紅三個表

SPU satandard product ubit(標準產品單位) 商品信息聚合的最小單位 一個iPhoneX
SKU stock keeping unit(庫存量單位) 庫存進出的計量單位 一個金色的全網通256G的iPhoneX


FASTDFS 分佈式文件存儲系統 冗餘備份 負載均衡 線性擴容
TrackServer --->負責負載均衡和調度
StorageServer-->負責文件存儲

FastDFS環境配置搭建
使用docker進行安裝

docker 沒有可視化界面的虛擬機
安裝 按照readme安裝
是C/S架構

docker的鏡像和容器

docker鏡像-->安裝系統的iso文件 或者虛擬環境
從服務器下載鏡像 docker pull redis/mysql...
從本地加載鏡像 docker load -i 鏡像路徑
導出鏡像文件 docker save -o 文件名字.rar 鏡像名
查看全部鏡像 docker images
刪除鏡像 docker images rm 鏡像名

docker容器 -->運行起來的鏡像 就叫作容器 一個鏡像能夠建立不少個容器
運行鏡像 docker run [選項] 鏡像名 可選指令 例:docker run -it ubuntu 進入到交互模式 退出容器關閉
給容器起名字 :docker run --name名字 ubuntu
以守護進程的方式運行容器 docker run -dit 鏡像名 後臺運行 nginx就須要
查看正在運行的容器 docker container ls
羅列全部的容器 docker container ls --all (運行的沒運行的都羅列出來) up是正在運行的
運行/中止容器 docker container stop/start 容器id/容器名字
進入正在運行的容器 docker exec[選項] 容器名/容器id /bin/bash
刪除容器 docker container rm 容器名/容器id-->正在運行的容器須要中止運行再刪除


自定義文件存儲系統--->>>???????//TODO
Django自帶由文件存儲系統 可是是存儲在本地
本項目中咱們須要將文件存儲在FastDFS服務器上

給tracker 1分配一個ip 2映射一個真實的目錄環境來保存文件資源 3將trackerserver 啓動起來
docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker

給storage 1分配一個ip 2讓storage找到tracker 3給storage映射一個真實的環境 4將storage啓動起來
docker run -dti --network=host --name storage -e TRACKER_SERVER=192.168.31.55:22122 -v /var/fdfs/storage:/var/fdfs delron/fastdfs storage ||||變化|||||


pip install /home/python/Desktop/fdfs_client-py-master.zip
導入配置文件client.conf到utils中
修改配置文件中的ip 爲本機IP
在shell中執行
from fdfs_client.client import Fdfs_client

client = Fdfs_client('utils/fdfs/client.conf')

client.upload_by_filename('/home/python/Desktop/pictures/11.jpg')

_open(name, model="rb)-->打開文件 以二進制的方式
_save(name, content)-->保存 文件名 文件內容
open和save是必須實現的方法
_exist()若是文件名在系統中已經存在爲True
url(name)返回訪問文件的完整url
delete(name)經過文件名刪除文件
listdir(path)列出路徑中的內容
size(name)返回文件的大小

在fdfs中新增fastdfsstorage 編寫自定義文件存儲系統
1 自定義的文件存儲系統必須繼承自django.core.files.storage.Storage
2 在存儲類中必須實現_save()和_open()方法, 以及任何後續可能使用到的任何方法
3 Django 必須可以在沒有任何參數的狀況下實例化您的存儲系統,這就意味着任何全部的設置都應該來自django.conf.settings
4 您的存儲類必須是可解構的以便於在遷移字段上使用時 能夠對其進行序列化
---->只要您的字段具備可自行序列化的參數 就可使用django.utils.deconstruct.deconstructible類裝飾器
---->這個裝飾器就是django系統的FileSystemStorage上使用的
5 在settings中配置自定義django文件存儲類

 

富文本編輯器 CKEditor
相似於word文檔 能夠進行 排版 文字 圖片 等等-->如商品的詳情信息 SPU
安裝
註冊 富文本編輯器 及 其中的圖片上傳模塊
在settings中進行配置
添加ckediter的路由 工程的路由
在商品SPU中添加響應的字段 詳細信息 包裝信息 售後服務 而後遷移字段到數據庫

添加測試數據
在後臺admin中顯示
在contains的admin中註冊模型類 廣告內容分類ContentCategory 廣告內容Content
在goods的admin中註冊模型類 商品分類 GoodsCategory 商品頻道GoodsChannel 商品Goods... 等模型類
建立admin超級用戶
解壓數據包data
刪除stroage中原有的data文件夾
將解壓數據包data移動到stroage中
導入sql數據


數據庫備份 mysqldump -uroot -pmysql 數據庫名 > .sql文件
數據庫恢復 mysql -uroot -pmysql 數據庫名 < .sql文件(路徑)


首頁靜態化
"""
分類數據在其餘界面中也有顯示
因此單獨作一個接口

首頁商品數據 在商品表中
也單獨作一個接口

首頁的訪問量比較大
致使首頁商品數據接口 和 商品分類接口訪問頻繁
致使數據訪問頻繁 有損數據庫性能

由於這兩個接口的數據不常常發生變化 就可使用緩存 cache
解決數據庫頻繁訪問的問題 可是數據的組織須要必定的時間 用戶體驗很差 感受比較卡

SEO閃亮登場
網站的搜索優化 主要抓取網站的靜態頁面 動態網頁是抓取不到的

首頁的靜態化處理 將首頁的部份內容先寫好 用戶訪問的時候可先加載這部份內容
靜態頁面的數據是最新的數據
先查詢數據
將查詢出的數據填充到靜態頁面中
把最新的靜態頁面放到指定的目錄中
"""
新建 crons.py-->首頁靜態化文件
...

先獲取模板
到系統指定的模板目錄中加載指定的模板
template = loader.get("模板文件名")
將獲取的數據傳遞給模板
html_data = template.render(數據)
把htnl數據寫入放到指定的目錄中(寫道front中)
file_path = os.path.join(配置中的變量, '模板文件名')
寫入靜態文件數據
with open(template) as f:
f.write(html_data)


新建模板文件夾
settings中配置模板文件夾 'DIRS': [os.path.join(BASE_DIR, 'templates')],
將模板文件(html文件)添加到templates中

安裝 install
註冊crontab

定時執行 靜態首頁獲取最新數據 的任務
系統級的任務-->寫在系統的settings中
CRNJOBS 的三個參數
1 頻次 minute hour day month week
2 任務 (函數)
3 >> 日誌的路徑-->必須正確,文件能夠不存在生成日誌的時候本身建立

在Terminal中添加任務 crontab add 羅列任務crontab show
移除定時任務 python manage.py crontab remove

 

商品列表頁面
頁面分析
讓前端傳入分類id
根據分類查詢分類下的商品列表 和 熱銷列表
2個接口

熱銷商品的接口
GET /goods/skus/(?P<category_id>\d+)/hotskus/
類視圖(ListAPIView)
指定序列化器
經過從寫get_queryset(self):
從self中獲取category_id
經過category_id從SKU表中查詢出查詢出3條數據安照銷量sales降序排序

GET /goods/skus/(?P<category_id>\d+)/skus/
商品列表數據的獲取
視圖(ListAPIView):
指定序列化器
添加排序
filter_backends=[OrderingFilter]
ordering_fields = ["字段", "字段", "字段"]
添加分頁
pagenation_class = CustomPageNumberPagination----- page頁碼 page_size每頁多少條數據
須要在 settings的REST_FRAMEWORK中添加PAGE_SIZE=2
或者經過繼承的方式自定義page_size=2 可是默認是不能設置的繼承重寫page_size_query_param=page_size
經過從寫get_queryset(self):
從self中獲取category_id
經過category_id從SKU表中查詢出查詢出全部數據

class CustomPageNumberPagination(PageNumberPagination):
# 每頁顯示幾條數據
page_size = 5
# 默認是None 設置的page_size無效
# 每頁顯示幾條數據
page_size_query_param = "page_size"

列表頁面商品分類的靜態化
將不常變的數據先查詢出來給模板
分類數據的靜態化 分類數據不多發生變化 當數據發生變化的時候 從新 生成靜態化頁面
列表和熱銷數據 使用ajax進行局部刷新

在utils goods.py中建立 獲取分類的方法 返回全部分類
在模型類發生變化的時候執行這個方法 從新生成靜態化頁面
在admin的admin.ModelAdmin的save()方法執行的時候執行
使用celery異步執行 生成靜態頁面的方法


商品搜索 -- 模糊搜索

安裝鏡像 經過docker
配置文件 的host改成本機的ip
like 的效率很低 不使用
使用 Elasticsearch 搜索引擎 配合elasticsearch-analysis-ik拓展實現中文分詞處理
安裝elasticsearch-ik 本地加載 docker load -i elasticsearch-ik--2.4.6_docker.tar
修改elasticsearch的配置文件 elasticsearc-2.4.6/config/elasticsearch.yml第54行,更改ip地址爲本機ip
network.host: 本機ip
建立docker容器運行
docker run -dti --network=host --name=elasticsearch -v /home/python/Desktop/elasticsearch-2.4.6/config:/usr/share/elasticsearch/config delron/elasticsearch-ik:2.4.6-1.0

把數據組織好以後 給搜索引擎 讓搜索引擎進行分詞處理
藉助haystack將數據傳遞給elasticsearch data-->haystack-->elasticsearch
安裝haystack
註冊haystack
在setting中進行haystack的配置 url爲本機的
建立索引類 -->在goods中(針對商品的搜索)search_indexes.py
建立模板-->search文件夾 indexes文件夾 goods文件夾 sku_text.text文件
裏面指定要建立分詞的字段{{object.id}}{{object.name}}...
在虛擬環境中執行 python manage.py rebuild_index 生成索引分詞

在視圖中建立搜索的視圖集
建立搜索的序列化器
添加路由

添加search 的html 和 js


商品詳情頁面
靜態化 在商品的信息發生變動的時候觸發生成靜態化頁面的功能
celery -A celery_tasks.main worker -l info-->在有celery_tasks的目錄下執行

使用腳本工具自動生成全部的商品詳情頁面

shell編程
#! usr/bin/env python -->指定環境
編寫python共功能代碼
切換到相應目錄
改變文件的執行權限
執行文件

 

用戶瀏覽歷史記錄
只記錄登陸用戶的瀏覽記錄
保存user_id sku_id

用戶訪問某一個商品詳情的時候將用戶id商品id發送到後端
將數據保存到redis中 數據庫中均可以

POST users/histories/ CreateAPIView
只有登陸用戶才能訪問
接收數據 驗證數據 保存數據 返回響應
按照步驟開發

當用戶訪問我的中心的時候將數據添加到最近瀏覽中


最近瀏覽的數據結構
user_id:[sku_id1,sku_id2,sku_id3,sku_id4,sku_id5]
使用有序集合ZSet 有序且不重複 經過權重將元素進行升序排序
此處權重設置爲時間的前後---->自行實現

redis的數據結構
String key:value字符串
List key:[value1,value2,value2,,...]列表
Hash key:field:value field2:value2..哈希
Set key:[value2,value1,value3,,...]集合
ZSet key:[value1,value2,value3,,...]有序集合


視圖
指定權限
指定序列化器
保存數據-->重寫序列化器的create方法
連接redis
獲取數據(user_id, sku_id)user經過context的request中獲取
在添加以前先刪除可能存在的sku_id ()
lrem(user.id, 0, sku_id)
保存
lpush(user.id, sku_id)從左邊插入數據
限制五條數據 ltrim 讓列表只保留指定區間內的元素
ltrim(user.id, 0,4)
列表去重
在插入數據到redis中前 查詢redis中是否有重名的數據 刪除重名的數據再插入數據

 

 

購物車 部分

不登陸用戶也能夠訪問購物車 將商品添加到購物車中
數據是保存在瀏覽器的cookie中

登陸用戶將數據保存到在服務器 redis和mysql均可以保存
實際開發最好放在數據庫中 本項目爲使用reids知識 就保存在redis中

須要保存的數據
未登陸用戶 商品id 數量 是否選中-->記住會動的東西
登陸用戶 商品id 用戶id 數量 是否選中

未登陸的數據的組織 cookie數據
cart = {"sku_id":{"count":5,"selected":True}}

登陸用戶的數據的組織 redis數據
保存在redis中-->使用列表能夠 可是不穩定 數據容易很混亂

使用Hash哈希能夠可是缺乏狀態 因此只記錄商品的id和個數
cart = {"user_id":{"sku_id":"count"}, "user_id": {"sku_id": "count"}}

使用Set 集合 記錄選中的id
cart_selected ={"user_id":{"sku_id", "sku_id", ..}}


cookie數據的處理 cookie數據的加密 使用pickle模塊
pickle.dumps(數據)將python數據類型數據轉換爲bytes類型
pickle.loads(數據)將bytes類型數據轉換爲python數據類型
使用base64模塊將bytes類型的數據從新編碼

import pickle
import base64
cart = {"1": {"count": 5,"selected": True}}
# 將字典數據轉換爲bytes類型
dumps = pickle.dumps(cart)
# 將bytes類型的數據使用base64從新編碼
encode = base64.b64encode(dumps)
# 將從新編碼以後的數據轉換爲字符串
encode.decode()

# 將數據轉換爲bytes類型
base64.b64decode(encode)

base64模塊 以6個bit爲一個單元 加密 解密 沒有語言之間的限制
對cookie數據進行從新編碼
base64.b64encode(bytes類型數據) 對bytes類型進行編碼 可以使用decode()轉換爲字符串
base64.b64decode(base64數據) 解碼


如用戶token過時 就讓其以匿名用戶的身份訪問 將商品添加到購物車(數據放到cookie中)
須要重寫 perform_authentication()方法 不讓它驗證 在須要判斷的時候在進行判斷


將商品添加到購物車
當用戶點擊添加購物車按鈕的時候 登錄用戶須要進將token(request.user) sku_id count selected 提交給後端
後端接收數據
驗證數據 序列化器完成驗證(sku_id, count, selected)
獲取數據 serializer.validated_data.商品數據信息
獲得用戶的信息 request.user
判斷user是否存在(是否登陸)
用戶存在將數據保存在redis中(登陸)
連接redis
保存數據 set hash redis_conn.hincrby("cart_%s" % user.id, sku_id,count)
將選中的數據保存到set中 sadd("cart_selected_%s"%user_id, sku_id)
返回數據
不存在cookie中(未登陸)
先讀取cookie信息 request.COOKIES.get("cart")
判斷是否有購物車信息
若是有是加密的數據(base64編碼的bytes的數據)
解碼(pickle.loads(base64.b64.encode(接收的cookie_cart)))
沒有
初始化一個空字典
判斷該商品是否存在cookie中
若是有
累加 last_count = cart[sku_id]["count"] count+=last_count
若是沒有
添加 cart[sku_id] = {"count":count, "selected":selected}
將購物購物車數據加密 response.set_cookie("cart", base64.b64encode(pickle.dumps(cart)).decode())
返回購物車數據 return response

cookie信息的讀取 value = request.COOKIES.get("鍵")


獲取購物車數據
判斷用戶是不是登錄用戶-->能不能從request中獲取用戶信息
登陸用戶從redis中查詢數據
連接redis
獲取哈希數據
hgetall-->獲取哈希中的全部域和值
獲取set數據 redis_cart
遍歷哈希數據 redis_selected
將其轉換爲與cookie_cart同樣的數據格式 redis中取出的數據是bytes類型的
cart = {}
for sku_id, count in redis_cart.items():
cart[int(sku_id)] = {
"count":int(count),
"selected": sku_id in redis_selected
}

未登陸用戶從cookie中獲取數據 request.COOKIES.get("cart")
判斷cart數據
有 解密 pickle.loads(base64.b64decode(cookie_cart))
無 爲空 cart = {}(初始化一個)
商品id-sku_id 數量-count 選中狀態-selected
獲取傳入的購物車中的全部的商品
遍歷全部商品 動態的給每件商品添加屬性
sku_ids = cart.keys()
skus = SKU.object.filter(id__in=sku.ids)
for sku in skus:
sku.count = cart[sku_id]["count"]
sku.selected = cart[sku_id]["selected"]
將對象轉換爲字典
seriaizer = 序列化器(skus, many=True)
返回數據

 

修改購物車數據
冪等 -->前端發送的是完整數據 校訂先後端的數值的一致便可
非冪等-->前端發送的是每次加的數量 有可能受網絡波動影響 形成數據出錯

前端傳入sku_id(must) count(must) selected(must) token(可選)
接收數據
驗證數據
獲取校驗過的數據 sku_id count selected
獲取用戶
判斷用戶是否登錄
登陸
更新redis
連接redis
更新hash hset("cart_%s" %user.id, sku_id, count)
更新set 選中: sadd("cart_selected_%s"user.id, sku_id)
未選中 srem("cart_selected_%s"user.id, sku_id)
返回數據
未登陸
更新cookie
獲取cart數據
有 解密 pickle.loads(base64.b64decode(cookie_cart))
無 爲空 (初始化)
更新數據
if sku_id in cart:
cart[sku_id] = {"cont":count, "selected":selected}
加密數據 cookie = base64.b64.encode(pickle.dumps(cart))
response.set_cookie("cart",cookie .deocde() )
返回數據
return response

 

# 刪除購物車數據
# 接收數據 data = request.data
# 校驗數據 序列化器(data=data) 序列化器.is_vali()
# 獲取驗證過的數據 sku_id = serialzier.validated_data["sku_id"]
# 獲取user
# 根據狀態判斷操做redis仍是cookie
# 登陸 操做redis
# 連接redis
# 刪除哈希 hdel("cart_%s"%user.id, sku_id)
# 刪除set srem("cart_selected_%s" % user.id, sku_id)
# 返回響應
# 未登陸 cookie
# 獲取cart信息 request.COOKIES.get("cart")
# 判斷cart數據
# 有 解密 pickle.loads(bsae64.b64decode(request.COOKIES.get("cart")))
# 無 爲空 (初始化)
# 刪除數據 if sku_id in cart: del cart[sku_id]
# 加密字典數據base.b64encode(pickle.dumps(cart)).decode
# response.set_cookie(base.b64encode(pickle.dumps(cart)).decode)
# 返回響應 return response


---->>4.11日實現
HINCRBY redis_conn.hincrby("cart_%s" % user.id, sku_id, count) 添加購物車使用代替自寫大段代碼

redis 管道技術 pipeline()
性能優化 減小c和s的連接(tcp包)的創建次數
建立管道實例
p = redis_conn.pipeline()
將命令添加到實例zhong
p.hincrby("cart_%s" % user.id, sku_id, new_count)
執行管道
p.excute()-->必定記得要執行管道啊 啊啊啊啊啊啊啊啊!!!!!!

 

登陸合併cookie中的數據到用戶的購物車redis中

登陸的時候
在登陸obtain_jwt_token()系統的不能知足咱們的需求時
重寫它 自定義一個類 繼承自ObtainJSONWebToken drf_jwt 提供的登陸獲取token的視圖
修改url 件url指向這個類

 

將ccokie數據合併到redis中
寫到函數中 在用戶 登陸的時候調用這個函數完成合並

獲取cookie數據 {sku_id:{count:xx,selected:True}}
獲取 redis數據 hash{sku_id:count} set{sku_id}
合併
以redis爲主
cookie中有的數據redis中沒有 添加 狀態添加
cookie中有的數據redis中有 替換 狀態添加
數據合併以前 定義二個變量
一個接收redis數據 一個記錄選中的id
對cookie進行變歷 合併數據
將最終的數據保存到redis
合併以後刪除cookie數據
|||||||||||||||||||||||
merge_cookie_to_redis(user, request, response)
獲取cookie數據get
判斷cookie數據 if not None
解密cookie數據 loads(base64)-->cart
獲取redis
連接
獲取hash hgetall bytes類型-->{sku_id:count}-->{b'1':b'10'}
定義redis_sku_ids = {} 存放遍歷完成以後的int類型的數據
定義selected_ids = [] 存放cookie中被選中的商品的ids
遍歷redis數據 for sku_id, count in redis中獲取到的數據的items():
對redis數據進行轉換for循環
redis_sku_ids[int(count)]=int(count)-->redis_sku_ids[int(count)]=count
遍歷cookie數據 for sku_id ,count_selected in 解密後的cookie中的cart的items():
若是cookie中的sku_id在redis中存在 if sku_id in redis_sku_ids:
就將redis中的該商品的數量改成cookie中的 redis_sku_ids[sku_id]=count_selected小字典["count"]
若是不存在 就寫入
redis_sku_ids[sku_id] = count_selected["count"]
添加到redis中 hmset-->保存多個哈希數據
redis_conn.hmset("cart_%s"%suer.id, redis_sku_ids) 就是保存int類型的redis數據的字典
sadd-->一個一個添加 解包 *
redis_conn.sadd("cart_selected_%s"%user.id, *selecte_ids) 就是將保存cookie中被選中的商品的id解包
刪除cookie數據 response.delete.cookie("接收的cookie數據")
response.delete.cookie("cart")
return response
else:
返回響應

 

訂單模塊

支付功能 orders.py

登陸用戶才能訪問 只有登陸用戶纔有收貨地址
收貨地址的獲取 直接訪問接口就能夠
商品列表從redis中獲取 展現選中的商品
運費 動態返回


前端傳入用戶信息 token(user)
登陸用戶才能訪問
獲取用戶信息
獲取redis信息 從redis中查詢購物車的信息 經過user.id
連接
獲取redis數據
hash hgetall-->獲取全部的商品id和數量{sku_id:count,sku_id:count}
set smembers-->獲取全部被選中的商品的id [sku_id, sku_id,sku_id,..]
cart = {}
根據id查詢redis中選中的商品的信息
for sku_id, count in 哈希結果:
放到cart中
cart[int(sku_id)] = int(count)

skus = SKU.objects.filter(id__in=set查詢結果)-->全部被選中的商品 (只有被選中的商品才應該提交付款)
for sku in skus:
sku.count = cart[sku_id]
定義 運費 decimal類型數據 精度準
將對象列表轉換爲字典列表
serializer = 序列化器({"skus":skus, "freight":freight})
序列化器skus many=True
skus = 指定序列化器-->序列化器的嵌套
返回數據
return Response(serializer.data)


運費的動態返回
將運費信息和商品信息放在一個字典中 各佔一條數據
組織數據的時候
context = {
運費:貨幣類型,-->DecimalField(總長度,小數位數)能夠保證數據存儲的精確度
商品信息:serializer.data'
}

序列化器的嵌套
字段 = 序列化器(條件)
字段 =serializer.類型(選項)

使用時 將數據放在字典中


提交訂單 使用到事務和併發

提交訂單 點擊按鈕生成訂單
不相信前端的數據
涉及到金錢等敏感數據 須要本身從庫中查詢
商品的數據是在redis中 不須要前端傳入
前端須要傳入 用戶信息 支付方式 地址信息

訂單表 一個訂單中有多個商品 一對多

調用序列化器的save()方法時
重寫 序列化器的create方法
須要將 訂單訂單信息 和 訂單商品列表的數據入庫
同時操做兩個表

首先實現訂單 信息的入庫 (一)
接收地址信息
接收用戶信息
接收支付方式
生成訂單id order_id 使用Django的time_zone.now .strftime(%Y%m%d%H%M%S)+"%09d"%user.id
訂單總數量 價格 運費 total_count = 0 total_amount=Decimal("0") freight=Decimal("10")
根據支付方式決定訂單狀態 if pay_method ==1:satus==2 else:status = 1 可使用常量表示
建立訂單 order = OrderInfo.fiter.create()

在實現 訂單商品列表的保存 (多)
連接redis -->cart
獲取redis數據
hash hgetall
set smembers
將redsi數據轉換 selected_cart={}for sku_id in set
只轉換選中的信息
對選中的商品的列表進行遍歷 for sku_id ,count in set.items():
根據商品信息進行查詢 sku = SKU.objects.get(pk=sku_id):
根據商品數量判斷庫存 if sku.stock < count:
庫存減小 銷量增長 sku.stock -= count sku.sales += count
數據入庫 sku.save()
order.total_count += count
order.total_amount += (count * sku.price)
order = Order_Goods.filter.create()

清除redis中選中的商品的信息


同時操做多個表 使用事務 要麼一塊兒成功 要麼一塊兒失敗
事務的with 語法-->在建立訂單表以前
save_pont 失敗的話回滾到這裏
with transaction.atomic():
須要記錄保存點
回滾rollbask()-->寫在raise的前面 回滾到save_point
提交commit()-->提交point


併發
多個用戶同時對同一個商品下單時提交數據 對錶進行操做 出現資源競爭問題
能夠給該條數據加鎖
悲觀鎖-->當操做某條記錄時 讓數據庫爲該條記錄加鎖 鎖住後別人沒法操做 容易出現死鎖現象 採用很少
樂觀鎖-->在更新的時候判斷此時的庫存是不是以前查詢出的庫存
是就表示沒人修改能夠更新庫存 不同就表示被別人搶過資源 就再也不執行庫存更新

樂觀鎖須要事務隔離級別的配合
事務隔離級別
串行化 serializable一個一個執行
可重複讀repeapable read 本事務不受其它事務的影響 無論其它事務是否修改了數據MySQL的默認事務隔離級別

讀取已提交 read-committed 當其它事務提交了對數據修改 本事務就能讀取到修改後的數據值
讀取未提交 read-uncommitted其它事務只要修改了數據 即便未提交 本事務也能看到修改後的數據值

修改mysql的隔離級別 爲read-committed


支付 支付寶支付的接入
電腦網站的支付流程
選擇支付寶支付 點擊去支付 根據支付寶的文檔拼接url跳轉到到 支付寶的支付頁面 用戶登陸 選擇我支付渠道 輸入支付密碼 確認支付 支付寶會把支付的交易流水號trad_no給商戶系統 商戶系統須要保存這個流水號(用戶退款的惟一憑證) 將支付寶交易流水號與商戶的訂單號一一對應(寫在一張表中)
apoid
沙箱網關
應用程序公鑰 使用指令生成的--公鑰放在支付寶--私鑰放在程序keys中
支付寶公鑰 支付寶--私鑰放在支付寶--公鑰放在應用程序中(從支付寶複製的是字符串須要在首尾添加標記)

當用戶點擊去支付
前端將訂單id傳入

後端接收訂單id
根據訂單id查詢訂單的信息 應該查詢當前用戶未支付訂單
建立支付寶實例對象
生成order_string
拼接支付的url


當支付成功以後
讓前端傳入回調參數 pay/status/?xxx=xxxx

後端接收參數 查詢字符串 sign 不能參與驗證 就把它刪除了
根據文檔進行數據驗證
若驗證成功 獲取 訂單id 和支付寶流水號
將支付寶流水號和訂單id保存起來
修改訂單狀態


xadmin
註冊xadmin
遷移
建立xadmin.py 關聯adminx和模型類

主從同步的機制是主服務器的二進制日誌
優勢 提升數據庫性能 提升數據安全性 提升主服務器性能
讀寫分離

收集靜態資源到front 的static中
使用Nginx處理靜態請求 靜態部署

動態請求須要Nginx轉發給uwsgi 再轉發給Django
加載配置文件

server / {
server_name 域名或者 ip;
listen 端口;
location 規則{
root 引導的路徑; + 訪問路徑/後面的內容
index html文件;默認顯示哪一個文件
alias 引導的路徑; 路徑的別名是server後面的內容
}
}

反向代理 監聽用戶輸入的端口 ip(域名) 當用戶輸入指定的ip+port時 nginx將其 proxy_pass到指定的程序(域名/ip+端口)中 程序的端口 與 nginx監聽的端口 不能相同!!
sudo vi upstream 在裏面設置
server { listen port; # 用戶請求的
server_name 域名/ip;# ip和端口
location / {
proxy_pass ip:port ; # 將用戶的請求轉到這裏
}
}


負載均衡 依賴於反向代理 經過反向代理 將請求引導到負載均衡的服務器中
sudo vi upstream 在裏面設置
upstream backends {
server1 ip:port ;
server2 ip:port ;
server3 ip:port ;
....
}
server {
listen port;#用戶輸入的
server_name ip;#ip和端口
location / {
proxy_pass http://backends/;
# 經過反向代理將 請求順序依次(能夠進行不一樣算法的配置)分發到backends中的各個服務器中
}
}

根據權重 設置 每一個服務器的請求的接收請求的量
upstream backends {
server1 ip:port backup;# 備胎 不參與 當下面的服務都器宕機了 就啓用這個1
server2 ip:port weight=5; # 是倍數 2的接收請求的頻率是3的5倍
server3 ip:port weight=1;
....
}
server {
listen port;#用戶輸入的
server_name ip;#ip和端口
location / {
proxy_pass http://backends/;
# 經過反向代理將 請求順序依次(能夠進行不一樣算法的配置)分發到backends中的各個服務器中
}
}


經過ip的hash值 將用戶分配到指定的服務器中
upstream backends {
ip hash;
server1 ip:port;
server2 ip:port;
server3 ip:port;
....
}
server {
listen port;#用戶輸入的
server_name ip;#ip和端口
location / {
proxy_pass http://backends/;
# 經過反向代理將 請求順序依次(能夠進行不一樣算法的配置)分發到backends中的各個服務器中
}
}

nginx 日誌的配置是在負載均衡中進行設置的

 

Docker進階 一條小魚(docker) 在海洋中(宿主機) 能夠存放不少小箱子(images)

dockerhub 存放不少小箱子 能夠獲取使用

docker 的三個概念
image 鏡像--->獨立的空間
包含用戶root空間 和 內核 用戶空間創建在內核的基礎之上的
搜索鏡像 docker search 鏡像名
羅列鏡像 docker images
拉取鏡像 docker pull 鏡像名:版本號(可選, 不寫版本號就是最新的)
打包鏡像 docker save -o 文件名 鏡像名
加載鏡像 docker loads -i 文件名
刪除鏡像 docker image rm 鏡像名(image id)
命名鏡像 dcoker tag 鏡像名:版本號 新名字:版本號
docker鏡像 使用官方的

cointainer 容器
實質是一個進程 擁有本身的文件系統 網絡 等 -d就是以守護進程的方式運行
數據不能寫入容器中 文件要保存再數據卷中
羅列容器 docker ps -a(--all)
運行容器docker run -d守護進程方式運行--name起別名 -it以交互模式運行(直接進入,退出就關閉進行)容器名
/bin/bash 進入文件系統
進入正在運行的容器 docker exec -it 容器名 /bin/bash
查看容器信息 docker inspect 容器名
刪除容器 docker rm 容器名
容器打包爲鏡像 docker commit -t"操做內容" 容器名 新名字:版本號(tag)
查看容器日誌 docker logs

Repository 倉庫
大倉庫 好比 dockerhub(共有倉庫) 把鏡像放在倉庫中 再從倉庫中pull 名字:tag
私有倉庫 本身建立
下載 docker pull registry
啓動倉庫 docker run -d -p 5000:5000 --restart=always --name registry registry
檢查倉庫效果 curl 127.0.0.1:5000/v2/_catalog
配置倉庫權限 在/etc/docker/daemon.json中
{
"registry-mirror": [
"https://registry.docker-cn.com"
],
"insecure-registries": [
"本機ip:5000"
]
}
重啓docker服務 systemctl restart docker
提交鏡像到私有倉庫 docker tag old_image_name:tag ip:5000/new_image_name:tag
倉庫必須經過 https訪問 推送 docker push ip:port/鏡像名:tag
下載鏡像 docker pull docker pull 192.168.229.128:5000/nginx:v1.0

Volume數據卷
不要往容器中寫入文件 要放在數據卷中-->映射宿主機的目錄
設置數據卷目錄 docker run -itd --name 容器名字 -v 宿主機目錄:容器目錄 鏡像名稱 [命令(可選)]
再宿主機中的目錄中操做和再容器中操做 是同步的
-v 指向宿主機的目錄 就算容器刪除 數據卷也會存在
數據卷容器 (多個容器之間共享文件)
建立數據卷容器目錄
docker create -v 容器數據卷的目錄 --name 容器名字 鏡像名稱 [命令(可選)]
建立兩個容器,同時掛載數據卷容器目錄
docker run --volumes-from 數據卷容器id/name -tid --name 容器名字 鏡像名稱 [命令(可選)]

網絡

dockefile
建立一個文件 Dockerfile
再文件中寫入指令
FROM 解釋器:版本號 基於哪一個鏡像
LABLE 註釋:人員, 版本等
RUN 指令
WORKDIR /工做目錄
COPY 原路徑 目標路徑 將Django項目考入當前路徑
CMD ["pyhton3", "manager.py", "runserver", "0.0.0.0:800"] 容易一建立就執行
EXPOSE 8000 對外聲明端口

構建鏡像 docker build -t django:版本號 . echo "文件內容">文件名 (建立一個文件文件內容是""內的內容)

 

 

安裝 docker 參照文檔

 

 

 

 

 

 

 

json.loads()用於將str類型的數據轉成dict
json.dumps()用於將dict類型的數據轉成str
17344436474

 

 


bug 集錦
401 身份未認證
沒法訪問FastDFS中的圖片 tracker storage及其IP設置 settings的配置 自定義存儲類的配置
沒法登陸 容許攜帶cookie 容許訪問的ip cors的設置(註冊,中間件配置,白名單)
掉用serializer.save()方法以前必須調用serialzier.is_valide()方法
serializer.序列化器(data=數據, instance=對象)
serializer = 序列化器(數據)不用data=數據

Decimal 貨幣類型

有模型的序列化器
class 序列化器():繼承自ModelSerializer
有自定義的字段就寫-->須要添加到fields中
class Meta:
model = 指定模型類
fields = ["要","返回","給前端","的字段","也能夠","理解爲","要驗證","的","字段"]
exclude=("模型","中","不須要","使用(驗證)","的","字段")
extra_kwargs={"字段1":{"額外的":"選項約束"},"字段2":{"額外的":"選項約束"},..}
不知足需求 的方法重寫(self,validated_data)
重寫
返回 validated_data

序列化器數據流向 視圖導入序列化器-->data(value,attrs)---->validated_data

沒有模型的序列化器
class 序列化器():繼承自Serializer
字段 = 類型(選項)
def 單個字段驗證(self, value)
在驗證
返回value
def 多個字段驗證(self, attrs):
驗證
返回 attrs


判斷該用戶是否頻繁獲取
if redis_conn.get('sms_flag_%s'%mobile):
return Response(status=status.HTTP_429_TOO_MANY_REQUESTS)
redis_conn.setex('sms_flag_%s'%mobile,60,1)-->60秒內獲取一次

 

省市區的模型 省級行政單位 沒有 parent id市級行政單位的parentid是省區縣級行政單位id爲市使用自關聯模型 自關聯字段的外鍵 指向自身

相關文章
相關標籤/搜索