用戶地址界面:先從用戶地址信息開始實現javascript
定義用戶地址視圖css
class AddressView(View): """用戶地址""" def get(self, request): """提供用戶地址頁面""" return render(request, 'user_center_site.html') def post(self, request): """修改地址信息""" pass
URL正則匹配html
url(r'^address$', views.AddressView.as_view(), name='address'),
問題:java
@login_required
會限制頁面訪問
class LoginView(View): """登錄""" def get(self, request): """響應登錄頁面""" return render(request, 'login.html') def post(self, request): """處理登錄邏輯""" # 獲取用戶名和密碼 user_name = request.POST.get('username') password = request.POST.get('pwd') # 獲取是否勾選'記住用戶名' remembered = request.POST.get('remembered') # 參數校驗 if not all([user_name, password]): return redirect(reverse('users:login')) # django用戶認證系統判斷是否登錄成功 user = authenticate(username=user_name, password=password) # 驗證登錄失敗 if user is None: # 響應登陸頁面,提示用戶名或密碼錯誤 return render(request, 'login.html', {'errmsg':'用戶名或密碼錯誤'}) # 驗證登錄成功,並判斷是不是激活用戶 if user.is_active == False: # 若是不是激活用戶 return render(request, 'login.html', {'errmsg':'用戶未激活'}) # 使用django的用戶認證系統,在session中保存用戶的登錄狀態 login(request, user) # 服務器記錄session後,設置客戶端cookie的過時日期 if remembered != 'on': # 不須要記住cookie信息 request.session.set_expiry(0) else: # 須要記住cookie信息 request.session.set_expiry(None) # 登錄成功,根據next參數決定跳轉方向 next = request.GET.get('next') if next is None: # 若是是直接登錄成功,就重定向到首頁 return redirect(reverse('goods:index')) else: # 若是是用戶中心重定向到登錄頁面,就回到用戶中心 return redirect(next)
request.GET
request.POST
request.user
latest('時間')
函數:按照時間排序,最近的時間在最前,並取出第0個數據,也能夠按照其餘條件排序render()
函數:參數1傳request,因此模板中能夠拿到user對象,不須要在上下文中構造python
class AddressView(LoginRequiredMixin, View): """用戶地址""" def get(self, request): """提供用戶地址頁面:若是驗證失敗重定向到登錄頁面""" # 從request中獲取user對象,中間件從驗證請求中的用戶,因此request中帶有user user = request.user try: # 查詢用戶地址:根據建立時間排序,最近的時間在最前,取第1個地址 # address = Address.objects.filter(user=user).order_by('-create_time')[0] # address = user.address_set.order_by('-create_time')[0] address = user.address_set.latest('create_time') except Address.DoesNotExist: # 若是地址信息不存在 address = None # 構造上下文 context = { # request中自帶user,調用模板時,request會傳給模板 # 'user':user, 'address':address } # return HttpResponse('這是用戶中心地址頁面') return render(request, 'user_center_site.html', context) def post(self, request): """修改地址信息""" pass
class AddressView(LoginRequiredMixin, View)
類的post方法中def post(self, request): """修改地址信息""" # 接收地址表單數據 user = request.user recv_name = request.POST.get("recv_name") addr = request.POST.get("addr") zip_code = request.POST.get("zip_code") recv_mobile = request.POST.get("recv_mobile") # 參數校驗 if all([recv_name, addr, zip_code, recv_mobile]): # address = Address( # user=user, # receiver_name=recv_name, # detail_addr=addr, # zip_code=zip_code, # receiver_mobile=recv_mobile # ) # address.save() # 保存地址信息到數據庫 Address.objects.create( user=user, receiver_name=recv_name, detail_addr=addr, zip_code=zip_code, receiver_mobile=recv_mobile ) return redirect(reverse("users:address"))
base.html
user_center_base.html
發送get請求,獲取我的中心界面 jquery
class UserInfoView(LoginRequiredMixin, View): """用戶中心""" def get(self, request): """查詢用戶信息和地址信息""" # 從request中獲取user對象,中間件從驗證請求中的用戶,因此request中帶有user user = request.user try: # 查詢用戶地址:根據建立時間排序,取第1個地址 address = user.address_set.latest('create_time') except Address.DoesNotExist: # 若是地址信息不存在 address = None # 構造上下文 context = { 'address': address } # 渲染模板 return render(request, 'user_center_info.html', context)
user_center_info.html
繼承自user_center_base.html
{% extends 'user_center_base.html' %} {% load staticfiles %} {% block body %} <div class="main_con clearfix"> <div class="left_menu_con clearfix"> <h3>用戶中心</h3> <ul> <li><a href="{% url 'users:info' %}" class="active">· 我的信息</a></li> <li><a href="user_center_order.html">· 所有訂單</a></li> <li><a href="{% url 'users:address' %}">· 收貨地址</a></li> </ul> </div> <div class="right_content clearfix"> <div class="info_con clearfix"> <h3 class="common_title2">基本信息</h3> <ul class="user_info_list"> <li><span>用戶名:</span>{{ user.username }}</li> <li><span>聯繫方式:</span>{{ address.receiver_mobile }}</li> <li><span>聯繫地址:</span>{{ address.detail_addr }}</li> </ul> </div> <h3 class="common_title2">最近瀏覽</h3> <div class="has_view_list"> <ul class="goods_type_list clearfix"> {# 用於填充瀏覽記錄 #} </ul> </div> </div> </div> {% endblock body %}
history_userid = [sku_id2, sku_id8, sku_id5 , ...]
這裏查詢瀏覽記錄的前提是:已經設計好瀏覽記錄的存儲規則nginx
django-redis
模塊來操做redis數據庫
get_redis_connection
返回一個已經跟Redis鏈接好的連接對象,該對象就能夠直接操做Redisredis_connection = get_redis_connection('default')
提示:settings.py
文件已經配置CACHES
選項的default
git
# 緩存 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://192.168.243.193:6379/5", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } }
瀏覽記錄查詢github
提示:目前沒有瀏覽記錄,因此查詢的是空的瀏覽記錄ajax
class UserInfoView(LoginRequiredMixin, View): """用戶中心""" def get(self, request): """查詢用戶信息和地址信息""" # 從request中獲取user對象,中間件從驗證請求中的用戶,因此request中帶有user user = request.user try: # 查詢用戶地址:根據建立時間排序,取第1個地址 address = user.address_set.latest('create_time') except Address.DoesNotExist: # 若是地址信息不存在 address = None # 建立redis鏈接對象 redis_connection = get_redis_connection('default') # 從Redis中獲取用戶瀏覽商品的sku_id,在redis中須要維護商品瀏覽順序[8,2,5] sku_ids = redis_connection.lrange('history_%s'%user.id, 0, 4) # 從數據庫中查詢商品sku信息,範圍在sku_ids中 # skuList = GoodsSKU.objects.filter(id__in=sku_ids) # 問題:通過數據庫查詢後獲得的skuList,就再也不是redis中維護的順序了,而是[2,5,8] # 需求:保證通過數據庫查詢後,依然是[8,2,5] skuList = [] for sku_id in sku_ids: sku = GoodsSKU.objects.get(id=sku_id) skuList.append(sku) # 構造上下文 context = { 'address':address, 'skuList':skuList, } # 調出並渲染模板 return render(request, 'user_center_info.html', context)
user_center_info.html
模板中補充用戶商品瀏覽記錄數據{% extends 'user_center_base.html' %} {% load staticfiles %} {% block body %} <div class="main_con clearfix"> <div class="left_menu_con clearfix"> <h3>用戶中心</h3> <ul> <li><a href="{% url 'users:info' %}" class="active">· 我的信息</a></li> <li><a href="user_center_order.html">· 所有訂單</a></li> <li><a href="{% url 'users:address' %}">· 收貨地址</a></li> </ul> </div> <div class="right_content clearfix"> <div class="info_con clearfix"> <h3 class="common_title2">基本信息</h3> <ul class="user_info_list"> <li><span>用戶名:</span>{{ user.username }}</li> <li><span>聯繫方式:</span>{{ address.receiver_mobile }}</li> <li><span>聯繫地址:</span>{{ address.detail_addr }}</li> </ul> </div> <h3 class="common_title2">最近瀏覽</h3> <div class="has_view_list"> <ul class="goods_type_list clearfix"> {% for sku in skuList %} <li> {# fastDFS:sku.default_image.url表示存放圖片的主機地址 #} <a href="detail.html"><img src="{{ sku.default_image.url }}"></a> <h4><a href="detail.html">{{ sku.name }}</a></h4> <div class="operate"> <span class="prize">¥{{ sku.price }}</span> <span class="unit">{{ sku.price }}/{{ sku.unit }}</span> <a href="#" class="add_goods" title="加入購物車"></a> </div> </li> {% endfor %} </ul> </div> </div> </div> {% endblock body %}
FastDFS分佈式介紹
FastDFS上傳和下載工做流程介紹
文件檢索的索引介紹
FastDFS分佈式存儲服務器安裝.docx
FastDFS分佈式存儲服務器安裝.docx
FastDFS分佈式存儲服務器安裝.docx
1.程序猿須要配置的文件:client.conf
tracker.conf
storage.conf
nginx.conf
sudo vim /etc/fdfs/tracker.conf sudo vim /etc/fdfs/storage.conf sudo vim /etc/fdfs/client.conf sudo vim /usr/local/nginx/conf/nginx.conf
2.須要啓動的:tracker,storage,nginx
sudo service fdfs_trackerd start 或者 sudo /etc/init.d/fdfs_trackerd start sudo service fdfs_storaged start 或者 sudo /etc/init.d/fdfs_storaged start sudo /usr/local/nginx/sbin/nginx
3.參考課件:FastDFS分佈式存儲服務器安裝.docx
fdfs_client
是Django對接FastDFS的工具包提示: fdfs_client-py-master.zip
已經下載成功,建議使用課件提供的壓縮包
進入到 fdfs_client-py-master.zip 目錄
pip install fdfs_client-py-master.zip
Storage
類
1.自定義文件存儲系統的目錄結構
2.Django項目中,使用client.conf
文件注意點
3.settings.py
中指定文件存儲系統類,指定爲自定義的文件存儲系統類
from django.core.files.storage import Storage class FastDFSStorage(Storage): """自定義Django存儲系統的類""" pass
# 配置Django自定義的存儲系統 DEFAULT_FILE_STORAGE = 'utils.fastdfs.storage.FastDFSStorage'
4.自定義文件存儲系統類代碼實現
from django.core.files.storage import Storage from fdfs_client.client import Fdfs_client from django.conf import settings class FastDFSStorage(Storage): """自定義Django存儲系統的類""" def __init__(self, client_conf=None,server_ip=None): """初始化,設置參數""" if client_conf is None: client_conf = settings.CLIENT_CONF self.client_conf = client_conf if server_ip is None: server_ip = settings.SERVER_IP self.server_ip = server_ip def _open(self, name, mode='rb'): """讀取文件時使用""" pass def _save(self, name, content): """存儲文件時使用:參數2是上傳來的文件名,參數3是上傳來的File對象""" # 建立fdfs客戶端client client = Fdfs_client(self.client_conf) # client獲取文件內容 file_data = content.read() # Django藉助client向FastDFS服務器上傳文件 try: result = client.upload_by_buffer(file_data) except Exception as e: print(e) # 本身調試臨時打印 raise # 根據返回數據,判斷是否上傳成功 if result.get('Status') == 'Upload successed.': # 讀取file_id file_id = result.get('Remote file_id') # 返回給Django存儲起來便可 return file_id else: # 開發工具類時,出現異常不要擅自處理,交給使用者處理 raise Exception('上傳文件到FastDFS失敗') def exists(self, name): """Django用來判斷文件是否存在的""" # 因爲Djnago不存儲圖片,因此永遠返回Fasle,直接保存到FastFDS return False def url(self, name): """用於返回圖片在服務器上完整的地址:server_ip+path""" return self.server_ip + name
5.自定義文件存儲系統代碼優化部分
# FastFDS使用的配置信息 CLIENT_CONF = os.path.join(BASE_DIR, 'utils/fastdfs/client.conf') SERVER_IP = 'http://192.168.243.193:8888/'
需求:使用後臺站點,向goods
應用中的GoodsCategory
模型中發佈內容
步驟:
GoodsCategory
模型中的內容1.本地化
LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai'
2.註冊模型類到後臺站點
from django.contrib import admin from goods.models import GoodsCategory,Goods,GoodsSKU # Register your models here. admin.site.register(GoodsCategory) admin.site.register(Goods) admin.site.register(GoodsSKU)
3.建立超級管理員並登錄進入到後臺站點
python manage.py createsuperuser
4.發佈GoodsCategory
模型中的內容
沒有mutagen和requests模塊
連接FastFDS服務器失敗
從新啓動tracker和storage和nginx便可
sudo service fdfs_trackerd start 或者 sudo /etc/init.d/fdfs_trackerd start sudo service fdfs_storaged start 或者 sudo /etc/init.d/fdfs_storaged start sudo /usr/local/nginx/sbin/nginx
富文本編輯器在後臺站點的展現效果
富文本字段:HTMLField
# 富文本編輯器字段 from tinymce.models import HTMLField
pip install django-tinymce==2.6.0
安裝富文本編輯器應用
INSTALLED_APPS = ( ... 'tinymce', )
settings.py
中添加編輯器配置
TINYMCE_DEFAULT_CONFIG = { 'theme': 'advanced', # 豐富樣式 'width': 600, 'height': 400, }
項目/urls.py中配置編輯器url
import tinymce.urls urlpatterns = [ ... url(r'^tinymce/', include('tinymce.urls')), ]
class IndexView(View): """首頁""" def get(self, request): """查詢首頁頁面須要的數據,構造上下文,渲染首頁頁面""" # 查詢用戶我的信息(request.user) # 查詢商品分類信息 categorys = GoodsCategory.objects.all() # 查詢圖片輪播信息:按照index進行排序 banners = IndexGoodsBanner.objects.all().order_by('index') # 查詢活動信息 promotion_banners = IndexPromotionBanner.objects.all().order_by('index') # 查詢分類商品信息 for category in categorys: title_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=0).order_by('index') category.title_banners = title_banners image_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=1).order_by('index') category.image_banners = image_banners # 查詢購物車信息 cart_num = 0 # 構造上下文:先處理購物車之外的上下文,並緩存 context = { 'categorys':categorys, 'banners':banners, 'promotion_banners':promotion_banners, 'cart_num':cart_num } return render(request, 'index.html',context)
{% extends 'base.html' %} {% block title %}每天生鮮-首頁{% endblock %} {# 刪除<head>,搜索框,底部. 保留body部分進行重寫 #} {% block body %} <div class="navbar_con"> <div class="navbar"> <h1 class="fl">所有商品分類</h1> <ul class="navlist fl"> <li><a href="">首頁</a></li> <li class="interval">|</li> <li><a href="">手機生鮮</a></li> <li class="interval">|</li> <li><a href="">抽獎</a></li> </ul> </div> </div> <div class="center_con clearfix"> <ul class="subnav fl"> {% for category in categorys %} <li><a href="#model0{{ forloop.counter }}" class="{{ category.logo }}">{{ category.name }}</a></li> {% endfor %} </ul> <div class="slide fl"> <ul class="slide_pics"> {% for banner in banners %} {# banner.image.url 獲取輪播模型類圖片屬性,url方法是配置FastDFS服務器提供圖片完整地址的方法 #} <li><a href="#"><img src="{{ banner.image.url }}" alt="幻燈片"></a></li> {% endfor %} </ul> <div class="prev"></div> <div class="next"></div> <ul class="points"></ul> </div> <div class="adv fl"> {% for promotion_banner in promotion_banners %} <a href="{{ promotion_banner.url }}"><img src="{{ promotion_banner.image.url }}"></a> {% endfor %} </div> </div> {% for category in categorys %} <div class="list_model"> <div class="list_title clearfix"> <h3 class="fl" id="model0{{ forloop.counter }}">{{ category.name }}</h3> <div class="subtitle fl"> <span>|</span> {% for title_banner in category.title_banners %} <a href="#">{{ title_banner.sku.name }}</a> {% endfor %} </div> <a href="#" class="goods_more fr" id="fruit_more">查看更多 ></a> </div> <div class="goods_con clearfix"> <div class="goods_banner fl"><img src="{{ category.image.url }}"></div> <ul class="goods_list fl"> {% for image_banner in category.image_banners %} <li> <h4><a href="#">{{ image_banner.sku.name }}</a></h4> <a href="#"><img src="{{ image_banner.sku.default_image.url }}"></a> <div class="prize">¥ {{ image_banner.sku.price }}</div> </li> {% endfor %} </ul> </div> </div> {% endfor %} {% endblock %} {% block bottom_files %} <script type="text/javascript" src="js/slideshow.js"></script> <script type="text/javascript"> BCSlideshow('focuspic'); var oFruit = document.getElementById('fruit_more'); var oShownum = document.getElementById('show_count'); var hasorder = localStorage.getItem('order_finish'); if(hasorder) { oShownum.innerHTML = '2'; } oFruit.onclick = function(){ window.location.href = 'list.html'; } </script> {% endblock %}
import os os.environ["DJANGO_SETTINGS_MODULE"] = "dailyfresh.settings" # 放到celery服務器上時將註釋打開 #import django #django.setup() from celery import Celery from django.core.mail import send_mail from django.conf import settings from goods.models import GoodsCategory,Goods,IndexGoodsBanner,IndexPromotionBanner,IndexCategoryGoodsBanner from django.template import loader # 建立celery應用對象 app = Celery('celery_tasks.tasks', broker='redis://192.168.243.193/4') @app.task def send_active_email(to_email, user_name, token): """發送激活郵件""" subject = "每天生鮮用戶激活" # 標題 body = "" # 文本郵件體 sender = settings.EMAIL_FROM # 發件人 receiver = [to_email] # 接收人 html_body = '<h1>尊敬的用戶 %s, 感謝您註冊每天生鮮!</h1>' \ '<br/><p>請點擊此連接激活您的賬號<a href="http://127.0.0.1:8000/users/active/%s">' \ 'http://127.0.0.1:8000/users/active/%s</a></p>' %(user_name, token, token) send_mail(subject, body, sender, receiver, html_message=html_body) @app.task def generate_static_index_html(): """生成靜態的html頁面""" # 查詢商品分類信息 categorys = GoodsCategory.objects.all() # 查詢圖片輪播信息:按照index進行排序 banners = IndexGoodsBanner.objects.all().order_by('index') # 查詢活動信息 promotion_banners = IndexPromotionBanner.objects.all().order_by('index') # 查詢分類商品信息 for category in categorys: title_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=0).order_by('index') category.title_banners = title_banners image_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=1).order_by('index') category.image_banners = image_banners # 查詢購物車信息 cart_num = 0 # 構造上下文 context = { 'categorys': categorys, 'banners': banners, 'promotion_banners': promotion_banners, 'cart_num': cart_num } # 加載模板 template = loader.get_template('static_index.html') html_data = template.render(context) # 保存成html文件:放到靜態文件中 file_path = os.path.join(settings.STATICFILES_DIRS[0], 'index.html') with open(file_path, 'w') as file: file.write(html_data)
重啓nginx:sudo /usr/local/nginx/sbin/nginx -s reload
class BaseAdmin(admin.ModelAdmin): """商品活動信息的管理類,運營人員在後臺發佈內容時,異步生成靜態頁面""" def save_model(self, request, obj, form, change): """後臺保存對象數據時使用""" # obj表示要保存的對象,調用save(),將對象保存到數據庫中 obj.save() # 調用celery異步生成靜態文件方法 generate_static_index_html.delay() def delete_model(self, request, obj): """後臺保存對象數據時使用""" obj.delete() generate_static_index_html.delay() class IndexPromotionBannerAdmin(BaseAdmin): """商品活動站點管理,若是有本身的新的邏輯也是寫在這裏""" # list_display = [] pass class GoodsCategoryAdmin(BaseAdmin): pass class GoodsAdmin(BaseAdmin): pass class GoodsSKUAdmin(BaseAdmin): pass class IndexCategoryGoodsBannerAdmin(BaseAdmin): pass # Register your models here. admin.site.register(GoodsCategory,GoodsCategoryAdmin) admin.site.register(Goods,GoodsAdmin) admin.site.register(GoodsSKU,GoodsSKUAdmin) admin.site.register(IndexPromotionBanner,IndexPromotionBannerAdmin) admin.site.register(IndexCategoryGoodsBanner,IndexCategoryGoodsBannerAdmin)
未登陸用戶訪問主頁
/index
登錄用戶訪問主頁
url(r'^index$', views.IndexView.as_view(), name='index')
訪問靜態頁面
訪問動態頁面
from django.core.cache import cache
cache.set('key', 內容, 有效期)
cache.get('key')
邏輯:
若是沒有緩存數據,就查詢數據庫
class IndexView(View): """首頁""" def get(self, request): """查詢首頁頁面須要的數據,構造上下文,渲染首頁頁面""" # 查詢用戶我的信息(request.user) # 先從緩存中讀取數據,若是有就獲取緩存數據,反之,就執行查詢 context = cache.get('index_page_data') if context is None: print('沒有緩存數據,查詢了數據庫') # 查詢商品分類信息 categorys = GoodsCategory.objects.all() # 查詢圖片輪播信息:按照index進行排序 banners = IndexGoodsBanner.objects.all().order_by('index') # 查詢活動信息 promotion_banners = IndexPromotionBanner.objects.all().order_by('index') # 查詢分類商品信息 for category in categorys: title_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=0).order_by('index') category.title_banners = title_banners image_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=1).order_by('index') category.image_banners = image_banners # 構造上下文:先處理購物車之外的上下文,並緩存 context = { 'categorys':categorys, 'banners':banners, 'promotion_banners':promotion_banners, } # 設置緩存數據:名字,內容,有效期 cache.set('index_page_data',context,3600) # 查詢購物車信息:不能被緩存,由於會常常變化 cart_num = 0 # 補充購物車數據 context.update(cart_num=cart_num) return render(request, 'index.html',context)
緩存須要在修改內容時刪除,否則內容修改了,但仍是緩存的舊數據
class BaseAdmin(admin.ModelAdmin): """商品活動信息的管理類,運營人員在後臺發佈內容時,異步生成靜態頁面""" def save_model(self, request, obj, form, change): """後臺保存對象數據時使用""" # obj表示要保存的對象,調用save(),將對象保存到數據庫中 obj.save() # 調用celery異步生成靜態文件方法,操做完表單後刪除靜態文件 generate_static_index_html.delay() # 修改了數據庫數據就須要刪除緩存 cache.delete('index_page_data') def delete_model(self, request, obj): """後臺保存對象數據時使用""" obj.delete() generate_static_index_html.delay() cache.delete('index_page_data')
哈希類型存儲:cart_userid sku_1 10 sku_2 20 字典結構:cart_userid:{sku_1:10,sku_2:20}
class IndexView(View): """首頁""" def get(self, request): """查詢首頁頁面須要的數據,構造上下文,渲染首頁頁面""" # 查詢用戶我的信息(request.user) # 先從緩存中讀取數據,若是有就獲取緩存數據,反之,就執行查詢 context = cache.get('index_page_data') if context is None: print('沒有緩存數據,查詢了數據庫') # 查詢商品分類信息 categorys = GoodsCategory.objects.all() # 查詢圖片輪播信息:按照index進行排序 banners = IndexGoodsBanner.objects.all().order_by('index') # 查詢活動信息 promotion_banners = IndexPromotionBanner.objects.all().order_by('index') # 查詢分類商品信息 for category in categorys: title_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=0).order_by('index') category.title_banners = title_banners image_banners = IndexCategoryGoodsBanner.objects.filter(category=category, display_type=1).order_by('index') category.image_banners = image_banners # 構造上下文:先處理購物車之外的上下文,並緩存 context = { 'categorys':categorys, 'banners':banners, 'promotion_banners':promotion_banners, } # 設置緩存數據:名字,內容,有效期 cache.set('index_page_data',context,3600) # 查詢購物車信息:不能被緩存,由於會常常變化 cart_num = 0 # 若是用戶登陸,就獲取購物車數據 if request.user.is_authenticated(): # 建立redis_conn對象 redis_conn = get_redis_connection('default') # 獲取用戶id user_id = request.user.id # 從redis中獲取購物車數據,返回字典 cart_dict = redis_conn.hgetall('cart_%s'%user_id) # 遍歷購物車字典的值,累加購物車的值 for value in cart_dict.values(): cart_num += int(value) # 補充購物車數據 context.update(cart_num=cart_num) return render(request, 'index.html',context)
/detail/1
url(r'^detail/(?P<sku_id>\d+)$', views.DetailView.as_view(), name='detail'),
class DetailView(View): """商品詳細信息頁面""" def get(self, request, sku_id): # 嘗試獲取緩存數據 context = cache.get("detail_%s" % sku_id) # 若是緩存不存在 if context is None: try: # 獲取商品信息 sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # from django.http import Http404 # raise Http404("商品不存在!") return redirect(reverse("goods:index")) # 獲取類別 categorys = GoodsCategory.objects.all() # 從訂單中獲取評論信息 sku_orders = sku.ordergoods_set.all().order_by('-create_time')[:30] if sku_orders: for sku_order in sku_orders: sku_order.ctime = sku_order.create_time.strftime('%Y-%m-%d %H:%M:%S') sku_order.username = sku_order.order.user.username else: sku_orders = [] # 獲取最新推薦 new_skus = GoodsSKU.objects.filter(category=sku.category).order_by("-create_time")[:2] # 獲取其餘規格的商品 other_skus = sku.goods.goodssku_set.exclude(id=sku_id) context = { "categorys": categorys, "sku": sku, "orders": sku_orders, "new_skus": new_skus, "other_skus": other_skus } # 設置緩存 cache.set("detail_%s"%sku_id, context, 3600) # 購物車數量 cart_num = 0 # 若是是登陸的用戶 if request.user.is_authenticated(): # 獲取用戶id user_id = request.user.id # 從redis中獲取購物車信息 redis_conn = get_redis_connection("default") # 若是redis中不存在,會返回None cart_dict = redis_conn.hgetall("cart_%s"%user_id) for val in cart_dict.values(): cart_num += int(val) # 瀏覽記錄: lpush history_userid sku_1, sku_2 # 移除已經存在的本商品瀏覽記錄 redis_conn.lrem("history_%s"%user_id, 0, sku_id) # 添加新的瀏覽記錄 redis_conn.lpush("history_%s"%user_id, sku_id) # 只保存最多5條記錄 redis_conn.ltrim("history_%s"%user_id, 0, 4) context.update({"cart_num": cart_num}) return render(request, 'detail.html', context)
{% extends 'base.html' %} {% load staticfiles %} {% block title %}每天生鮮-商品詳情{% endblock %} {% block body %} <div class="navbar_con"> <div class="navbar clearfix"> <div class="subnav_con fl"> <h1>所有商品分類</h1> <span></span> <ul class="subnav"> {% for category in categorys %} <li><a href="#" class="{{ category.logo }}">{{ category.name }}</a></li> {% endfor %} </ul> </div> </div> </div> <div class="breadcrumb"> <a href="/">所有分類</a> <span>></span> <a href="#">{{ sku.category.name }}</a> <span>></span> <a href="#">商品詳情</a> </div> <div class="goods_detail_con clearfix"> <div class="goods_detail_pic fl"><img src="{{ sku.default_image.url }}"></div> <div class="goods_detail_list fr"> <h3>{{ sku.name}}</h3> <p>{{ sku.title }}</p> <div class="prize_bar"> <span class="show_pirze">¥<em>{{ sku.price }}</em></span> <span class="show_unit">單 位:{{ sku.unit }}</span> </div> {% if other_skus %} <div> <p>其餘規格:</p> <ul> {% for sku in other_skus %} <li><a href="{% url 'goods:detail' sku.id %}">{{ sku.price }}/{{ sku.unit }}</a></li> {% endfor %} </ul> </div> {% endif %} <div class="goods_num clearfix"> <div class="num_name fl">數 量:</div> <div class="num_add fl"> <input type="text" class="num_show fl" id="num_show" value="1"> <a href="javascript:;" class="add fr" id="add">+</a> <a href="javascript:;" class="minus fr" id="minus">-</a> </div> </div> <div class="total">總價:<em>{{ sku.price }}</em>元</div> <div class="operate_btn"> <a href="javascript:;" class="buy_btn" id="buy_btn">當即購買</a> <a href="javascript:;" class="add_cart" sku_id="{{ sku.id }}" id="add_cart">加入購物車</a> </div> </div> </div> <div class="main_wrap clearfix"> <div class="l_wrap fl clearfix"> <div class="new_goods"> <h3>新品推薦</h3> <ul> {% for sku in new_skus %} <li> <a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.default_image.url }}"></a> <h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4> <div class="prize">¥{{ sku.price }}</div> </li> {% endfor %} </ul> </div> </div> <div class="r_wrap fr clearfix"> <ul class="detail_tab clearfix"> <li id="tag_detail" class="active">商品介紹</li> <li id="tag_comment">評論</li> </ul> <div class="tab_content" id="tab_detail"> <dl> <dt>商品詳情:</dt> <dd>{{ sku.goods.desc|safe }}</dd> </dl> </div> <div class="tab_content" id="tab_comment" style="display: none;"> {% for order in orders %} <dl> <dd>客戶:{{ order.username }} 時間:{{ order.ctime }}</dd> <dt>{{ order.comment }}</dt> </dl> <hr/> {% endfor %} </div> </div> </div> {% endblock %} {% block footer %} <div class="add_jump"></div> {% endblock %} {% block bottom_files %} <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script> <script type="text/javascript"> $("#tag_detail").click(function(){ $("#tag_comment").removeClass("active"); $(this).addClass("active"); $("#tab_comment").hide(); $("#tab_detail").show(); }); $("#tag_comment").click(function(){ $("#tag_detail").removeClass("active"); $(this).addClass("active"); $("#tab_detail").hide(); $("#tab_comment").show(); }); $("#buy_btn").click(function(){ var count = $("#num_show").val(); window.location.href = '/order/commit?g={{goods.id}}@' + count; }); var $add_x = $('#add_cart').offset().top; var $add_y = $('#add_cart').offset().left; var $to_x = $('#show_count').offset().top; var $to_y = $('#show_count').offset().left; // 點擊加入購物車 $('#add_cart').click(function(){ // 將商品的id 和 數量發送給後端視圖,保存到購物車數據中 var req_data = { sku_id: $('#add_cart').attr("sku_id"), count: $("#num_show").val(), csrfmiddlewaretoken: "{{ csrf_token }}" }; {# // 使用ajax向後端發送數據#} {# $.post("/cart/add", req_data, function (response_data) {#} {# // 根據response_data中的code決定處理效果#} {# if (1 == response_data.code) {#} {# // 用戶未登陸#} {# window.location.href = "/users/login"; // 讓頁面跳轉到登陸頁面#} {# } else if (0 == response_data.code) {#} {# // 添加到購物車成功動畫#} {# $(".add_jump").css({'left':$add_y+80,'top':$add_x+10,'display':'block'});#} {##} {# $(".add_jump").stop().animate({#} {# 'left': $to_y+7,#} {# 'top': $to_x+7},#} {# "fast", function() {#} {# $(".add_jump").fadeOut('fast',function(){#} {# $('#show_count').html(response_data.cart_num);#} {# });#} {# });#} {##} {# } else {#} {# // 其餘錯誤信息,alert展現#} {# alert(response_data.message);#} {# }#} {# }, "json");#} }); $("#add").click(function(){ var num_show = $("#num_show").val(); num_show = parseInt(num_show); num_show += 1; $("#num_show").val(num_show); var price = $(".show_pirze>em").html(); price = parseFloat(price); var total = price * num_show; $(".total>em").html(total.toFixed(2)); }); $("#minus").click(function(){ var num_show = $("#num_show").val(); num_show = parseInt(num_show); num_show -= 1; if (num_show < 1){ num_show = 1; } $("#num_show").val(num_show); var price = $(".show_pirze>em").html(); price = parseFloat(price); var total = price * num_show; $(".total>em").html(total.toFixed(2)); }); </script> {% endblock %}
/list/category_id/page_num/?sort='default'
/list/category_id/page_num/?sort='price'
/list/category_id/page_num/?sort='hot'
GoodsCategory.DoesNotExist
EmptyPage
class ListView(View): """商品列表""" def get(self, request, category_id, page_num): # 獲取sort參數:若是用戶不傳,就是默認的排序規則 sort = request.GET.get('sort', 'default') # 校驗參數 # 判斷category_id是否正確,經過異常來判斷 try: category = GoodsCategory.objects.get(id=category_id) except GoodsCategory.DoesNotExist: return redirect(reverse('goods:index')) # 查詢商品全部類別 categorys = GoodsCategory.objects.all() # 查詢該類別商品新品推薦 new_skus = GoodsSKU.objects.filter(category=category).order_by('-create_time')[:2] # 查詢該類別全部商品SKU信息:按照排序規則來查詢 if sort == 'price': # 按照價格由低到高 skus = GoodsSKU.objects.filter(category=category).order_by('price') elif sort == 'hot': # 按照銷量由高到低 skus = GoodsSKU.objects.filter(category=category).order_by('-sales') else: skus = GoodsSKU.objects.filter(category=category) # 不管用戶是否傳入或者傳入其餘的排序規則,我在這裏都重置成'default' sort = 'default' # 分頁:須要知道從第幾頁展現 page_num = int(page_num) # 建立分頁器:每頁兩條記錄 paginator = Paginator(skus,2) # 校驗page_num:只有知道分頁對對象,才能知道page_num是否正確 try: page_skus = paginator.page(page_num) except EmptyPage: # 若是page_num不正確,默認給用戶第一頁數據 page_skus = paginator.page(1) # 獲取頁數列表 page_list = paginator.page_range # 購物車 cart_num = 0 # 若是是登陸的用戶 if request.user.is_authenticated(): # 獲取用戶id user_id = request.user.id # 從redis中獲取購物車信息 redis_conn = get_redis_connection("default") # 若是redis中不存在,會返回None cart_dict = redis_conn.hgetall("cart_%s" % user_id) for val in cart_dict.values(): cart_num += int(val) # 構造上下文 context = { 'sort':sort, 'category':category, 'cart_num':cart_num, 'categorys':categorys, 'new_skus':new_skus, 'page_skus':page_skus, 'page_list':page_list } # 渲染模板 return render(request, 'list.html', context)
{% extends 'base.html' %} {% load staticfiles %} {% block title %} 每天生鮮-商品列表 {% endblock %} {% block body %} <div class="navbar_con"> <div class="navbar clearfix"> <div class="subnav_con fl"> <h1>所有商品分類</h1> <span></span> <ul class="subnav"> {% for category in categorys %} {# 默認跳轉到某個分類商品列表的第一頁 #} <li><a href="{% url 'goods:list' category.id 1 %}" class="{{ category.logo }}">{{ category.name }}</a></li> {% endfor %} </ul> </div> <ul class="navlist fl"> <li><a href="">首頁</a></li> <li class="interval">|</li> <li><a href="">手機生鮮</a></li> <li class="interval">|</li> <li><a href="">抽獎</a></li> </ul> </div> </div> <div class="breadcrumb"> <a href="{% url 'goods:index' %}">所有分類</a> <span>></span> <a href="{% url 'goods:list' category.id 1 %}">{{ category.name }}</a> </div> <div class="main_wrap clearfix"> <div class="l_wrap fl clearfix"> <div class="new_goods"> <h3>新品推薦</h3> <ul> {% for new_sku in new_skus %} <li> <a href="{% url 'goods:detail' new_sku.id %}"><img src="{{ new_sku.default_image.url }}"></a> <h4><a href="{% url 'goods:detail' new_sku.id %}">{{ new_sku.name }}</a></h4> <div class="prize">¥{{ new_sku.price }}</div> </li> {% endfor %} </ul> </div> </div> <div class="r_wrap fr clearfix"> <div class="sort_bar"> <a href="{% url 'goods:list' category.id 1 %}?srot=default" {% if sort == 'default' %}class="active"{% endif %}>默認</a> <a href="{% url 'goods:list' category.id 1 %}?srot=price" {% if sort == 'price' %}class="active"{% endif %}>價格</a> <a href="{% url 'goods:list' category.id 1 %}?srot=hot" {% if sort == 'hot' %}class="active"{% endif %}>人氣</a> </div> <ul class="goods_type_list clearfix"> {% for sku in page_skus %} <li> <a href="{% url 'goods:detail' sku.id %}"><img src="{{ sku.default_image.url }}"></a> <h4><a href="{% url 'goods:detail' sku.id %}">{{ sku.name }}</a></h4> <div class="operate"> <span class="prize">¥{{ sku.price }}</span> <span class="unit">{{ sku.price }}/{{ sku.unit }}</span> <a href="#" class="add_goods" title="加入購物車"></a> </div> </li> {% endfor %} </ul> <div class="pagenation"> {% if page_skus.has_previous %} <a href="{% url 'goods:list' category.id page_skus.previous_page_number %}?sort={{ sort }}">上一頁</a> {% endif %} {% for index in page_list %} <a href="{% url 'goods:list' category.id index %}?sort={{ sort }}" {% if index == page_skus.number %}class="active"{% endif %}>{{ index }}</a> {% endfor %} {% if page_skus.has_next %} <a href="{% url 'goods:list' category.id page_skus.next_page_number %}?sort={{ sort }}">下一頁</a> {% endif %} </div> </div> </div> {% endblock %}
select * from table where name like '%草莓%' or title like '%草莓%';
草莓
,盒裝草莓
,500g草莓
like '%草莓%'
改爲like '%北京草莓%'
就沒法再查詢出草莓
,盒裝草莓
,500g草莓
# 全文檢索框架 pip install django-haystack # 全文檢索引擎 pip install whoosh # 中文分詞框架 pip install jieba
1.安裝haystack應用
INSTALLED_APPS = ( ... 'haystack', )
2.在settings.py文件中配置搜索引擎
# 配置搜索引擎後端 HAYSTACK_CONNECTIONS = { 'default': { # 使用whoosh引擎:提示,若是不須要使用jieba框架實現分詞,就使用whoosh_backend 'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine', # 索引文件路徑 'PATH': os.path.join(BASE_DIR, 'whoosh_index'), } } # 當添加、修改、刪除數據時,自動生成索引 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
在要創建索引的表對應的應用下,建立search_indexes.py
文件
定義商品索引類GoodsSKUIndex()
,繼承自indexes.SearchIndex
和indexes.Indexable
from haystack import indexes from goods.models import GoodsSKU class GoodsSKUIndex(indexes.SearchIndex, indexes.Indexable): """創建索引時被使用的類""" text = indexes.CharField(document=True, use_template=True) def get_model(self): """從哪一個表中查詢""" return GoodsSKU def index_queryset(self, using=None): """返回要創建索引的數據""" return self.get_model().objects.all()
在templates
下面新建目錄search/indexes/應用名
goods
應用中的GoodsSKU
模型類中的字段要創建索引:search/indexes/goods
在新建目錄下,建立goodssku_text.txt
,並編輯要創建索引的字段,以下圖
python manage.py rebuild_index
/search/
get
接收關鍵字:q
import haystack.urls url(r'^search/', include(haystack.urls)),
全文檢索結果:
templates/search
目錄下的search.html
search.html
,咱們須要本身創建該html文件,並定義本身的搜索結果頁面
傳遞的上下文包括:
settings.py
文件中設置HAYSTACK_SEARCH_RESULTS_PER_PAGE
HAYSTACK_SEARCH_RESULTS_PER_PAGE
能夠控制每頁顯示數量HAYSTACK_SEARCH_RESULTS_PER_PAGE = 1
search.html
編寫,相似商品列表頁面
{% extends 'base.html' %} {% load staticfiles %} {% block title %}每天生鮮-搜索結果{% endblock %} {% block search_bar %} <div class="search_bar clearfix"> <a href="{% url 'goods:index' %}" class="logo fl"><img src="{% static 'images/logo.png' %}"></a> <div class="sub_page_name fl">| 搜索結果</div> <div class="search_con fr"> <form action="/search/" method="get"> <input type="text" class="input_text fl" name="q" placeholder="搜索商品"> <input type="submit" class="input_btn fr" value="搜索"> </form> </div> </div> {% endblock %} {% block body %} <div class="main_wrap clearfix"> <ul class="goods_type_list clearfix"> {% for result in page %} <li> {# object取得纔是sku對象 #} <a href="{% url 'goods:detail' result.object.id %}"><img src="{{ result.object.default_image.url }}"></a> <h4><a href="{% url 'goods:detail' result.object.id %}">{{result.object.name}}</a></h4> <div class="operate"> <span class="prize">¥{{ result.object.price }}</span> <span class="unit">{{ result.object.price }}/{{ result.object.unit }}</span> </div> </li> {% empty %} <p>沒有找到您要查詢的商品。</p> {% endfor %} </ul> {% if page.has_previous or page.has_next %} <div class="pagenation"> {% if page.has_previous %}<a href="/search/?q={{ query }}&page={{ page.previous_page_number }}">上一頁</a>{% endif %} | {% if page.has_next %}<a href="/search/?q={{ query }}&page={{ page.next_page_number }}">下一頁</a>{% endif %} </div> {% endif %} </div> {% endblock %}
草莓
、盒裝草莓
、大草莓
、北京草莓
草莓
做爲關鍵字時,檢索不出來盒裝草莓
、大草莓
、北京草莓
jieba
/home/python/.virtualenvs/py3_django/lib/python3.5/site-packages/
haystack/backends/
中2.建立ChineseAnalyzer.py
文件
import jieba from 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()
3.拷貝whoosh_backend.py
爲whoosh_cn_backend.py
cp whoosh_backend.py whoosh_cn_backend.py
4.更改分詞的類爲ChineseAnalyzer
whoosh_cn_backend.py
from .ChineseAnalyzer import ChineseAnalyzer
analyzer=StemmingAnalyzer() 改成 analyzer=ChineseAnalyzer()
5.更改分詞引擎
6.從新建立索引數據
python manage.py rebuild_index
測試結果