书籍出处:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé
(译者@ucag注:咳咳,第七章终于来了。其实在一月份就翻译完了????但是后来我回老家了,就没发出来。各位久等了~)
(译者@夜夜月注:真羡慕有寒假和暑假的人- -愿你们的寒暑假作业越多越好,粗略的校对了下,精校版本请大家继续等待)
第七章
建立一个在线商店
在上一章,你创建了一个用户跟踪系统和建立了一个用户活跃流。你也学习了 Django 信号是如何工作的,并且把 Redis 融合进了项目中来为图像视图计数。在这一章中,你将学会如何建立一个最基本的在线商店。你将会为你的产品创建目录和使用 Django sessions 实现一个购物车。你也将学习怎样定制上下文处理器( context processors )以及用 Celery 来激活动态任务。
在这一章中,你将学会:
创建一个产品目录
使用 Django sessions 建立购物车
管理顾客的订单
用 Celery 发送异步通知
创建一个在线商店项目(project)
我们将从新建一个在线商店项目开始。我们的用户可以浏览产品目录并且可以向购物车中添加商品。最后,他们将清点购物车然后下单。这一章涵盖了在线商店的以下几个功能:
创建产品目录模型(模型),将它们添加到管理站点,创建基本的视图(view)来展示目录
使用 Django sessions 建立一个购物车系统,使用户可以在浏览网站的过程中保存他们选中的商品
创建下单表单和功能
发送一封异步的确认邮件在用户下单的时候
首先,用以下命令来为你的新项目创建一个虚拟环境,然后激活它:
mkdir env virtualenv env/myshop source env/myshop/bin/activate
用以下命令在你的虚拟环境中安装 Django :
pip install django==1.8.6
创建一个叫做 myshop
的新项目,再创建一个叫做 shop
的应用,命令如下:
django-admin startproject myshopcd myshop django-admin startapp shop
编辑你项目中的 settings.py
文件,像下面这样将你的应用添加到 INSTALLED_APPS
中:
INSTALLED_APPS = [ # ... 'shop', ]
现在你的应用已经在项目中激活。接下来让我们为产品目录定义模型(models)。
创建产品目录模型(models)
我们商店中的目录将会由不同分类的产品组成。每一个产品会有一个名字,一段可选的描述,一张可选的图片,价格,以及库存。 编辑位于shop
应用中的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)由一个 name
字段和一个唯一的 slug
字段构成。Product
模型(model):
category
: 这是一个链接向Category
的ForeignKey
。这是个多对一(many-to-one)关系。一个产品可以属于一个分类,一个分类也可包含多个产品。name
: 这是产品的名字slug
: 用来为这个产品建立 URL 的 slugimage
: 可选的产品图片description
: 可选的产品描述price
: 这是个DecimalField
(译者@ucag注:十进制字段)。这个字段使用 Python 的decimal.Decimal
元类来保存一个固定精度的十进制数。max_digits
属性可用于设定数字的最大值,decimal_places
属性用于设置小数位数。stock
: 这是个PositiveIntegerField
(译者@ucag注:正整数字段) 来保存这个产品的库存。available
: 这个布尔值用于展示产品是否可供购买。这使得我们可在目录中使产品废弃或生效。created
: 当对象被创建时这个字段被保存。update
: 当对象最后一次被更新时这个字段被保存。
对于 price
字段,我们使用 DecimalField
而不是 FloatField
来避免精度问题。
我们总是使用
DecimalField
来保存货币值。FloatField
在内部使用 Python 的float
类型。反之,DecimalField
使用的是 Python 中的Decimal
类型,使用Decimal
类型可以避免精度问题。
在 Product
模型(model)中的 Meta
类中,我们使用 index_together
元选项来指定 id
和 slug
字段的共同索引。我们定义这个索引,因为我们准备使用这两个字段来查询产品,两个字段被索引在一起来提高使用双字段查询的效率。
由于我们会在模型(models)中和图片打交道,打开 shell ,用下面的命令安装 Pillow :
pip isntall Pillow==2.9.0
现在,运行下面的命令来为你的项目创建初始迁移:
python manage.py makemigrations
你将会看到以下输出:
Migrations for 'shop': 0001_initial.py: - Create model Category - Create model Product - Alter index_together for product (1 constraint(s))
用下面的命令来同步你的数据库:
python mange.py migrate
你将会看到包含下面这一行的输出:
Applying shop.0001_initial... OK
现在数据库已经和你的模型(models)同步了。
注册目录模型(models)到管理站点
让我们把模型(models)注册到管理站点,这样我们就可以轻松管理产品和产品分类了。编辑 shop
应用的 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
属性来设置可被编辑的字段,并且这些字段都在管理站点的列表页被列出。这样可以让你一次编辑多行。任何在 list_editable
的字段也必须在 list_display
中,因为只有这样被展示的字段才可以被编辑。
现在,使用如下命令为你的站点创建一个超级用户:
python manage.py createsuperuser
使用命令 python manage.py runserver
启动开发服务器。 访问 http://127.0.0.1:8000/admin/shop/product/add ,登录你刚才创建的超级用户。在管理站点的交互界面添加一个新的品种和产品。 product 的更改页面如下所示:
创建目录视图(views)
为了展示产品目录, 我们需要创建一个视图(view)来列出所有产品或者是给出的筛选后的产品。编辑 shop
应用中的 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
的查询集来检索可用的产品。我们使用一个可选参数 category_slug
通过所给产品类别来有选择性的筛选产品。
我们也需要一个视图来检索和展示单一的产品。把下面的代码添加进去:
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
参数来检索 Product
实例。我们可以只用 ID 就可以得到这个实例,因为它是一个独一无二的属性。尽管,我们在 URL 中引入了 slug 来建立搜索引擎友好(SEO-friendly)的 URL。
在创建了产品列表和明细视图(views)之后,我们该为它们定义 URL 模式了。在 shop
应用的路径下创建一个新的文件,命名为 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'),
这些是我们产品目录的URL模式。 我们为 product_list
视图(view)定义了两个不同的 URL 模式。 命名为product_list
的模式不带参数调用 product_list
视图(view);命名为 product_list_bu_category
的模式向视图(view)函数传递一个 category_slug
参数,以便通过给定的产品种类来筛选产品。我们为 product_detail
视图(view)添加的模式传递了 id
和 slug
参数来检索特定的产品。
像这样编辑 myshop
项目中的 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')), ]
在项目的主要 URL 模式中,我们引入了 shop
应用的 URL 模式,并指定了一个命名空间,叫做 shop
。
现在,编辑 shop
应用中的 models.py
文件,导入 reverse()
函数,然后给 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])
正如你已经知道的那样, get_absolute_url()
是检索一个对象的 URL 约定俗成的方法。这里,我们将使用我们刚刚在 urls.py
文件中定义的 URL 模式。
创建目录模板(templates)
现在,我们需要为产品列表和明细视图创建模板(templates)。在 shop
应用的路径下创建如下路径和文件:
templates/ shop/ base.html product/ list.html detail.html
我们需要定义一个基础模板(template),然后在产品列表和明细模板(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>
这就是我们将为我们的商店应用使用的基础模板(template)。为了引入模板使用的 CSS 和图像,你需要复制这一章示例代码中的静态文件,位于 shop
应用中的 static/
路径下。把它们复制到你的项目中相同的地方。
编辑 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