日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

天天生鲜(Django4.0版本) + 开发遇到的问题及解决

發布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 天天生鲜(Django4.0版本) + 开发遇到的问题及解决 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

1、項目來源及開發介紹

1.1、天天生鮮介紹

1.2、Web開發流程介紹

1.2.1、需求分析

1.2.2、項目架構概覽

1.2.3、SKU與SPU概念

2、項目架構

3、數據庫表結構

4、用戶認證模型

5、類視圖

6、用戶模塊開發

6.1、Django4.0認證系統文檔

6.2、用戶注冊

6.2.1、django發送郵件

?6.2.2、Celery異步任務隊列

6.3、用戶激活

6.3.1、加密用戶身份信息

6.3.2、解密用戶身份信息驗證

6.4、用戶登錄

6.4.1、配置redis作為Django緩存和session后端

6.4.2、登錄判斷裝飾器login_required

6.5、用戶退出

6.6、用戶地址

6.7、用戶個人信息

6.7.1 redis存儲歷史瀏覽記錄分析

6.7.2、django-redis獲取redis鏈接

7、商品模塊開發

7.1、了解FastDFS分布式文件系統

7.2、python對接fastdfs

7.3、項目上傳圖片和使用圖片流程

7.4、Django二次開發對接FastDFS

7.5、商品首頁

7.5.1、基本頁面處理

7.5.2、保存購物車信息的數據設計

7.5.3 頁面靜態化

7.5.4、使用緩存

7.6、商品詳情頁

7.7、商品列表頁

7.7.1、分頁

7.7.2、頁碼控制

7.8、商品搜索

7.8.1、安裝和配置

7.8.2、索引文件生成

7.8.3、全文檢索的使用

7.8.4、改變分詞方式

8、購物車模塊開發

8.1、添加到購物車

8.2、購物車頁面

8.3、購物車記錄更新

8.4、購物車記錄刪除

9、訂單模塊開發

9.1、提交訂單頁面

9.2、訂單生成

9.2.1、mysql事務

9.2.2、訂單并發處理

9.3、用戶中心-訂單頁

9.4、訂單支付

10、項目部署

10.1、uwsgi

10.1.1、uwsgi的安裝

10.1.2、uwsgi的配置

10.1.3、uwsgi的啟動和停止

10.2、nginx

10.2.1、nginx 配置轉發請求給uwsgi

10.2.2、nginx配置處理靜態文件

10.2.3 nginx轉發請求給另外地址

10.2.4、nginx配置upstream實現負載均衡

10.2.5、部署項目流程圖

11、開發過程中遇到的bug個人總結

1、在模板中載入靜態文件前需要在配置文件settings.py中加入配置

2、django新版本中查詢數據庫后對象不存在報錯的異常類是core.exceptions的ObjectDoesNotExist

3、加密用戶身份信息當做url的token值,用authlib包代替itsdangerous,可以指定簽名算法HS256

4、celery不支持window10,安裝eventlet包,使用celery -A celery_tasks.tasks worker -l info -P eventlet -E命令或者建議使用下面的命令就用安裝eventlet包

5、django自帶的login()報錯:redis輸入了一個空類型的值

6、Django的authenticate已經包含了is_active判斷,即使用戶名密碼正確,is_active為0也會返回空,所以需要在setting.py中加配置

7、FastDFS只支持Linux,可以把fastDFS運行在云服務器上,Windows本地電腦上運行FastDFS客戶端fdfs-client-py

8、window啟動nginx,先到文件夾中啟動nginx.exe,再到cmd中輸入nginx.exe。停止nginx,在cmd中完整有序停止用nginx -s quit,再用taskkill /f /t /im nginx.exe

9、is_authenticated在Django4.0中是一個屬性而不是一個方法

10、Django4.0已經移除ungettext,所以在引入haystack的時候會報錯,修改haystack文件夾下的admin.py把ungettext三處地方修改為ngettext

11、Django4.0已經移除smart_text,修改haystack文件夾下的form.py倆處地方把smart_text修改為smart_str

12、配置為樂觀鎖時,需要設置MySQL默認的隔離級別(可重復讀)為(讀取提交內容),在MySQL配置文件skip-ex,,,下添加一行transaction-isolation = READ-COMMITTED

13、網站如果想讓支付寶平臺訪問(獲取支付后的結果),需要有公網IP

14、安裝alipay-sdk-python 提示安裝pycrypto 問題:

15、支付寶沙箱測試不需要重新設置密鑰和公鑰,直接使用默認。

16、使用支付寶交易查詢接口要使用AlipayTradeQueryResponse來獲取解析后響應的結果

17、win10上沒法部署uwsgi,可以通過其它方式間接使用uwsgi,但沒必要,最終都是要部署在Linux系統。

18、Django網站響應慢(修改數據庫查詢方式)

19、指定表名,不用一定和應用名相連,class Meta: ?db_table = '表名'

20、發布網站時需要在settings.py中把debug改為false,allowed_host = ['*']

21、cookie:是由服務器生成,存儲在瀏覽器端的一小段文本信息。

22、session

23、模板中的自定義過濾器參數只能是一個或倆個

24、csrf防護(跨站請求偽造攻擊):

25、url反向解析:


1、項目來源及開發介紹

修改后的天天生鮮(Django4.0版本):

GitHub - LiXZe/dailyfreshContribute to LiXZe/dailyfresh development by creating an account on GitHub.https://github.com/LiXZe/dailyfresh

1.1、天天生鮮介紹

? ? 首先,此次天天生鮮項目來源是黑馬程序員Python就業班的Web開發項目,雖是幾年前的項目,但在Python商城項目里也算是功能比較齊全,什么異步任務處理、頁面靜態化、搜索引擎、高并發庫存問題、郵件發送激活、購物車緩存、分布式文件存儲、服務器部署、支付寶支付接口調用這些都有,適合拿來學習Python的Web開發練練手,此篇博客的內容也是我對原先項目筆記的修改,有些圖片是原先黑馬項目的圖片。

? ? 其次,原先天天生鮮項目使用的是Django1.8.2版本,我自己改成了新版本Django4.0版本的,使用的虛擬環境是anaconda,不論學習什么語言和框架,一定要會看框架對應的開發文檔,從1.8.2到4.0版本的變動挺多的,就需要自己去查看Django4.0的開發文檔,附上Django4.0中文的開發文檔對應鏈接:

Django 文檔 | Django 文檔 | Djangohttps://docs.djangoproject.com/zh-hans/4.0/

? ? 最后文末,是我在開發過程中遇到的各種問題,然后我就記了下來,附上了我的解決方法(由于我這個項目是在win10環境下寫的,所以遇到的問題會更多。

先上網站運行后截圖:

首先是主頁:

用戶中心的3個功能頁面:

?

?

?

?購物車頁面:

?

訂單支付:

?

商品詳情:

?

?

?

1.2、Web開發流程介紹

在正式學習商城項目開發前,先介紹一下常見Web商城項目的開發流程:

1.2.1、需求分析

1.2.1.1、用戶模塊

1.注冊頁

  • 注冊時校驗用戶名是否已被注冊。
  • 完成用戶信息的注冊。
  • 給用戶的注冊郵箱發送郵件,用戶點擊郵件中的激活鏈接完成用戶賬戶的激活。

2.登錄頁

  • 實現用戶的登錄功能。

3.用戶中心

  • 用戶中心信息頁:顯示登錄用戶的信息,包括用戶名、電話和地址,同時頁面下方顯示出用戶最近瀏覽的商品信息。
  • 用戶中心地址頁:顯示登錄用戶的默認收件地址,頁面下方的表單可以新增用戶的收貨地址。
  • 用戶中心訂單頁:顯示登錄用戶的訂單信息。

4.其他

  • 如果用戶已經登錄,頁面頂部顯示登錄用戶的信息。

1.2.1.2、商品相關

1.首頁

  • 動態指定首頁輪播商品信息。
  • 動態指定首頁活動信息。
  • 動態獲取商品的種類信息并顯示。
  • 動態指定首頁顯示的每個種類的商品(包括圖片商品和文字商品)。
  • 點擊某一個商品時跳轉到商品的詳情頁面。

2.商品詳情頁

  • 顯示出某個商品的詳情信息。
  • 頁面的左下方顯示出該種類商品的2個新品信息。

3.商品列表頁

  • 顯示出某一個種類商品的列表數據,分頁顯示并支持按照默認、價格、和人氣進行排序。
  • 頁面的左下方顯示出該種類商品的2個新品信息。

4.其他

  • 通過頁面搜索框搜索商品信息。

1.2.1.3、購物車相關

  • 列表頁和詳情頁將商品添加到購物車。
  • 用戶登錄后,首頁,詳情頁,列表頁顯示登錄用戶購物車中商品的數目。
  • 購物車頁面:對用戶購物車中商品的操作。如選擇某件商品,增加或減少購物車中商品的數目。

1.2.1.4、訂單相關

  • 提交訂單頁面:顯示用戶準備購買的商品信息。
  • 點擊提交訂單完成訂單的創建。
  • 用戶中心訂單頁顯示用戶的訂單信息。
  • 點擊支付完成訂單的支付。

1.2.2、項目架構概覽

1.2.2.1、頁面圖:

1.2.2.2、功能圖:

1.2.2.3、部署圖:

1.2.3、SKU與SPU概念

? ? SPU = Standard Product Unit (標準產品單位)

? ? SPU 是商品信息聚合的最小單位,是一組可復用、易檢索的標準化信息的集合,該集合描述 了一個產品的特性。通俗點講,屬性值、特性相同的商品就可以稱為一個 SPU。 例如:iphone7 就是一個 SPU,與商家,與顏色、款式、套餐都無關。

? ? SKU=stock keeping unit(庫存量單位)

? ? SKU 即庫存進出計量的單位, 可以是以件、盒、托盤等為單位。 SKU 是物理上不可分割的最小存貨單元。在使用時要根據不同業態,不同管理模式來處理。 在服裝、鞋類商品中使用最多最普遍。 例如:紡織品中一個 SKU 通常表示:規格、顏色、款式。

2、項目架構

3、數據庫表結構

4、用戶認證模型

5、類視圖

將視圖view以類的形式定義

通用類視圖基類:

django.views.generic.View? ( 與django.views.generic.base.View 是同一個)

urls.py中配置路由使用類視圖的as_view()方法

由dispatch()方法具體將請求request分發至對應請求方式的處理方法中(get、post等)

類視圖資料:內置基于類的視圖 API | Django 文檔 | Django

6、用戶模塊開發

6.1、Django4.0認證系統文檔

django-admin 和 manage.py | Django 文檔 | Django

方法名

備注

create_user

創建用戶

authenticate

登錄驗證

login

記錄登錄狀態

logout

退出用戶登錄

is_authenticated

判斷用戶是否登錄

login_required裝飾器

進行登錄判斷

6.2、用戶注冊

6.2.1、django發送郵件

?6.2.2、Celery異步任務隊列

6.2.2.1、使用

在項目根目錄下新建一個celery_task文件夾,文件夾下新建一個task文件:

# 使用celery from django.core.mail import send_mail from django.conf import settings from celery import Celery import time from django.template import loader# 在任務處理者一端加這幾句 import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh.settings") django.setup()from goods.models import GoodsType, IndexGoodsBanner, IndexPromotionBanner, IndexTypeGoodsBanner# 創建一個Celery類的實例對象 app = Celery('celery_tasks.tasks', broker='redis://mast:yourpassword@127.0.0.1:6379/1') # 使用我本地電腦的redis的1號數據庫當做celery的broker,

此段代碼在注冊視圖函數中,異步實現發送郵件功能:

# 發送激活郵件,包含激活鏈接: http://127.0.0.1:8000/user/active/token值# 激活鏈接中需要包含用戶的身份信息, 并且要把身份信息進行加密"""加密用戶的身份信息,生成激活token,使用authlib代替itsdangerous"""data = {'confirm': user.id}header = {'alg': 'HS256'} # 簽名算法token = jwt.encode(header=header, payload=data, key=settings.SECRET_KEY) # byte類型token = token.decode()# 發送郵箱:使用celery異步處理發送郵件,加入到任務隊列(broker)中send_register_active_email.delay(email, username, token)# 返回應答, 跳轉到首頁return redirect(reverse('goods:index'))

6.2.2.2、發出任務

6.2.2.3、啟動worker

重新打開一個終端,輸入命令celery -A celery_tasks.tasks worker -l info --pool solo

6.3、用戶激活

使用authlib加密用戶的身份信息,而不是使用原先項目自帶的itsdangerous。

authlib的使用:

參考資料:

JSON Web Token (JWT) — Authlib 1.0.1 documentation

6.3.1、加密用戶身份信息

from authlib.jose import jwt, JoseError"""加密用戶的身份信息,生成激活token,使用authlib代替itsdangerous"""data = {'confirm': user.id}header = {'alg': 'HS256'} # 簽名算法token = jwt.encode(header=header, payload=data, key=settings.SECRET_KEY) # byte類型token = token.decode()

6.3.2、解密用戶身份信息驗證

# 進行解密,獲取要激活的用戶信息try:info = jwt.decode(token, settings.SECRET_KEY)print(info)# 獲取待激活用戶的iduser_id = info['confirm']# 根據id獲取用戶信息user = User.objects.get(id=user_id)user.is_active = 1user.save()

6.4、用戶登錄

6.4.1、配置redis作為Django緩存和session后端

django-redis文檔:django-redis 中文文檔 — Django-Redis 4.7.0 文檔

配置:

# Django的緩存配置 CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379/2", # 使用2號數據庫"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient","PASSWORD": "yourpassword"}} }

6.4.2、登錄判斷裝飾器login_required

可以在用戶模塊的地址配置直接使用:

urlpatterns = [path('register/', RegisterView.as_view(), name='register'), # 使用視圖類re_path(r'active/(?P<token>.*)$', ActiveView.as_view(), name='active'), # 激活用戶path('login/', LoginView.as_view(), name='login'),path('', UserInfoView.as_view(), name='user'), # 用戶信息-中心re_path(r'order/(?P<page>\d+)$', UserOrderView.as_view(), name='order'), # 用戶信息-訂單path('address/', AddressView.as_view(), name='address'), # 用戶信息-地址path('logout/', LogoutView.as_view(), name='logout'), # 用戶注銷path('testcelery/', views.testCelery)# path('', login_required(UserInfoView.as_view()), name='user'), # 用戶信息-中心# path('order/', login_required(UserOrderView.as_view()), name='order'), # 用戶信息-訂單# path('address/', login_required(AddressView.as_view()), name='address'), # 用戶信息-地址# path('register', views.register, name='register'),# path('register_handle', views.register_handle, name='register_handle') ]

或者修改類LoginRequireMixin中的父類的as_view方法:

在setting.py中配置未登錄時的跳

6.5、用戶退出

logout函數清除登錄用戶的session信息。

6.6、用戶地址

模型類和模型管理器類:

6.7、用戶個人信息

6.7.1 redis存儲歷史瀏覽記錄分析

?

參考資料:

Redis 命令參考 — Redis 命令參考

Welcome to redis-py’s documentation! — redis-py dev documentation

6.7.2、django-redis獲取redis鏈接

from django_redis import get_redis_connectionon = get_redis_connection('default') # 使用setting.py中默認的redis配置

7、商品模塊開發

7.1、了解FastDFS分布式文件系統

(我個人是把FastDFS部署在自己的阿里云服務器上,安裝到云服務器上去訪問是也遇到了很多問題,文末總結問題)

集群

啟動FastDFS的方法,需要的操作:

修改如下的配置文件? (在/etc/fdfs目錄中)

?

tracker_server=己的ip:22122

啟動tracker、storage、nginx服務:

sudo service fdfs_trackerd start

sudo service fdfs_storaged start

sudo /usr/local/nginx/sbin/nginx

執行如下命令測試是否成功

fdfs_upload_file /etc/fdfs/client.conf 要上片文件

如果返回類似group1/M00/00/00/rBIK6VcaP0aARXXvAAHrUgHEviQ394.jpg的文件id則說明文件上傳成功

在瀏覽器中可以用?? 127.0.0.1:8888/返回的文件id??

訪問圖片

7.2、python對接fastdfs

(根據視頻提供的方法這一步也有出現一些問題,但還是給出,文末總結解決問題)

1. workon django_py3

2. 進入fdfs_client-py-master.zip所在目錄

3. pip install fdfs_client-py-master.zip

7.3、項目上傳圖片和使用圖片流程

?

海量存儲,存儲容量擴展方便。

文件內容重復。

結合nginx提高網站訪問圖片的效率。

7.4、Django二次開發對接FastDFS

配置文件settings中加入如下配置:

# 設置Django的文件存儲類 DEFAULT_FILE_STORAGE = 'utils.fdfs.storage.FDFSStorage'# 設置fdfs使用的client.conf配置文件 FDFS_CLIENT_CONF = r'D:\django4.0\dailyfresh\utils\fdfs\client.conf'# 設置我的阿里云服務器上的fdfs的nginx的IP和端口號 FDFS_URL = 'http://121.89.236.181:8888/'

創建utils/fdfs 目錄:

storage.py文件中自定義文件存儲類:

from django.core.files.storage import Storage from fdfs_client.client import Fdfs_client from dailyfresh import settingsclass FDFSStorage(Storage):"""自定義fastdfs文件存儲類"""def __init__(self, client_conf=None, base_url=None):"""動態地對storage類進行配置"""if client_conf is None:client_conf = settings.FDFS_CLIENT_CONFself.client_conf = client_confif base_url is None:base_url = settings.FDFS_URLself.base_url = base_urldef save(self, name, content, max_length=None): # 一定需要添加參數max_length"""name為上傳文件的名字,content包含上傳文件內容的File對象"""client = Fdfs_client(self.client_conf)# 上傳文件到我的阿里云服務器fastdfs系統中result = client.upload_by_buffer(content.read())if result.get('Status') != 'Upload successed.':# 上傳失敗raise Exception('上傳文件到云服務器的fastdfs系統失敗!')filename = result.get('Remote file_id')# save方法最后返回的內容為保存在django系統中的圖片文件名稱return filenamedef url(self, name):"""返回訪問文件的url地址,name為保存在django數據表中的為文件名"""return self.base_url + name

7.5、商品首頁

7.5.1、基本頁面處理

動態給對象增加屬性

7.5.2、保存購物車信息的數據設計

7.5.3 頁面靜態化

把原本動態的頁面處理結果保存成html文件,讓用戶直接訪問這個生成出來的靜態的html頁面

配置在管理員頁面的方法中:

from django.contrib import admin from goods.models import GoodsType, IndexTypeGoodsBanner, IndexPromotionBanner, IndexGoodsBanner, GoodsSKU from django.core.cache import cacheclass BaseModelAdmin(admin.ModelAdmin):def save_model(self, request, obj, form, change):"""新增或更新表中的數據時調用"""super().save_model(request, obj, form, change)# 發出任務,讓celery worker重新生成首頁靜態頁from celery_tasks.tasks import generate_static_index_htmlgenerate_static_index_html.delay()# 清除首頁的緩存數據cache.delete('index_page_data')def delete_model(self, request, obj):"""刪除表中的數據時調用"""super().delete_model(request, obj)# 發出任務,讓celery worker重新生成首頁靜態頁from celery_tasks.tasks import generate_static_index_htmlgenerate_static_index_html.delay()# 清除首頁的緩存數據cache.delete('index_page_data')

7.5.4、使用緩存

將處理計算的結果先臨時保存起來,下次使用的時候可以先直接使用,如果沒有這個備份的數據,重新進行計算處理

將緩存數據保存在內存中 (本項目中保存在redis中)

cache

修改了數據庫的數據,直接刪除緩存

緩存要設置有效期

7.6、商品詳情頁

添加歷史瀏覽記錄:

# 獲取用戶購物車中商品的數目user = request.usercart_count = 0if user.is_authenticated:# 用戶已登錄conn = get_redis_connection('default')cart_key = 'cart_%d' % user.idcart_count = conn.hlen(cart_key)# 添加用戶的歷史記錄conn = get_redis_connection('default')history_key = 'history_%d' % user.id# 移除列表中的goods_idconn.lrem(history_key, 0, goods_id)# 把goods_id插入到列表的左側conn.lpush(history_key, goods_id)# 只保存用戶最新瀏覽的5條信息conn.ltrim(history_key, 0, 4)

7.7、商品列表頁

7.7.1、分頁

django有自帶的分頁器:分頁 | Django 文檔 | Django

?

7.7.2、頁碼控制

7.8、商品搜索

7.8.1、安裝和配置

(在win10環境下安裝haystack、whoosh和jieba會遇到bug)

安裝python包。

pip install django-haystack

pip install whoosh

在settings.py文件中注冊應用haystack并做如下配置:

# 全文檢索框架的配置 HAYSTACK_CONNECTIONS = {'default': {# 使用whoosh引擎# 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine','ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine', # 使用自定義用jieba中文分詞庫修改過的引擎# 索引文件路徑'PATH': Path.joinpath(BASE_DIR, 'whoosh_index'),} }

7.8.2、索引文件生成

在goods應用目錄下新建一個search_indexes.py文件,在其中定義一個商品索引類:

在templates下面新建目錄search/indexes/goods:

在此目錄下面新建一個文件goodssku_text.txt并編輯內容如下:

?

使用命令生成索引文件。

????? python manage.py rebuild_index

7.8.3、全文檢索的使用

1) 在項目目錄下的url文件配置url:

urlpatterns = [path('admin/', admin.site.urls),# 做url反向解析,動態獲取url地址。namespace是放在include里面,include還需要傳一個元組,元組內還要有app_name。path('user/', include(('user.urls', 'user'), namespace='user')), # 用戶模塊path('cart/', include(('cart.urls', 'cart'), namespace='cart')), # 購物車模塊path('order/', include(('order.urls', 'order'), namespace='order')), # 訂單模塊path('tinymce/', include('tinymce.urls')), # 富文本編輯器path('search/', include('haystack.urls')), # 全文檢索框架path('', include(('goods.urls', 'goods'), namespace='goods')), # 商品模塊,作為主頁 ]

2)表單搜索時設置表單內容如下:

點擊標題進行提交時,會通過haystack搜索數據。

全文檢索結果:

搜索出結果后,haystack會把搜索出的結果傳遞給templates/search目錄下的search.html,傳遞的上下文包括:

query:搜索關鍵字

page:當前頁的page對象 –>遍歷page對象,獲取到的是SearchResult類的實例對象,對象的屬性object才是模型類的對象。

paginator:分頁paginator對象

通過HAYSTACK_SEARCH_RESULTS_PER_PAGE 可以控制每頁顯示數量。

7.8.4、改變分詞方式

1) 安裝jieba分詞模塊。

pip install jieba

去到conda虛擬環境下python的下的haystack目錄(因為我是用pip install安裝的包):

/home/python/.virtualenvs/bj17_py3/lib/python3.5/site-packages/haystack/backends/

在上面的目錄中創建ChineseAnalyzer.py文件:

import jiebafrom whoosh.analysis import Tokenizer, Token class ChineseTokenizer(Tokenizer): ?def __call__(self, value, positions=False, chars=False, ?keeporiginal=False, removestops=True, ?start_pos=0, start_char=0, mode='', **kwargs): ?t = Token(positions, chars, removestops=removestops, mode=mode, **kwargs) ?seglist = jieba.cut(value, cut_all=True) ?for w in seglist: ?t.original = t.text = w ?t.boost = 1.0 ?if positions: ? t.pos = start_pos + value.find(w) ?if chars: ?t.startchar = start_char + value.find(w) ?t.endchar = start_char + value.find(w) + len(w) ?yield t def ChineseAnalyzer(): ?return ChineseTokenizer()

復制whoosh_backend.py文件,改為如下名稱。

??????? whoosh_cn_backend.py

打開復制出來的新文件,引入中文分析類,內部采用jieba分詞。

from .ChineseAnalyzer import ChineseAnalyzer

更改詞語分析類:

查找 analyzer=StemmingAnalyzer() 改為 analyzer=ChineseAnalyzer()

修改settings.py文件中的配置項。

重新創建索引數據

python manage.py rebuild_index

8、購物車模塊開發

8.1、添加到購物車

確定前端是否傳遞數據,傳遞什么數據,什么格式

確定前端訪問的方式(get? post)

確定返回給前端的什么數據,什么格式

8.2、購物車頁面

購物車頁面js

Jquery選擇器參考資料:

http://www.w3school.com.cn/jquery/jquery_ref_selectors.asp

8.3、購物車記錄更新

采用ajax post請求

前端需要傳遞的參數:商品id(sku_id) 商品數量(count)

8.4、購物車記錄刪除

采用ajax post請求

前端需要傳遞的參數:商品id(sku_id)

9、訂單模塊開發

9.1、提交訂單頁面

9.2、訂單生成

9.2.1、mysql事務

  • 事務概念

一組mysql語句,要么執行,要么全不不執行。

事務的特點

1原子性:一組事務,要么成功;要么撤回。

2穩定性 有非法數據(外鍵約束之類),事務撤回。

3隔離性事務獨立運行。一個事務處理后的結果,影響了其他事務,那么其他事務會撤回。事務的100%隔離,需要犧牲速度。

4可靠性:軟、硬件崩潰后,InnoDB數據表驅動會利用日志文件重構修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 選項 決定什么時候吧事務保存到日志里。

  • 事務控制語句

BEGINSTART TRANSACTION;顯式地開啟一個事務;

COMMIT;也可以使用COMMIT WORK,不過二者是等價的。COMMIT會提交事務,并使已對數據庫進行的所有修改稱為永久性的;

ROLLBACK;有可以使用ROLLBACK WORK,不過二者是等價的。回滾會結束用戶的務,并撤銷正在進行的所有未提交的修改;

SAVEPOINT identifierSAVEPOINT允許在事務中創建一個保存點,一個事務中可以有多個SAVEPOINT

RELEASE SAVEPOINT identifier;刪除一個事務的保存點,當沒有指定的保存點時,執行該語句會拋出一個異常;

ROLLBACK TO identifier;把事務回滾到標記點;

  • mysql事務隔離級別

SQL標準定義了4類隔離級別,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的并發處理,并擁有更低的系統開銷。
Read Uncommitted(讀取未提交內容)

在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離級別很少用于實際應用,因為它的性能也不比其他級別好多少。讀取未提交的數據,也被稱之為臟讀(Dirty Read)。
Read Committed(讀取提交內容)

這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。這種隔離級別 也支持所謂的不可重復讀(Nonrepeatable Read),因為同一事務的其他實例在該實例處理其間可能會有新的commit,所以同一select可能返回不同結果。
Repeatable Read(可重讀)

?????? 這是MySQL的默認事務隔離級別,它確保同一事務的多個實例在并發讀取數據時,會看到同樣的數據行。不過理論上,這會導致另一個棘手的問題:幻讀 Phantom Read)。簡單的說,幻讀指當用戶讀取某一范圍的數據行時,另一個事務又在該范圍內插入了新行,當用戶再讀取該范圍的數據行時,會發現有新的幻影行。InnoDBFalcon存儲引擎通過多版本并發控制(MVCCMultiversion Concurrency Control)機制解決了該問題。

Serializable(可串行化)?
?????? 這是最高的隔離級別,它通過強制事務排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。

?????????這四種隔離級別采取不同的鎖類型來實現,若讀取的是同一個數據的話,就容易發生問題。例如:

臟讀(Drity Read)某個事務已更新一份數據,另一個事務在此時讀取了同一份數據,由于某些原因,前一個RollBack了操作,則后一個事務所讀取的數據就會是不正確的。

不可重復讀(Non-repeatable read):在一個事務的兩次查詢之中數據不一致,這可能是兩次查詢過程中間插入了一個事務更新的原有的數據。

幻讀(Phantom Read):在一個事務的兩次查詢中數據筆數不一致,例如有一個事務查詢了幾行(Row)數據,而另一個事務卻在此時插入了新的幾行數據,先前的事務在接下來的查詢中,就會發現有幾行數據是它先前所沒有的。

  • 設置mysql事務的隔離級別

打開mysql配置文件: sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf, 添加如下行。

保存配置文件,重啟mysql服務。

????? sudo service mysql restart

9.2.2、訂單并發處理

9.2.1.1、悲觀鎖

select * from df_goods_sku where id=17 for update;

?

訂單創建(悲觀鎖):

# 前端傳遞的參數:地址id(addr_id) 支付方式(pay_method) 用戶要購買的商品id字符串(sku_ids) # mysql事務: 一組sql操作,要么都成功,要么都失敗 # 高并發:秒殺 # 支付寶支付 class OrderCommitView1(View):"""訂單創建(悲觀鎖版本)"""@transaction.atomicdef post(self, request):"""訂單創建"""# 判斷用戶是否登錄user = request.userif not user.is_authenticated:# 用戶未登錄return JsonResponse({'res': 0, 'errmsg': '用戶未登錄'})# 接收參數addr_id = request.POST.get('addr_id')pay_method = request.POST.get('pay_method')sku_ids = request.POST.get('sku_ids') # 1,3# 校驗參數if not all([addr_id, pay_method, sku_ids]):return JsonResponse({'res': 1, 'errmsg': '參數不完整'})# 校驗支付方式if pay_method not in OrderInfo.PAY_METHODS.keys():return JsonResponse({'res': 2, 'errmsg': '非法的支付方式'})# 校驗地址try:addr = Address.objects.get(id=addr_id)except ObjectDoesNotExist:# 地址不存在return JsonResponse({'res': 3, 'errmsg': '地址非法'})# todo: 創建訂單核心業務# 組織參數# 訂單id: 20171122181630+用戶idorder_id = datetime.now().strftime('%Y%m%d%H%M%S') + str(user.id)# 運費transit_price = 10# 總數目和總金額total_count = 0total_price = 0# 設置事務保存點save_id = transaction.savepoint()try:# todo: 向df_order_info表中添加一條記錄order = OrderInfo.objects.create(order_id=order_id,user=user,addr=addr,pay_method=pay_method,total_count=total_count,total_price=total_price,transit_price=transit_price)# todo: 用戶的訂單中有幾個商品,需要向df_order_goods表中加入幾條記錄conn = get_redis_connection('default')cart_key = 'cart_%d' % user.idsku_ids = sku_ids.split(',')for sku_id in sku_ids:# 獲取商品的信息try:# select * from df_goods_sku where id=sku_id for update;(加鎖)sku = GoodsSKU.objects.select_for_update().get(id=sku_id)except:# 商品不存在transaction.savepoint_rollback(save_id)return JsonResponse({'res': 4, 'errmsg': '商品不存在'})print('user:%d stock:%d' % (user.id, sku.stock))# import time# time.sleep(10)# 從redis中獲取用戶所要購買的商品的數量count = conn.hget(cart_key, sku_id)# todo: 判斷商品的庫存if int(count) > sku.stock:transaction.savepoint_rollback(save_id)return JsonResponse({'res': 6, 'errmsg': '商品庫存不足'})# todo: 向df_order_goods表中添加一條記錄OrderGoods.objects.create(order=order,sku=sku,count=count,price=sku.price)# todo: 更新商品的庫存和銷量sku.stock -= int(count)sku.sales += int(count)sku.save()# todo: 累加計算訂單商品的總數量和總價格amount = sku.price * int(count)total_count += int(count)total_price += amount# todo: 更新訂單信息表中的商品的總數量和總價格order.total_count = total_countorder.total_price = total_priceorder.save()except Exception as e:transaction.savepoint_rollback(save_id)return JsonResponse({'res': 7, 'errmsg': '下單失敗'})# 提交事務transaction.savepoint_commit(save_id)# todo: 清除用戶購物車中對應的記錄conn.hdel(cart_key, *sku_ids)# 返回應答return JsonResponse({'res': 5, 'message': '創建成功'})

悲觀鎖獲取數據時對數據行了鎖定,其他事務要想獲取鎖,必須等原事務結束。

  • 9.2.2.2、樂觀鎖

查詢時不鎖數據,提交更改時進行判斷.

update df_goods_sku set stock=0, sales=1 where id=17 and stock=1;

沖突比較少的時候,使用樂觀鎖。

沖突比較多的時候,使用悲觀鎖。

訂單創建(樂觀鎖):

class OrderCommitView(View):"""訂單創建(樂觀鎖版本)"""@transaction.atomicdef post(self, request):"""訂單創建"""# 判斷用戶是否登錄user = request.userif not user.is_authenticated:# 用戶未登錄return JsonResponse({'res': 0, 'errmsg': '用戶未登錄'})# 接收參數addr_id = request.POST.get('addr_id')pay_method = request.POST.get('pay_method')sku_ids = request.POST.get('sku_ids') # 1,3# 校驗參數if not all([addr_id, pay_method, sku_ids]):return JsonResponse({'res': 1, 'errmsg': '參數不完整'})# 校驗支付方式if pay_method not in OrderInfo.PAY_METHODS.keys():return JsonResponse({'res': 2, 'errmsg': '非法的支付方式'})# 校驗地址try:addr = Address.objects.get(id=addr_id)except ObjectDoesNotExist:# 地址不存在return JsonResponse({'res': 3, 'errmsg': '地址非法'})# todo: 創建訂單核心業務# 組織參數# 訂單id: 20171122181630+用戶idorder_id = datetime.now().strftime('%Y%m%d%H%M%S') + str(user.id)# 運費transit_price = 10# 總數目和總金額total_count = 0total_price = 0# 設置事務保存點save_id = transaction.savepoint()try:# todo: 向df_order_info表中添加一條記錄order = OrderInfo.objects.create(order_id=order_id,user=user,addr=addr,pay_method=pay_method,total_count=total_count,total_price=total_price,transit_price=transit_price)# todo: 用戶的訂單中有幾個商品,需要向df_order_goods表中加入幾條記錄conn = get_redis_connection('default')cart_key = 'cart_%d' % user.idsku_ids = sku_ids.split(',')for sku_id in sku_ids:for i in range(3):# 獲取商品的信息try:sku = GoodsSKU.objects.get(id=sku_id)except:# 商品不存在transaction.savepoint_rollback(save_id)return JsonResponse({'res': 4, 'errmsg': '商品不存在'})# 從redis中獲取用戶所要購買的商品的數量count = conn.hget(cart_key, sku_id)# todo: 判斷商品的庫存if int(count) > sku.stock:transaction.savepoint_rollback(save_id)return JsonResponse({'res': 6, 'errmsg': '商品庫存不足'})# todo: 更新商品的庫存和銷量orgin_stock = sku.stocknew_stock = orgin_stock - int(count)new_sales = sku.sales + int(count)# print('user:%d times:%d stock:%d' % (user.id, i, sku.stock))# import time# time.sleep(10)# update df_goods_sku set stock=new_stock, sales=new_sales# where id=sku_id and stock = orgin_stock# 返回受影響的行數res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales)if res == 0:if i == 2:# 嘗試的第3次transaction.savepoint_rollback(save_id)return JsonResponse({'res': 7, 'errmsg': '下單失敗2'})continue# todo: 向df_order_goods表中添加一條記錄OrderGoods.objects.create(order=order,sku=sku,count=count,price=sku.price)# todo: 累加計算訂單商品的總數量和總價格amount = sku.price * int(count)total_count += int(count)total_price += amount# 跳出循環break# todo: 更新訂單信息表中的商品的總數量和總價格order.total_count = total_countorder.total_price = total_priceorder.save()except Exception as e:transaction.savepoint_rollback(save_id)return JsonResponse({'res': 7, 'errmsg': '下單失敗'})# 提交事務transaction.savepoint_commit(save_id)# todo: 清除用戶購物車中對應的記錄conn.hdel(cart_key, *sku_ids)# 返回應答return JsonResponse({'res': 5, 'message': '創建成功'})

9.3、用戶中心-訂單頁

9.4、訂單支付

調用支付寶的統一收單下單并支付頁面接口:
統一收單下單并支付頁面接口 | 網頁&移動應用

然后還需要調用支付寶的收單線下交易查詢接口:

統一收單線下交易查詢接口 | 網頁&移動應用

其中需要用到的商家和買家賬戶信息都在沙箱系統中:

支付寶開放平臺

?

?

10、項目部署

10.1、uwsgi

遵循wsgi協議的web服務器。

10.1.1、uwsgi的安裝

?????? pip install uwsgi

10.1.2、uwsgi的配置

項目部署時,需要修改settings.py文件夾下的:

DEBUG=FALSE

ALLOWED_HOSTS=[‘*’]

uwsgi文件:

[uwsgi] #使用nginx連接時使用 socket=127.0.0.1:8080 #直接做web服務器使用 python manage.py runserver ip:port #http=127.0.0.1:8080 #項目目錄 chdir=D:\django4.0\dailyfresh #項目中wsgi.py文件的目錄,相對于項目目錄 wsgi-file=dailyfresh/wsgi.py #指定啟動的工作進程數 processes=4 #指定工作進程中的線程數 threads=2 master=True #保存啟動之后主進程的pid pidfile=uwsgi.pid #設置uwsgi后臺運行,uwsgi.log保存日志信息 daemonize=uwsgi.log #設置虛擬環境的路徑 virtualenv=D:\Anaconda3\envs\dailyfresh

10.1.3、uwsgi的啟動和停止

啟動:uwsgi –-ini 配置文件路徑 例如:uwsgi –-ini uwsgi.ini

停止:uwsgi --stop uwsgi.pid路徑 例如:uwsgi –-stop uwsgi.pid

10.2、nginx

10.2.1、nginx 配置轉發請求給uwsgi

location / {

???????? include uwsgi_params;

???????? uwsgi_pass uwsgi服務器的ip:port;

}

10.2.2、nginx配置處理靜態文件

django settings.py中配置收集靜態文件路徑:

STATIC_ROOT=收集的靜態文件路徑 例如:/var/www/dailyfresh/static;

django 收集靜態文件的命令:

?????? python manage.py collectstatic

執行上面的命令會把項目中所使用的靜態文件收集到STATIC_ROOT指定的目錄下。

收集完靜態文件之后,nginx提供靜態文件,需要在nginx配置文件中增加如下配置:

location /static {

???????? alias /var/www/dailyfresh/static/;

}

10.2.3 nginx轉發請求給另外地址

在location 對應的配置項中增加 proxy_pass 轉發的服務器地址。

如當用戶訪問127.0.0.1時,在nginx中配置把這個請求轉發給172.16.179.131:80(nginx)服務器,讓這臺服務器提供靜態首頁。

配置如下:

location = /{

???????? proxy_pass http://172.16.179.131;

}

10.2.4、nginx配置upstream實現負載均衡

ngnix 配置負載均衡時,在server配置的前面增加upstream配置項。

upstream dailyfresh {

???????? server 127.0.0.1:8080;

???????? server 127.0.0.1:8081;

}

10.2.5、部署項目流程圖

  • 項目總結
  • 生鮮類產品? B2C? PC電腦端網頁
  • 功能模塊:用戶模塊? 商品模塊(首頁、 搜索、商品) 購物車模塊? 訂單模塊(下單、 支付)
  • 用戶模塊:注冊、登錄、激活、退出、個人中心、地址
  • 商品模塊:首頁、詳情、列表、搜索(haystack+whoosh
  • 購物車: 增加、刪除、修改、查詢
  • 訂單模塊:確認訂單頁面、提交訂單(下單)請求支付查詢支付結果評論
  • django默認的認證系統 AbstractUser
  • itsdangerous? 生成簽名的token (序列化工具 dumps? loads)
  • 郵件 (django提供郵件支持 配置參數? send_mail)
  • ?celery (重點 ?整體認識 異步任務)
  • ?頁面靜態化 (緩解壓力? celery? nginx
  • ?緩存(緩解壓力, 保存的位置、有效期、與數據庫的一致性問題)
  • ?FastDFS (分布式的圖片存儲服務, 修改了django的默認文件存儲系統)
  • ?搜索( whoosh? 索引? 分詞)
  • ?購物車redis 哈希 歷史記錄redis list
  • ?ajax 前端用ajax請求后端接口
  • ?事務
  • ?高并發的庫存問題 (悲觀鎖、樂觀鎖
  • ?支付的使用流程
  • ?nginx (負載均衡? 提供靜態文件)
  • 11、開發過程中遇到的bug個人總結

    1、在模板中載入靜態文件前需要在配置文件settings.py中加入配置

    2、django新版本中查詢數據庫后對象不存在報錯的異常類是core.exceptions的ObjectDoesNotExist

    3、加密用戶身份信息當做url的token值,用authlib包代替itsdangerous,可以指定簽名算法HS256

    4、celery不支持window10,安裝eventlet包,使用celery -A celery_tasks.tasks worker -l info -P eventlet -E命令或者建議使用下面的命令就用安裝eventlet包

    用celery生成靜態頁面報django.db.utils.DatabaseError錯誤,改為celery -A celery_tasks.tasks worker -l info --pool solo啟動就不會報錯

    5、django自帶的login()報錯:redis輸入了一個空類型的值

    原因:在setting.py中配置redis(密碼)時不能直接放在url中

    6、Django的authenticate已經包含了is_active判斷,即使用戶名密碼正確,is_active為0也會返回空,所以需要在setting.py中加配置

    7、FastDFS只支持Linux,可以把fastDFS運行在云服務器上,Windows本地電腦上運行FastDFS客戶端fdfs-client-py

    這里我在自己的服務器上改了很久。我目的是為了把FastDFS安裝在服務器上,然后自己本地跑代碼測試的時候就就可以從云服務器上把圖片拉下來。配置過程要按下面的來。

    可以把云服務器上FastDFS的storage.conf, client.conf, mod_fastdfs.conf這3個文件的tracker_ip都設為外網IP。
    每次更改后需要對tracker和storage這倆個服務器重啟,已經重啟nginx服務,然后fdfs_monitor /etc/fdfs/client.conf檢查一下監控信息。
    本地上傳圖片到云服務器的FastDFS時的自定義的storage.py代碼文件中,參數為路徑的方法不能是client.upload_by_file或client.upload_by_filename,否則就會報錯。

    然后django上傳圖片到服務器返回來的路徑沒有后綴,所以要用瀏覽器的前端代碼去解析

    8、window啟動nginx,先到文件夾中啟動nginx.exe,再到cmd中輸入nginx.exe。停止nginx,在cmd中完整有序停止用nginx -s quit,再用taskkill /f /t /im nginx.exe

    9、is_authenticated在Django4.0中是一個屬性而不是一個方法

    10、Django4.0已經移除ungettext,所以在引入haystack的時候會報錯,修改haystack文件夾下的admin.py把ungettext三處地方修改為ngettext

    11、Django4.0已經移除smart_text,修改haystack文件夾下的form.py倆處地方把smart_text修改為smart_str

    12、配置為樂觀鎖時,需要設置MySQL默認的隔離級別(可重復讀)為(讀取提交內容),在MySQL配置文件skip-ex,,,下添加一行transaction-isolation = READ-COMMITTED

    13、網站如果想讓支付寶平臺訪問(獲取支付后的結果),需要有公網IP

    14、安裝alipay-sdk-python 提示安裝pycrypto 問題:

    直接先pip install pycryptodome,下載alipay-sdk-python-3.3.398放到Lib\site-packages 的文件目錄下,
    進入到文件中修改 setup.py, 將requires = ["pycrypto","rsa"] 修改成 requires = ["crypto","rsa"]

    15、支付寶沙箱測試不需要重新設置密鑰和公鑰,直接使用默認。

    使用支付寶網頁支付接口一直報錯商戶ID錯誤,原因settle_detail_info.trans_in才是設置商戶ID(PID),而不是設置買家id的
    sub_merchant.merchant_id是設置子商戶商戶id的而不是商戶ID的

    16、使用支付寶交易查詢接口要使用AlipayTradeQueryResponse來獲取解析后響應的結果

    返回的數據就封裝在這個類里,像使用交易支付接口一樣的方法就會報Alipay SDK源代碼出錯

    17、win10上沒法部署uwsgi,可以通過其它方式間接使用uwsgi,但沒必要,最終都是要部署在Linux系統。

    18、Django網站響應慢(修改數據庫查詢方式)

    CONN_MAX_AGE(定義連接的最大生存期的參數)=None設置為無限制的持久連接,避免了在每個請求中重新建立到數據庫的連接的開銷,但是
    除了get方法外,all,filter,exclude,order_by方法都會返回一個查詢集(QuerySet)
    查詢集特性:

    1、惰性查詢:只有在實際使用查詢集中的數據的時候才會發生對數據庫的真正查詢。
    2、緩存:當使用的是同一個查詢集時,第一次使用的時候會發生實際數據庫的查詢,然后把結果緩存起來,之后再使用這個查詢集時,使用的是緩存中的結果。

    19、指定表名,不用一定和應用名相連,class Meta: ?db_table = '表名'

    20、發布網站時需要在settings.py中把debug改為false,allowed_host = ['*']

    21、cookie:是由服務器生成,存儲在瀏覽器端的一小段文本信息。

    設置:HttpResponse('').set_cookie('key', value, max_age=計算秒數,expires=)
    獲取:reques.COOKIE

    22、session

    瀏覽器訪問服務器,在django_session表中設置session信息,表中有個session_key和session_data,session_key這一列是唯一標識碼,session_data這一列是設置的信息。
    然后django拿著這個唯一標識碼設置一個cookie名為sessionid,下次瀏覽器訪問服務器就會根據sessionid的值取出對應session的信息(這一步django會自動幫我們做),放在request.session中。
    設置:request.session['']=
    獲取:request.session['']

    23、模板中的自定義過濾器參數只能是一個或倆個

    模板繼承:所有頁面都相同的內容都放在父模板文件中{% extensds 'booktest/base.html' %}
    父模板需要預留一個塊,{% block 塊名 %} ?{% endblock 塊名 %}?

    24、csrf防護(跨站請求偽造攻擊):

    只針對post請求,在有post請求的模板下加一個模板標簽{% csrf_token %}
    ? ? 1)渲染模板文件時在頁面生成一個名字叫做csrfmiddlewaretoken的隱藏域。
    ? ? 2)服務器交給瀏覽器保存一個名字為csrftoken的cookie信息。
    ? ? 3)提交表單時,兩個值都會發給服務器,服務器進行比對,如果一樣,則csrf驗證通過,否則失敗。

    25、url反向解析:

    某一個url配置的地址發生變化時,頁面上使用反向解析生成地址的位置不需要發生變化。
    ? ? ? from django.urls import reverse

    總結

    以上是生活随笔為你收集整理的天天生鲜(Django4.0版本) + 开发遇到的问题及解决的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。