Python全栈项目:基于Django的电子商务平台编写
2025-12-02 16:55 tlnshuju 阅读(0) 评论(0) 收藏 举报项目概述
随着互联网经济的快速发展,电子商务已成为商业活动的重要组成部分。本文将详细介绍如何使用Python的Django框架开发一个功能完整的电子商务平台,涵盖从项目架构设计到具体实现的全过程。
这个项目将实现一个现代化的B2C电商平台,包括商品展示、购物车、订单管理、支付集成、用户系统等核心功能。通过这个项目,我们可以深入理解全栈开发的完整流程。
技术栈选型
后端技术
- Django 4.2+: 成熟稳定的Web框架,提供完善的ORM、认证系统和管理后台
- Django REST Framework: 构建RESTful API,实现前后端分离
- Celery: 处理异步任务,如发送邮件、生成报表
- Redis: 缓存系统和消息队列
- PostgreSQL: 主数据库,提供强大的数据完整性保证
前端技术
- Vue.js 3 / React: 构建动态用户界面
- Bootstrap 5 / Tailwind CSS: 响应式UI框架
- Axios: HTTP客户端,处理API请求
- Webpack: 模块打包工具
第三方集成
- Stripe / PayPal: 支付网关
- Aliyun OSS / AWS S3: 对象存储服务
- SendGrid / 阿里云邮件: 邮件服务
- Elasticsearch: 全文搜索引擎
项目架构设计
整体架构
ecommerce-platform/
├── apps/
│ ├── users/ # 用户管理模块
│ ├── products/ # 商品管理模块
│ ├── orders/ # 订单管理模块
│ ├── cart/ # 购物车模块
│ ├── payment/ # 支付模块
│ └── reviews/ # 评论模块
├── config/
│ ├── settings/
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── static/
├── media/
├── templates/
├── requirements/
└── manage.py
数据库设计原则
采用规范化设计,确保数据一致性和完整性。主要遵循以下原则:
- 第三范式设计,避免数据冗余
- 合理使用外键关系
- 为高频查询字段建立索引
- 敏感信息加密存储
核心功能模块实现
1. 用户管理系统
用户系统是电商平台的基础,需要实现完整的认证和授权机制。
# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
"""扩展用户模型"""
phone = models.CharField(max_length=11, unique=True, null=True, blank=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
birthday = models.DateField(null=True, blank=True)
is_verified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'users'
verbose_name = '用户'
verbose_name_plural = verbose_name
class UserAddress(models.Model):
"""用户收货地址"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='addresses')
receiver = models.CharField(max_length=50, verbose_name='收货人')
province = models.CharField(max_length=50, verbose_name='省份')
city = models.CharField(max_length=50, verbose_name='城市')
district = models.CharField(max_length=50, verbose_name='区县')
detail = models.CharField(max_length=200, verbose_name='详细地址')
phone = models.CharField(max_length=11, verbose_name='联系电话')
is_default = models.BooleanField(default=False, verbose_name='默认地址')
class Meta:
db_table = 'user_addresses'
verbose_name = '用户地址'
verbose_name_plural = verbose_name
实现JWT认证机制:
# users/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import authenticate
class UserLoginView(APIView):
"""用户登录"""
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
refresh = RefreshToken.for_user(user)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
'user': {
'id': user.id,
'username': user.username,
'email': user.email,
}
})
return Response(
{'error': '用户名或密码错误'},
status=status.HTTP_401_UNAUTHORIZED
)
2. 商品管理模块
商品模块是电商平台的核心,需要支持分类、SPU/SKU、库存管理等功能。
# products/models.py
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Category(models.Model):
"""商品分类"""
name = models.CharField(max_length=50, verbose_name='分类名称')
parent = models.ForeignKey('self', on_delete=models.CASCADE,
null=True, blank=True, related_name='children')
sort = models.IntegerField(default=0, verbose_name='排序')
is_active = models.BooleanField(default=True, verbose_name='是否启用')
class Meta:
db_table = 'categories'
verbose_name = '商品分类'
verbose_name_plural = verbose_name
class Product(models.Model):
"""商品SPU"""
name = models.CharField(max_length=200, verbose_name='商品名称')
category = models.ForeignKey(Category, on_delete=models.PROTECT,
related_name='products')
description = models.TextField(verbose_name='商品描述')
main_image = models.ImageField(upload_to='products/', verbose_name='主图')
is_on_sale = models.BooleanField(default=True, verbose_name='是否上架')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'products'
verbose_name = '商品'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['category', 'is_on_sale']),
]
class ProductSKU(models.Model):
"""商品SKU"""
product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name='skus')
sku_code = models.CharField(max_length=50, unique=True, verbose_name='SKU编码')
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格')
stock = models.IntegerField(default=0, verbose_name='库存')
specs = models.JSONField(verbose_name='规格信息')
image = models.ImageField(upload_to='skus/', null=True, verbose_name='SKU图片')
class Meta:
db_table = 'product_skus'
verbose_name = '商品SKU'
verbose_name_plural = verbose_name
class ProductImage(models.Model):
"""商品图片"""
product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name='images')
image = models.ImageField(upload_to='products/gallery/')
sort = models.IntegerField(default=0)
class Meta:
db_table = 'product_images'
ordering = ['sort']
实现商品搜索功能:
# products/views.py
from rest_framework.generics import ListAPIView
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer
from .filters import ProductFilter
class ProductListView(ListAPIView):
"""商品列表"""
queryset = Product.objects.filter(is_on_sale=True).select_related('category')
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = ProductFilter
search_fields = ['name', 'description']
ordering_fields = ['created_at', 'price']
ordering = ['-created_at']
3. 购物车系统
购物车需要支持未登录用户(基于Session)和已登录用户(基于数据库)两种场景。
# cart/models.py
from django.db import models
from django.contrib.auth import get_user_model
from apps.products.models import ProductSKU
User = get_user_model()
class CartItem(models.Model):
"""购物车商品"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cart_items')
sku = models.ForeignKey(ProductSKU, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1, verbose_name='数量')
is_selected = models.BooleanField(default=True, verbose_name='是否选中')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'cart_items'
unique_together = ['user', 'sku']
verbose_name = '购物车商品'
verbose_name_plural = verbose_name
购物车业务逻辑:
# cart/services.py
from django.core.cache import cache
from .models import CartItem
from apps.products.models import ProductSKU
class CartService:
"""购物车服务"""
@staticmethod
def add_to_cart(user, sku_id, quantity=1):
"""添加商品到购物车"""
try:
sku = ProductSKU.objects.get(id=sku_id)
if sku.stock < quantity:
return None, "库存不足"
cart_item, created = CartItem.objects.get_or_create(
user=user,
sku=sku,
defaults={'quantity': quantity}
)
if not created:
cart_item.quantity += quantity
if cart_item.quantity > sku.stock:
return None, "超出库存限制"
cart_item.save()
return cart_item, None
except ProductSKU.DoesNotExist:
return None, "商品不存在"
@staticmethod
def get_cart_items(user):
"""获取购物车商品列表"""
return CartItem.objects.filter(user=user).select_related(
'sku__product'
).order_by('-created_at')
@staticmethod
def calculate_total(user):
"""计算购物车总价"""
items = CartItem.objects.filter(user=user, is_selected=True)
total = sum(item.sku.price * item.quantity for item in items)
return total
4. 订单管理系统
订单系统需要处理订单创建、状态流转、库存扣减等复杂业务逻辑。
# orders/models.py
from django.db import models
from django.contrib.auth import get_user_model
from apps.products.models import ProductSKU
User = get_user_model()
class Order(models.Model):
"""订单"""
STATUS_CHOICES = (
('PENDING', '待支付'),
('PAID', '已支付'),
('SHIPPED', '已发货'),
('COMPLETED', '已完成'),
('CANCELLED', '已取消'),
('REFUNDING', '退款中'),
('REFUNDED', '已退款'),
)
order_no = models.CharField(max_length=32, unique=True, verbose_name='订单号')
user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='orders')
status = models.CharField(max_length=20, choices=STATUS_CHOICES,
default='PENDING', verbose_name='订单状态')
total_amount = models.DecimalField(max_digits=10, decimal_places=2,
verbose_name='订单总额')
shipping_fee = models.DecimalField(max_digits=10, decimal_places=2,
default=0, verbose_name='运费')
actual_amount = models.DecimalField(max_digits=10, decimal_places=2,
verbose_name='实付金额')
# 收货信息
receiver = models.CharField(max_length=50, verbose_name='收货人')
phone = models.CharField(max_length=11, verbose_name='联系电话')
province = models.CharField(max_length=50, verbose_name='省份')
city = models.CharField(max_length=50, verbose_name='城市')
district = models.CharField(max_length=50, verbose_name='区县')
address = models.CharField(max_length=200, verbose_name='详细地址')
remark = models.TextField(blank=True, verbose_name='备注')
paid_at = models.DateTimeField(null=True, blank=True, verbose_name='支付时间')
shipped_at = models.DateTimeField(null=True, blank=True, verbose_name='发货时间')
completed_at = models.DateTimeField(null=True, blank=True, verbose_name='完成时间')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'orders'
verbose_name = '订单'
verbose_name_plural = verbose_name
ordering = ['-created_at']
indexes = [
models.Index(fields=['user', 'status']),
models.Index(fields=['order_no']),
]
class OrderItem(models.Model):
"""订单商品"""
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
sku = models.ForeignKey(ProductSKU, on_delete=models.PROTECT)
product_name = models.CharField(max_length=200, verbose_name='商品名称')
product_image = models.CharField(max_length=500, verbose_name='商品图片')
specs = models.JSONField(verbose_name='规格信息')
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='单价')
quantity = models.IntegerField(verbose_name='数量')
subtotal = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='小计')
class Meta:
db_table = 'order_items'
verbose_name = '订单商品'
verbose_name_plural = verbose_name
订单创建服务(使用事务确保数据一致性):
# orders/services.py
from django.db import transaction
from django.utils import timezone
import uuid
from .models import Order, OrderItem
from apps.cart.models import CartItem
from apps.products.models import ProductSKU
class OrderService:
"""订单服务"""
@staticmethod
@transaction.atomic
def create_order(user, address_id, cart_item_ids):
"""创建订单"""
# 获取购物车商品
cart_items = CartItem.objects.filter(
id__in=cart_item_ids,
user=user,
is_selected=True
).select_related('sku__product')
if not cart_items:
raise ValueError("没有选中的商品")
# 检查库存并锁定库存
for item in cart_items:
sku = ProductSKU.objects.select_for_update().get(id=item.sku.id)
if sku.stock < item.quantity:
raise ValueError(f"商品{sku.product.name}库存不足")
# 获取收货地址
from apps.users.models import UserAddress
address = UserAddress.objects.get(id=address_id, user=user)
# 计算订单金额
total_amount = sum(item.sku.price * item.quantity for item in cart_items)
shipping_fee = 0 # 运费计算逻辑
actual_amount = total_amount + shipping_fee
# 创建订单
order = Order.objects.create(
order_no=OrderService.generate_order_no(),
user=user,
total_amount=total_amount,
shipping_fee=shipping_fee,
actual_amount=actual_amount,
receiver=address.receiver,
phone=address.phone,
province=address.province,
city=address.city,
district=address.district,
address=address.detail,
)
# 创建订单商品
order_items = []
for item in cart_items:
order_items.append(OrderItem(
order=order,
sku=item.sku,
product_name=item.sku.product.name,
product_image=item.sku.image.url if item.sku.image else '',
specs=item.sku.specs,
price=item.sku.price,
quantity=item.quantity,
subtotal=item.sku.price * item.quantity
))
# 扣减库存
item.sku.stock -= item.quantity
item.sku.save()
OrderItem.objects.bulk_create(order_items)
# 清空购物车
cart_items.delete()
return order
@staticmethod
def generate_order_no():
"""生成订单号"""
return timezone.now().strftime('%Y%m%d%H%M%S') + str(uuid.uuid4().hex[:8]).upper()
5. 支付集成
集成第三方支付平台,以Stripe为例:
# payment/services.py
import stripe
from django.conf import settings
from apps.orders.models import Order
stripe.api_key = settings.STRIPE_SECRET_KEY
class PaymentService:
"""支付服务"""
@staticmethod
def create_payment_intent(order_id):
"""创建支付意图"""
try:
order = Order.objects.get(id=order_id, status='PENDING')
intent = stripe.PaymentIntent.create(
amount=int(order.actual_amount * 100), # 转换为分
currency='cny',
metadata={'order_id': order.id, 'order_no': order.order_no},
description=f'订单支付 - {order.order_no}'
)
return {
'client_secret': intent.client_secret,
'payment_intent_id': intent.id
}
except Order.DoesNotExist:
raise ValueError("订单不存在")
@staticmethod
@transaction.atomic
def handle_payment_success(payment_intent_id):
"""处理支付成功"""
intent = stripe.PaymentIntent.retrieve(payment_intent_id)
order_id = intent.metadata.get('order_id')
order = Order.objects.select_for_update().get(id=order_id)
order.status = 'PAID'
order.paid_at = timezone.now()
order.save()
# 发送支付成功通知
from .tasks import send_payment_notification
send_payment_notification.delay(order.id)
return order
6. 异步任务处理
使用Celery处理耗时任务:
# payment/tasks.py
from celery import shared_task
from django.core.mail import send_mail
from django.conf import settings
from apps.orders.models import Order
@shared_task
def send_payment_notification(order_id):
"""发送支付成功通知"""
try:
order = Order.objects.get(id=order_id)
subject = f'订单支付成功 - {order.order_no}'
message = f'''
尊敬的{order.receiver}:
您的订单{order.order_no}已支付成功!
订单金额:¥{order.actual_amount}
我们会尽快为您安排发货。
'''
send_mail(
subject,
message,
settings.DEFAULT_FROM_EMAIL,
[order.user.email],
fail_silently=False,
)
return f"通知邮件已发送至{order.user.email}"
except Order.DoesNotExist:
return f"订单{order_id}不存在"
@shared_task
def generate_sales_report():
"""生成销售报表"""
from django.utils import timezone
from datetime import timedelta
end_date = timezone.now()
start_date = end_date - timedelta(days=30)
orders = Order.objects.filter(
status='COMPLETED',
completed_at__range=[start_date, end_date]
)
total_sales = sum(order.actual_amount for order in orders)
total_orders = orders.count()
# 生成报表逻辑
report_data = {
'period': f"{start_date.date()} 至 {end_date.date()}",
'total_sales': total_sales,
'total_orders': total_orders,
'average_order_value': total_sales / total_orders if total_orders > 0 else 0
}
return report_data
前端实现
Vue.js 商品列表组件
{{ product.name }}
¥{{ product.min_price }}
第 {{ currentPage }} 页
<script>
import { ref, reactive, onMounted } from 'vue'
import axios from 'axios'
export default {
name: 'ProductList',
setup() {
const products = ref([])
const categories = ref([])
const currentPage = ref(1)
const hasPrev = ref(false)
const hasNext = ref(false)
const filters = reactive({
category: '',
ordering: '-created_at',
search: ''
})
const loadProducts = async () => {
try {
const params = {
page: currentPage.value,
...filters
}
const response = await axios.get('/api/products/', { params })
products.value = response.data.results
hasPrev.value = !!response.data.previous
hasNext.value = !!response.data.next
} catch (error) {
console.error('加载商品失败:', error)
}
}
const loadCategories = async () => {
try {
const response = await axios.get('/api/categories/')
categories.value = response.data
} catch (error) {
console.error('加载分类失败:', error)
}
}
const addToCart = async (product) => {
try {
await axios.post('/api/cart/', {
sku_id: product.default_sku_id,
quantity: 1
})
alert('添加成功!')
} catch (error) {
console.error('添加失败:', error)
}
}
const prevPage = () => {
if (hasPrev.value) {
currentPage.value--
loadProducts()
}
}
const nextPage = () => {
if (hasNext.value) {
currentPage.value++
loadProducts()
}
}
onMounted(() => {
loadProducts()
loadCategories()
})
return {
products,
categories,
filters,
currentPage,
hasPrev,
hasNext,
loadProducts,
addToCart,
prevPage,
nextPage
}
}
}
</script>
性能优化策略
1. 数据库优化
# 使用select_related和prefetch_related减少查询次数
products = Product.objects.select_related('category').prefetch_related(
'skus', 'images'
).filter(is_on_sale=True)
# 使用索引
class Product(models.Model):
class Meta:
indexes = [
models.Index(fields=['category', '-created_at']),
models.Index(fields=['is_on_sale', '-created_at']),
]
# 使用数据库连接池
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'CONN_MAX_AGE': 600, # 连接池
'OPTIONS': {
'connect_timeout': 10,
}
}
}
2. 缓存策略
# settings.py - Redis缓存配置
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'PARSER_CLASS': 'redis.connection.HiredisParser',
'CONNECTION_POOL_KWARGS': {'max_connections': 50},
}
}
}
# 使用缓存
from django.core.cache import cache
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 缓存15分钟
def product_list(request):
products = Product.objects.filter(is_on_sale=True)
return render(request, 'products/list.html', {'products': products})
# 手动缓存
def get_hot_products():
cache_key = 'hot_products'
products = cache.get(cache_key)
if products is None:
products = list(Product.objects.filter(
is_on_sale=True
).order_by('-sales')[:10])
cache.set(cache_key, products, 60 * 30) # 缓存30分钟
return products
3. 静态资源优化
# settings.py
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# 使用CDN加速
if not DEBUG:
STATIC_URL = 'https://cdn.example.com/static/'
MEDIA_URL = 'https://cdn.example.com/media/'
# 启用Gzip压缩
MIDDLEWARE = [
'django.middleware.gzip.GZipMiddleware',
# ...
]
# 图片压缩和缩略图
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
def compress_image(image, quality=85):
"""压缩图片"""
img = Image.open(image)
if img.mode != 'RGB':
img = img.convert('RGB')
output = BytesIO()
img.save(output, format='JPEG', quality=quality, optimize=True)
output.seek(0)
return InMemoryUploadedFile(
output, 'ImageField',
f"{image.name.split('.')[0]}.jpg",
'image/jpeg', output.tell(), None
)
部署方案
1. Docker容器化部署
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
gcc \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制项目文件
COPY . .
# 收集静态文件
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "config.wsgi:application"]
# docker-compose.yml
version: '3.8'
services:
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ecommerce
POSTGRES_USER: ecommerce_user
POSTGRES_PASSWORD: ${DB_PASSWORD}
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
web:
build: .
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/app
- static_volume:/app/staticfiles
- media_volume:/app/media
ports:
- "8000:8000"
depends_on:
- db
- redis
environment:
- DEBUG=False
- DATABASE_URL=postgresql://ecommerce_user:${DB_PASSWORD}@db:5432/ecommerce
- REDIS_URL=redis://redis:6379/0
celery:
build: .
command: celery -A config worker -l info
volumes:
- .:/app
depends_on:
- db
- redis
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- static_volume:/app/staticfiles
- media_volume:/app/media
- ./ssl:/etc/nginx/ssl
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:
2. Nginx配置
# nginx.conf
upstream django {
server web:8000;
}
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
client_max_body_size 20M;
location / {
proxy_pass http://django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
location /static/ {
alias /app/staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /app/media/;
expires 7d;
}
}
安全措施
1. 基础安全配置
# settings/production.py
DEBUG = False
ALLOWED_HOSTS = ['example.com', 'www.example.com']
# HTTPS设置
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
# 密码验证
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 8}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
2. API限流
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/hour',
'user': '1000/hour'
}
}
# 自定义限流类
from rest_framework.throttling import UserRateThrottle
class PaymentRateThrottle(UserRateThrottle):
rate = '10/hour' # 支付接口每小时最多10次
3. SQL注入防护
# 始终使用Django ORM的参数化查询
# 正确方式
products = Product.objects.filter(name__icontains=search_term)
# 错误方式 - 容易SQL注入
# cursor.execute(f"SELECT * FROM products WHERE name LIKE '%{search_term}%'")
# 如果必须使用原生SQL
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM products WHERE name LIKE %s", [f'%{search_term}%'])
监控与日志
日志配置
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/ecommerce.log',
'maxBytes': 1024 * 1024 * 10, # 10MB
'backupCount': 5,
'formatter': 'verbose',
},
'error_file': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/error.log',
'maxBytes': 1024 * 1024 * 10,
'backupCount': 5,
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file', 'error_file'],
'level': 'INFO',
'propagate': True,
},
'apps': {
'handlers': ['file', 'error_file'],
'level': 'INFO',
'propagate': False,
},
},
}
测试策略
单元测试示例
# tests/test_orders.py
from django.test import TestCase
from django.contrib.auth import get_user_model
from apps.orders.services import OrderService
from apps.products.models import Product, ProductSKU
from apps.users.models import UserAddress
User = get_user_model()
class OrderServiceTest(TestCase):
def setUp(self):
"""测试前准备"""
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
self.product = Product.objects.create(
name='测试商品',
description='测试描述'
)
self.sku = ProductSKU.objects.create(
product=self.product,
sku_code='TEST001',
price=99.99,
stock=100
)
self.address = UserAddress.objects.create(
user=self.user,
receiver='张三',
province='北京',
city='北京市',
district='朝阳区',
detail='xxx街道',
phone='13800138000'
)
def test_create_order_success(self):
"""测试创建订单成功"""
from apps.cart.models import CartItem
cart_item = CartItem.objects.create(
user=self.user,
sku=self.sku,
quantity=2
)
order = OrderService.create_order(
user=self.user,
address_id=self.address.id,
cart_item_ids=[cart_item.id]
)
self.assertIsNotNone(order)
self.assertEqual(order.user, self.user)
self.assertEqual(order.items.count(), 1)
self.assertEqual(order.total_amount, 199.98)
# 验证库存扣减
self.sku.refresh_from_db()
self.assertEqual(self.sku.stock, 98)
# 验证购物车清空
self.assertEqual(CartItem.objects.filter(user=self.user).count(), 0)
def test_create_order_insufficient_stock(self):
"""测试库存不足"""
from apps.cart.models import CartItem
cart_item = CartItem.objects.create(
user=self.user,
sku=self.sku,
quantity=101 # 超过库存
)
with self.assertRaises(ValueError):
OrderService.create_order(
user=self.user,
address_id=self.address.id,
cart_item_ids=[cart_item.id]
)
API测试
# tests/test_api.py
from rest_framework.test import APITestCase
from rest_framework import status
from django.contrib.auth import get_user_model
User = get_user_model()
class ProductAPITest(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
def test_get_product_list(self):
"""测试获取商品列表"""
response = self.client.get('/api/products/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_add_to_cart_authenticated(self):
"""测试已登录用户添加购物车"""
self.client.force_authenticate(user=self.user)
data = {
'sku_id': 1,
'quantity': 2
}
response = self.client.post('/api/cart/', data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
项目总结
通过这个完整的Django电子商务平台项目,我们实现了以下核心功能:
技术亮点
- 前后端分离架构: 使用Django REST Framework构建API,前端使用Vue.js实现响应式界面
- 分布式任务处理: 使用Celery处理异步任务,提升系统响应速度
- 高性能缓存: 通过Redis实现多层缓存策略,大幅提升查询效率
- 安全防护: 实施多层安全措施,包括HTTPS、SQL注入防护、CSRF防护等
- 容器化部署: 使用Docker实现一键部署,简化运维流程
可扩展功能
- 搜索优化: 集成Elasticsearch实现全文搜索和搜索建议
- 推荐系统: 基于用户行为的协同过滤推荐算法
- 秒杀系统: 高并发场景下的库存控制和限流策略
- 物流跟踪: 集成第三方物流API实现物流实时追踪
- 数据分析: 用户行为分析和销售数据可视化
- 移动端适配: 开发React Native或Flutter移动应用
- 国际化: 支持多语言和多货币
最佳实践
- 代码规范: 遵循PEP 8编码规范,使用pylint和black进行代码检查
- 版本控制: 使用Git进行版本管理,采用Git Flow工作流
- 文档完善: 编写详细的API文档和开发文档
- 持续集成: 配置CI/CD流程,自动化测试和部署
- 性能监控: 使用APM工具监控应用性能
学习收获
开发这样一个完整的电商平台,可以让我们深入理解:
- Web应用的完整开发流程
- 前后端分离架构的设计思想
- 数据库设计和优化技巧
- 分布式系统的架构设计
- 高并发场景的处理方案
- 生产环境的部署和运维
这个项目为进一步学习微服务架构、云原生应用开发打下了坚实的基础。
浙公网安备 33010602011771号