書籍出處: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 的 slug

  • image: 可選的產(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