書籍出處:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé
(譯者@ucag注:咳咳,第七章終于來了。其實(shí)在一月份就翻譯完了????但是后來我回老家了,就沒發(fā)出來。各位久等了~)
(譯者@夜夜月注:真羨慕有寒假和暑假的人- -愿你們的寒暑假作業(yè)越多越好,粗略的校對了下,精校版本請大家繼續(xù)等待)
第七章
建立一個(gè)在線商店
在上一章,你創(chuàng)建了一個(gè)用戶跟蹤系統(tǒng)和建立了一個(gè)用戶活躍流。你也學(xué)習(xí)了 Django 信號是如何工作的,并且把 Redis 融合進(jìn)了項(xiàng)目中來為圖像視圖計(jì)數(shù)。在這一章中,你將學(xué)會如何建立一個(gè)最基本的在線商店。你將會為你的產(chǎn)品創(chuàng)建目錄和使用 Django sessions 實(shí)現(xiàn)一個(gè)購物車。你也將學(xué)習(xí)怎樣定制上下文處理器( context processors )以及用 Celery 來激活動態(tài)任務(wù)。
在這一章中,你將學(xué)會:
創(chuàng)建一個(gè)產(chǎn)品目錄
使用 Django sessions 建立購物車
管理顧客的訂單
用 Celery 發(fā)送異步通知
創(chuàng)建一個(gè)在線商店項(xiàng)目(project)
我們將從新建一個(gè)在線商店項(xiàng)目開始。我們的用戶可以瀏覽產(chǎn)品目錄并且可以向購物車中添加商品。最后,他們將清點(diǎn)購物車然后下單。這一章涵蓋了在線商店的以下幾個(gè)功能:
創(chuàng)建產(chǎn)品目錄模型(模型),將它們添加到管理站點(diǎn),創(chuàng)建基本的視圖(view)來展示目錄
使用 Django sessions 建立一個(gè)購物車系統(tǒng),使用戶可以在瀏覽網(wǎng)站的過程中保存他們選中的商品
創(chuàng)建下單表單和功能
發(fā)送一封異步的確認(rèn)郵件在用戶下單的時(shí)候
首先,用以下命令來為你的新項(xiàng)目創(chuàng)建一個(gè)虛擬環(huán)境,然后激活它:
mkdir env virtualenv env/myshop source env/myshop/bin/activate
用以下命令在你的虛擬環(huán)境中安裝 Django :
pip install django==1.8.6
創(chuàng)建一個(gè)叫做 myshop
的新項(xiàng)目,再創(chuàng)建一個(gè)叫做 shop
的應(yīng)用,命令如下:
django-admin startproject myshopcd myshop django-admin startapp shop
編輯你項(xiàng)目中的 settings.py
文件,像下面這樣將你的應(yīng)用添加到 INSTALLED_APPS
中:
INSTALLED_APPS = [ # ... 'shop', ]
現(xiàn)在你的應(yīng)用已經(jīng)在項(xiàng)目中激活。接下來讓我們?yōu)楫a(chǎn)品目錄定義模型(models)。
創(chuàng)建產(chǎn)品目錄模型(models)
我們商店中的目錄將會由不同分類的產(chǎn)品組成。每一個(gè)產(chǎn)品會有一個(gè)名字,一段可選的描述,一張可選的圖片,價(jià)格,以及庫存。 編輯位于shop
應(yīng)用中的models.py
文件,添加以下代碼:
from django.db import modelsclass Category(models.Model): name = models.CharField(max_length=200, db_index=True) slug = models.SlugField(max_length=200, db_index=True, unique=True) class Meta: ordering = ('name',) verbose_name = 'category' verbose_name_plural = 'categories' def __str__(self): return self.name class Product(models.Model): category = models.ForeignKey(Category, related_name='products') name = models.CharField(max_length=200, db_index=True) slug = models.SlugField(max_length=200, db_index=True) image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True) description = models.TextField(blank=True) price = models.DecimalField(max_digits=10, decimal_places=2) stock = models.PositiveIntegerField() available = models.BooleanField(default=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) class Meta: ordering = ('name',) index_together = (('id', 'slug'),) def __str__(self): return self.name
這是我們的 Category
和 Product
模型(models)。Category
模型(models)由一個(gè) name
字段和一個(gè)唯一的 slug
字段構(gòu)成。Product
模型(model):
category
: 這是一個(gè)鏈接向Category
的ForeignKey
。這是個(gè)多對一(many-to-one)關(guān)系。一個(gè)產(chǎn)品可以屬于一個(gè)分類,一個(gè)分類也可包含多個(gè)產(chǎn)品。name
: 這是產(chǎn)品的名字slug
: 用來為這個(gè)產(chǎn)品建立 URL 的 slugimage
: 可選的產(chǎn)品圖片description
: 可選的產(chǎn)品描述price
: 這是個(gè)DecimalField
(譯者@ucag注:十進(jìn)制字段)。這個(gè)字段使用 Python 的decimal.Decimal
元類來保存一個(gè)固定精度的十進(jìn)制數(shù)。max_digits
屬性可用于設(shè)定數(shù)字的最大值,decimal_places
屬性用于設(shè)置小數(shù)位數(shù)。stock
: 這是個(gè)PositiveIntegerField
(譯者@ucag注:正整數(shù)字段) 來保存這個(gè)產(chǎn)品的庫存。available
: 這個(gè)布爾值用于展示產(chǎn)品是否可供購買。這使得我們可在目錄中使產(chǎn)品廢棄或生效。created
: 當(dāng)對象被創(chuàng)建時(shí)這個(gè)字段被保存。update
: 當(dāng)對象最后一次被更新時(shí)這個(gè)字段被保存。
對于 price
字段,我們使用 DecimalField
而不是 FloatField
來避免精度問題。
我們總是使用
DecimalField
來保存貨幣值。FloatField
在內(nèi)部使用 Python 的float
類型。反之,DecimalField
使用的是 Python 中的Decimal
類型,使用Decimal
類型可以避免精度問題。
在 Product
模型(model)中的 Meta
類中,我們使用 index_together
元選項(xiàng)來指定 id
和 slug
字段的共同索引。我們定義這個(gè)索引,因?yàn)槲覀儨?zhǔn)備使用這兩個(gè)字段來查詢產(chǎn)品,兩個(gè)字段被索引在一起來提高使用雙字段查詢的效率。
由于我們會在模型(models)中和圖片打交道,打開 shell ,用下面的命令安裝 Pillow :
pip isntall Pillow==2.9.0
現(xiàn)在,運(yùn)行下面的命令來為你的項(xiàng)目創(chuàng)建初始遷移:
python manage.py makemigrations
你將會看到以下輸出:
Migrations for 'shop': 0001_initial.py: - Create model Category - Create model Product - Alter index_together for product (1 constraint(s))
用下面的命令來同步你的數(shù)據(jù)庫:
python mange.py migrate
你將會看到包含下面這一行的輸出:
Applying shop.0001_initial... OK
現(xiàn)在數(shù)據(jù)庫已經(jīng)和你的模型(models)同步了。
注冊目錄模型(models)到管理站點(diǎn)
讓我們把模型(models)注冊到管理站點(diǎn),這樣我們就可以輕松管理產(chǎn)品和產(chǎn)品分類了。編輯 shop
應(yīng)用的 admin.py
文件,添加如下代碼:
from django.contrib import adminfrom .models import Category, Productclass CategoryAdmin(admin.ModelAdmin): list_display = ['name', 'slug'] prepopulated_fields = {'slug': ('name',)} admin.site.register(Category, CategoryAdmin)class ProductAdmin(admin.ModelAdmin): list_display = ['name', 'slug', 'price', 'stock', 'available', 'created', 'updated'] list_filter = ['available', 'created', 'updated'] list_editable = ['price', 'stock', 'available'] prepopulated_fields = {'slug': ('name',)} admin.site.register(Product, ProductAdmin)
記住,我們使用 prepopulated_fields
屬性來指定那些要使用其他字段來自動賦值的字段。正如你以前看到的那樣,這樣做可以很方便的生成 slugs 。我們在 ProductAdmin
類中使用 list_editable
屬性來設(shè)置可被編輯的字段,并且這些字段都在管理站點(diǎn)的列表頁被列出。這樣可以讓你一次編輯多行。任何在 list_editable
的字段也必須在 list_display
中,因?yàn)橹挥羞@樣被展示的字段才可以被編輯。
現(xiàn)在,使用如下命令為你的站點(diǎn)創(chuàng)建一個(gè)超級用戶:
python manage.py createsuperuser
使用命令 python manage.py runserver
啟動開發(fā)服務(wù)器。 訪問 http://127.0.0.1:8000/admin/shop/product/add ,登錄你剛才創(chuàng)建的超級用戶。在管理站點(diǎn)的交互界面添加一個(gè)新的品種和產(chǎn)品。 product 的更改頁面如下所示:
創(chuàng)建目錄視圖(views)
為了展示產(chǎn)品目錄, 我們需要?jiǎng)?chuàng)建一個(gè)視圖(view)來列出所有產(chǎn)品或者是給出的篩選后的產(chǎn)品。編輯 shop
應(yīng)用中的 views.py
文件,添加如下代碼:
from django.shortcuts import render, get_object_or_404from .models import Category, Productdef product_list(request, category_slug=None): category = None categories = Category.objects.all() products = Product.objects.filter(available=True) if category_slug: category = get_object_or_404(Category, slug=category_slug) products = products.filter(category=category) return render(request, 'shop/product/list.html', {'category': category, 'categories': categories, 'products': products})
我們只篩選 available=True
的查詢集來檢索可用的產(chǎn)品。我們使用一個(gè)可選參數(shù) category_slug
通過所給產(chǎn)品類別來有選擇性的篩選產(chǎn)品。
我們也需要一個(gè)視圖來檢索和展示單一的產(chǎn)品。把下面的代碼添加進(jìn)去:
def product_detail(request, id, slug): product = get_object_or_404(Product, id=id, slug=slug, available=True) return render(request, 'shop/product/detail.html', {'product': product})
product_detail
視圖(view)接收 id
和 slug
參數(shù)來檢索 Product
實(shí)例。我們可以只用 ID 就可以得到這個(gè)實(shí)例,因?yàn)樗且粋€(gè)獨(dú)一無二的屬性。盡管,我們在 URL 中引入了 slug 來建立搜索引擎友好(SEO-friendly)的 URL。
在創(chuàng)建了產(chǎn)品列表和明細(xì)視圖(views)之后,我們該為它們定義 URL 模式了。在 shop
應(yīng)用的路徑下創(chuàng)建一個(gè)新的文件,命名為 urls.py
,然后添加如下代碼:
from django.conf.urls import urlfrom . import views urlpatterns = [ url(r'^$', views.product_list, name='product_list'), url(r'^(?P<category_slug>[-\w]+)/$', views.product_list, name='product_list_by_category'), url(r'^(?P<id>\d+)/(?P<slug>[-\w]+)/$', views.product_detail, name='product_detail'),
這些是我們產(chǎn)品目錄的URL模式。 我們?yōu)?nbsp;product_list
視圖(view)定義了兩個(gè)不同的 URL 模式。 命名為product_list
的模式不帶參數(shù)調(diào)用 product_list
視圖(view);命名為 product_list_bu_category
的模式向視圖(view)函數(shù)傳遞一個(gè) category_slug
參數(shù),以便通過給定的產(chǎn)品種類來篩選產(chǎn)品。我們?yōu)?nbsp;product_detail
視圖(view)添加的模式傳遞了 id
和 slug
參數(shù)來檢索特定的產(chǎn)品。
像這樣編輯 myshop
項(xiàng)目中的 urls.py
文件:
from django.conf.urls import url, includefrom django.contrib import admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^', include('shop.urls', namespace='shop')), ]
在項(xiàng)目的主要 URL 模式中,我們引入了 shop
應(yīng)用的 URL 模式,并指定了一個(gè)命名空間,叫做 shop
。
現(xiàn)在,編輯 shop
應(yīng)用中的 models.py
文件,導(dǎo)入 reverse()
函數(shù),然后給 Category
模型和 Product
模型添加 get_absolute_url()
方法:
from django.core.urlresolvers import reverse# ...class Category(models.Model): # ... def get_absolute_url(self): return reverse('shop:product_list_by_category', args=[self.slug]) class Product(models.Model):# ... def get_absolute_url(self): return reverse('shop:product_detail', args=[self.id, self.slug])
正如你已經(jīng)知道的那樣, get_absolute_url()
是檢索一個(gè)對象的 URL 約定俗成的方法。這里,我們將使用我們剛剛在 urls.py
文件中定義的 URL 模式。
創(chuàng)建目錄模板(templates)
現(xiàn)在,我們需要為產(chǎn)品列表和明細(xì)視圖創(chuàng)建模板(templates)。在 shop
應(yīng)用的路徑下創(chuàng)建如下路徑和文件:
templates/ shop/ base.html product/ list.html detail.html
我們需要定義一個(gè)基礎(chǔ)模板(template),然后在產(chǎn)品列表和明細(xì)模板(templates)中繼承它。 編輯 shop/base.html
模板(template),添加如下代碼:
{% load static %}<!DOCTYPE html><html><head> <meta charset="utf-8" /> <title>{% block title %}My shop{% endblock %}</title> <link href="{% static "css/base.css" %}" rel="stylesheet"></head><body> <div id="header"> <a href="/" class="logo">My shop</a> </div> <div id="subheader"> <div class="cart"> Your cart is empty. </div> </div> <div id="content"> {% block content %} {% endblock %} </div></body></html>
這就是我們將為我們的商店應(yīng)用使用的基礎(chǔ)模板(template)。為了引入模板使用的 CSS 和圖像,你需要復(fù)制這一章示例代碼中的靜態(tài)文件,位于 shop
應(yīng)用中的 static/
路徑下。把它們復(fù)制到你的項(xiàng)目中相同的地方。
編輯 shop/product/list.html
模板(template),然后添加如下代碼:
{% extends "shop/base.html" %} {% load static %} {% block title %} {% if category %}{{ category.name }}{% else %}Products{% endif %} {% endblock %} {% block content %} <div id="sidebar"> <h3>Categories</h3> <ul> <li {% if not category %}class="selected"{% endif %}> <a href="{% url "shop:product_list" %}">All</a> </li> {% for c in categories %} <li {% if category.slug == c.slug %}class="selected"{% endif %}> <a href="{{ c.get_absolute_url }}">{{ c.name }}</a> </li> http://www.cnblogs.com/levelksk/p/6495402.html