現在,我們有了一個產品目錄界面,用戶如果看到滿意的產品,就可以將其放入購物車。下面就讓我們來實現購物車的功能。
首先要做一下簡單的分析和設計。購物車應該顯示一系列產品的清單,其中列出了買方選中的產品。但是這個清單沒有必要馬上保存到數據庫,因為直到付款之前,用戶隨時都有可能改變主意。我們只需要在用戶的session中記錄這些產品就可以了。
購物車中的條目
購物車中的條目與產品(Product)很 類似,但是我們沒有必要將這些信息再重復記錄一次,而只需要讓條目關聯到產品即可。此外在條目中還會記錄一些產品中沒有的信息,比如數量。最后,我們想要 在條目中記錄一下單價——雖然產品中包含了價格信息,但是有時候可能會有一些折扣,所以需要記錄下來用戶購買時的價格。基於這些分析,我們設計了條目這一 模型類:
class LineItem(models.Model): product = models.ForeignKey(Product) unit_price = models.DecimalField(max_digits=8,decimal_places=2) quantity = models.IntegerField()
購物車
購物車是這些條目的容器。我們希望實現一個“聰明的”購物車,它可以有自己的一些行為:比如,如果放入已經有的產品,就更改該產品的數量而不是再增加一個 條目;能夠查詢當前購物車中的產品數量,等等。所以購物車也應該是一個模型類。但是與LineItem不同,購物車並不需要記錄到數據庫中,就好像超市並 不關注顧客使用了哪量購物車而只關注他買了什么商品一樣。所以購物車不應該繼承自models.Model,而僅僅應該是一個普通類:
class Cart(object): def __init__(self, *args, **kwargs): self.items = [] self.total_price = 0 def add_product(self,product): self.total_price += product.price for item inself.items: if item.product.id == product.id: item.quantity += 1 return self.items.append(LineItem(product=product,unit_price=product.price,quantity=1))
在我們設計模型的同時,界面設計師也為我們設計好了購物車的界面:
接下來就可以實現url映射,view函數和模板。首先我們希望購物車的url為”http://localhost:8000/depotapp/cart/view/“,這需要在depotapp/urls.py的urlpatterns中增加一行:
(r'cart/view/', view_cart),
然后在depotapp/views.py中定義視圖函數。注意購物車是保存在session中的,需要通過request.session.get獲取:
def view_cart(request): cart = request.session.get("cart",None) t = get_template('depotapp/view_cart.html') ifnot cart: cart = Cart() request.session["cart"] = cart c = RequestContext(request,locals()) return HttpResponse(t.render(c))
最后實現模板界面:
{% extends "base.html" %} {% block title %} 我的購物車{% endblock %} {% block pagename %} 我的購物車 {% endblock %} {% block content %} <divclass="row"> <divclass="span10"> <tableclass="condensed-table"> <thead> <tr> <thclass="header">數量</th> <thclass="yellow header">名稱</th> <thclass="blue header">單價</th> <thclass="green header">小計</th> </tr> </thead> <tbody> {% for item in cart.items %} <tr> <th>{{item.quantity}}</th> <td>{{item.product.title}}</td> <td>{{item.unit_price}}</td> <td>{% widthratio item.quantity 1 item.unit_price %} </td> </tr> {% endfor %} <tr> <th></th> <td></td> <th>總計:</th> <th>{{cart.total_price}}</th> </tr> </tbody> </table> </div> <divclass="span4"> <p><aclass="btn primary span2"href="#">繼續購物</a></a></p> <p><aclass="btn danger span2"href="#">清空購物車</a></p> <p><aclass="btn success span2"href="#">結算</a></p> </div> </div> {% endblock %}
這里面有一個技巧。因為Django模板的設計理念是”業務邏輯應該和表現邏輯相對分開“,所以在Django模板中不建議執行過多的代碼。在計算 條目小計的時候,使用的是Django模板的widthratio標簽。該標簽的原意是按比例計算寬度:根據當前值(this_value)和最大值 (max_value)之間的比例,以及最大寬度(max_width)計算出當前的寬度(this_width),即{% widthratio this_value max_value max_width %} = max_width * this_value / max_value。但是如果我們設定max_value=1,就可以通過width ratio在Django模板中進行乘法計算了。同理還可以進行除法計算。而總計價格的計算是通過模型(Cart)類實現的。
為Django增加session支持
好了,我們已經做了大量的工作,現在讓我們欣賞一下自己的作品。但是啟動server並訪問http://localhost:8000 /depotapp/cart/view/時,卻提示”找不到django_session表“。這是因為Django對session的支持是通過內置 的django.contrib.sessions應用實現的,該應用會將session數據保存在數據庫中。但是創建project是Django並沒 有默認安裝該app——Django不會瞞着你做任何事情。所以如果我們”顯式地“決定要使用session,需要更改project的設置,在 depot/settings.py的INSTALLED_APPS中去掉session的注釋:
INSTALLED_APPS = ( #'django.contrib.auth', #'django.contrib.contenttypes', 'django.contrib.sessions', #'django.contrib.sites', #'django.contrib.messages', #'django.contrib.staticfiles', # Uncomment the next line to enable the admin: # 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', 'depot.depotapp', 'django-groundwork', )
然后還要同步一下數據庫:
$ python manage.py syncdb
Creating tables ...
Creating table django_session
Installing custom SQL ...
Installing indexes ...
No fixtures found.
這是再啟動服務器,就可以看到我們實現的購物車了。但是這個購物車能看不能用,無法將商品加入購物車,也無法從中去掉商品。下一節,讓我們繼續完善購物車的功能。