扩展电商平台功能:优惠券、国际化与推荐系统实现
在上一章节中,我们为电商平台集成了在线支付功能,并实现了PDF发票的自动生成与发送。本章将聚焦于三个重要功能的实现:优惠券折扣系统、网站国际化与本地化、以及基于购买数据的商品推荐引擎。
1. 优惠券系统
在线优惠券是电商平台常用的营销工具,通常由一串字符代码构成,具有有效期和使用次数限制。我们将实现一个基于百分比的折扣优惠券系统,该优惠券在有效期内可多次使用,用户输入优惠码后,购物车总价将自动计算折扣。
1.1 创建优惠券应用
首先在项目中创建新应用:
python manage.py startapp coupons
在 settings.py 中注册:
INSTALLED_APPS = [
# ...
'coupons.apps.CouponsConfig',
]
1.2 定义优惠券数据模型
编辑 coupons/models.py:
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
class Coupon(models.Model):
code = models.CharField(max_length=50, unique=True)
valid_from = models.DateTimeField()
valid_to = models.DateTimeField()
discount = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)])
active = models.BooleanField()
def __str__(self):
return self.code
模型说明:code 存储唯一优惠码,valid_from 和 valid_to 定义有效期,discount 为0-100的整数折扣百分比,active 控制是否启用。
执行数据库迁移并在 admin.py 中注册:
from django.contrib import admin
from .models import Coupon
class CouponAdmin(admin.ModelAdmin):
list_display = ['code', 'valid_from', 'valid_to', 'discount', 'active']
list_filter = ['active', 'valid_from', 'valid_to']
search_fields = ['code']
admin.site.register(Coupon, CouponAdmin)
1.3 实现购物车优惠逻辑
创建 coupons/forms.py 用于用户输入优惠码:
from django import forms
class CouponApplyForm(forms.Form):
code = forms.CharField()
编写视图处理优惠码验证:
# coupons/views.py
from django.shortcuts import redirect
from django.utils import timezone
from django.views.decorators.http import require_POST
from .models import Coupon
from .forms import CouponApplyForm
@require_POST
def coupon_apply(request):
now = timezone.now()
form = CouponApplyForm(request.POST)
if form.is_valid():
code = form.cleaned_data['code']
try:
coupon = Coupon.objects.get(
code__iexact=code,
valid_from__lte=now,
valid_to__gte=now,
active=True
)
request.session['coupon_id'] = coupon.id
except Coupon.DoesNotExist:
request.session['coupon_id'] = None
return redirect('cart:cart_detail')
配置URL路由:
# coupons/urls.py
from django.urls import path
from . import views
app_name = 'coupons'
urlpatterns = [
path('apply/', views.coupon_apply, name='apply'),
]
在主路由中包含:
urlpatterns += [
path('coupons/', include('coupons.urls', namespace='coupons')),
]
1.4 扩展购物车类
修改 cart/cart.py,在 Cart 类中添加优惠相关方法:
from coupons.models import Coupon
from decimal import Decimal
class Cart:
def __init__(self, request):
# ... 原有代码 ...
self.coupon_id = self.session.get('coupon_id')
@property
def coupon(self):
if self.coupon_id:
try:
return Coupon.objects.get(id=self.coupon_id)
except Coupon.DoesNotExist:
return None
return None
def get_discount(self):
if self.coupon:
return (self.coupon.discount / Decimal('100')) * self.get_total_price()
return Decimal('0')
def get_total_price_after_discount(self):
return self.get_total_price() - self.get_discount()
1.5 更新订单模型和视图
为 Order 模型添加字段:
# orders/models.py
from decimal import Decimal
from django.core.validators import MinValueValidator, MaxValueValidator
from coupons.models import Coupon
class Order(models.Model):
# ... 原有字段 ...
coupon = models.ForeignKey(Coupon, related_name='orders', null=True, blank=True, on_delete=models.SET_NULL)
discount = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)])
def get_total_cost(self):
total_cost = sum(item.get_cost() for item in self.items.all())
return total_cost - total_cost * (self.discount / Decimal('100'))
在订单创建视图中保存优惠信息:
# orders/views.py
def order_create(request):
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
if cart.coupon:
order.coupon = cart.coupon
order.discount = cart.coupon.discount
order.save()
# ... 创建 OrderItem 并清空购物车 ...
request.session['coupon_id'] = None # 清除优惠券session
# ... 后续处理 ...
2. 国际化与本地化
Django 提供完善的 i18n 框架,支持多语言翻译、本地化格式和时区处理。我们将为站点添加英语和西班牙语支持。
2.1 基础配置
在 settings.py 中配置:
from django.utils.translation import gettext_lazy as _
LANGUAGES = (
('en', _('English')),
('es', _('Spanish')),
)
LANGUAGE_CODE = 'en'
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale/'),
)
添加语言中间件,注意放置顺序:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
# ...
]
在项目根目录创建 locale/ 目录,包含 en/ 和 es/ 子目录。
2.2 翻译字符串
在模型和表单中使用 gettext_lazy 标记翻译:
# orders/models.py
from django.utils.translation import gettext_lazy as _
class Order(models.Model):
first_name = models.CharField(_('first name'), max_length=50)
# ... 其他字段
生成翻译文件:
django-admin makemessages --all
编辑 .po 文件添加翻译,然后编译:
django-admin compilemessages
2.3 使用 Rosetta 管理翻译
安装 django-rosetta:
pip install django-rosetta
配置路由:
urlpatterns += [
path('rosetta/', include('rosetta.urls')),
]
通过 /rosetta/ 路径访问管理界面,可直接编辑翻译并自动编译。
2.4 翻译URL模式
使用 i18n_patterns 为URL添加语言前缀:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
path(_('admin/'), admin.site.urls),
path(_('cart/'), include('cart.urls', namespace='cart')),
path(_('orders/'), include('orders.urls', namespace='orders')),
path(_('payment/'), include('payment.urls', namespace='payment')),
path(_('coupons/'), include('coupons.urls', namespace='coupons')),
path('', include('shop.urls', namespace='shop')),
)
2.5 添加语言选择器
在模板中添加切换语言的功能:
{# base.html #}
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
<div class="languages">
{% trans "Language" %}:
<ul>
{% for language in languages %}
<li>
<a href="/{{ language.code }}/"
{% if language.code == LANGUAGE_CODE %}class="selected"{% endif %}>
{{ language.name_local }}
</a>
</li>
{% endfor %}
</ul>
</div>
2.6 模型字段翻译
安装 django-parler:
pip install django-parler
配置:
PARLER_LANGUAGES = {
None: (
{'code': 'en'},
{'code': 'es'},
),
'default': {
'fallback': 'en',
'hide_untranslated': False,
}
}
修改模型使用 TranslatableModel:
from parler.models import TranslatableModel, TranslatedFields
class Product(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
slug=models.SlugField(max_length=200),
description=models.TextField(blank=True)
)
# ... 其他字段
更新管理后台:
from parler.admin import TranslatableAdmin
@admin.register(Product)
class ProductAdmin(TranslatableAdmin):
list_display = ['name', 'price', 'available']
# ...
3. 商品推荐系统
基于 Redis 实现协同过滤推荐,记录经常被一起购买的商品组合。
3.1 安装和配置 Redis
确保 Redis 服务运行,安装 Python 客户端:
pip install redis
配置连接参数:
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 1
3.2 实现推荐引擎
创建 shop/recommender.py:
import redis
from django.conf import settings
from .models import Product
r = redis.StrictRedis(
host=settings.REDIS_HOST,
port=settings.REDIS_PORT,
db=settings.REDIS_DB
)
class Recommender:
def get_product_key(self, product_id):
return f'product:{product_id}:purchased_with'
def products_bought(self, products):
product_ids = [p.id for p in products]
for product_id in product_ids:
for with_id in product_ids:
if product_id != with_id:
r.zincrby(
self.get_product_key(product_id),
1,
with_id
)
def suggest_products_for(self, products, max_results=6):
product_ids = [p.id for p in products]
if len(product_ids) == 1:
suggestions = r.zrange(
self.get_product_key(product_ids[0]),
0, -1, desc=True
)[:max_results]
else:
flat_ids = ''.join(str(id) for id in product_ids)
tmp_key = f'tmp_{flat_ids}'
keys = [self.get_product_key(id) for id in product_ids]
r.zunionstore(tmp_key, keys)
r.zrem(tmp_key, *product_ids)
suggestions = r.zrange(tmp_key, 0, -1, desc=True)[:max_results]
r.delete(tmp_key)
suggested_ids = [int(id) for id in suggestions]
suggested_products = list(
Product.objects.filter(id__in=suggested_ids)
)
suggested_products.sort(
key=lambda x: suggested_ids.index(x.id)
)
return suggested_products
def clear_purchases(self):
for pid in Product.objects.values_list('id', flat=True):
r.delete(self.get_product_key(pid))
3.3 在视图中集成推荐
在商品详情视图添加推荐:
# shop/views.py
from .recommender import Recommender
def product_detail(request, id, slug):
# ... 获取商品对象 ...
r = Recommender()
recommended = r.suggest_products_for([product], 4)
return render(request, 'shop/product/detail.html', {
'product': product,
'recommended_products': recommended,
# ...
})
在购物车视图添加推荐:
# cart/views.py
from shop.recommender import Recommender
def cart_detail(request):
cart = Cart(request)
# ... 其他代码 ...
r = Recommender()
cart_products = [item['product'] for item in cart]
recommended = r.suggest_products_for(cart_products, 4)
return render(request, 'cart/detail.html', {
'cart': cart,
'recommended_products': recommended,
# ...
})
3.4 更新购买数据
在支付成功时记录商品关联:
# payment/views.py
from shop.recommender import Recommender
def payment_process(request):
# ... 支付处理 ...
if payment_success:
order.paid = True
order.save()
r = Recommender()
items = [item.product for item in order.items.all()]
r.products_bought(items)
总结
本章实现了三个核心功能:基于 session 的优惠券系统,多语言国际化支持,以及基于 Redis 的商品推荐引擎。这些功能显著提升了电商平台的实用性和用户体验。下一章将构建在线教育平台,重点使用 Django 的类视图和内容管理系统。