前言
哈喽,大家好,我是海鸽。
相信很多 python 的小伙伴已经很熟悉Django
的开发模式了,但是其中有部分小伙伴,可能会相当苦恼自己的Django
正在变成一个由视图和模型组成的巨大乱麻,且它们还在不停地直接交互?
那么,要如何解决这类问题呢?
有一句话是这么说的:没有什么是一包辣条不能解决的!如果有。那就两包。
计算机科学领域的任何问题都可以通过增加一个中间层来解决。
Any problem in computer science can be solved by an additional level of indirection。
楔子
刚好,最近一位小伙伴给海鸽发来一道面试题,其大意如下:
【题目:Django 批量订单处理与商品搜索系统】
- 背景场景: 您需要基于 Django 搭建一个小型电商模块,要求同时支持"批量订单处理 "与"商品搜索 "两大功能,并能在高并发场景下稳定运行 。该系统可能被多个外部 API 或前端调用,需确保数据的正确性与一致性。
【核心需求】:
- 批量订单处理
- 一次提交中,可下单多个不同商品;要求逐一扣减库存,并记录订单明细。
- 任一商品库存不足时,需返回对应的失败信息,不影响其他商品的下单。
- 要防止超卖、重复扣减等问题,需设计适当的锁或并发控制机制(可使用悲观锁、乐观锁或分布式锁等)。
- 商品搜索功能
- 提供简单的商品搜索接口,可按商品名称、关键词等进行查询。
- 大部分查询场景下会是高频读操作,需要使用缓存(如 Redis)设计好批量数据库搜索。
- 缓存需与数据库保持一致,当商品信息更新或售罄时,需更新或失效缓存。 数据库与缓存。
- 请自行设计核心数据表与缓存结构(如存放库存、价格、关键词索引等)。
- 说明当库存或商品信息改变时,如何更新数据库与缓存?若缓存连接失败或不可用时,如何降级或回退?
- 异常处理与可扩展性
- 业务异常:如库存不足、订单格式非法、搜索关键字为空等,要有清晰的捕获与返回。
- 系统异常:如数据库或缓存宕机,需要记录日志并提供合理的恢复或补偿策略。
- 要求分层设计(例如:models、views、services/业务逻辑层、cache 管理等),保证后续扩容或二次开发时可顺利集成分库分表、消息队列异步处理、限流等高级功能。
好家伙,现在已经这样面试了吗?其中一项重要要求便是:分层设计!
感兴趣的可以试试上面的面试题能得几分,文末有上述需求粗略的评分标准。
这时肯定就有人跳出来了,"使用 Django 的 MVC 模式不就行了"。
是的,没错。
不过,海鸽是见过那种一个视图函数里面塞满业务逻辑的,说多了都是泪啊。
当然,一些有经验的程序员、架构会采用 DDD 的设计,或者至少分出Control
、Service
等层,使逻辑清晰,或便于维护。
对 python 架构感兴趣的可以看看这本书:《Python的架构模式》。
今天就引入的其中一个使代码干净的专业技巧: 存储库模式(Repository Pattern)
。
什么是存储库模式(Repository Pattern)?
存储库设计模式是一种抽象数据访问的结构模式,提供了一种集中的方式来管理数据操作。
通过将数据层与业务逻辑分离,它增强了代码的可维护性、可测试性和灵活性,从而更轻松地在应用程序中使用各种数据源。
简单来说:
**存储库充当数据库(模型)和视图/服务之间的中间层。**它处理所有数据库操作,因此您的视图保持整洁并专注于逻辑。
存储库模式和 ORM 的区别
在 Django 中,存储库模式和直接使用 ORM 存在显著差异,主要体现在代码结构、职责划分和灵活性上,具体区别如下:
1. 核心概念不同
- 直接使用 ORM :Django 的 ORM(如
Model.objects
方法)允许直接在业务逻辑中编写数据查询代码(如User.objects.filter(age__gt=18)
),数据访问逻辑与业务逻辑紧密结合。 - 存储库模式:引入一个中间层(存储库类),统一负责数据的查询、保存、删除等操作。业务逻辑不直接调用 ORM,而是通过存储库类间接操作数据,将数据访问逻辑与业务逻辑分离。
2. 代码组织方式不同
-
直接使用 ORM :查询代码通常分散在视图、服务等业务逻辑中。例如:
python# 视图中直接使用 ORM def get_adults(request): adults = User.objects.filter(age__gt=18) return render(request, 'adults.html', {'adults': adults})
-
存储库模式 :将所有数据操作封装在专门的存储库类中,业务逻辑通过调用存储库方法获取数据。例如:
python# 存储库类封装 ORM 操作 class UserRepository: @staticmethod def get_adults(): return User.objects.filter(age__gt=18) # 视图中调用存储库 def get_adults(request): adults = UserRepository.get_adults() return render(request, 'adults.html', {'adults': adults})
3. 灵活性与可测试性不同
- 直接使用 ORM :
- 优点:简单直接,适合小型项目,减少代码量。
- 缺点:业务逻辑与数据访问耦合度高,若更换数据库或 ORM,需修改大量业务代码;单元测试时难以模拟数据库操作。
- 存储库模式 :
- 优点:解耦业务逻辑与数据访问,更换数据源(如从 MySQL 换为 MongoDB)时,只需修改存储库实现,不影响业务代码;便于编写单元测试(可通过模拟存储库返回固定数据)。
- 缺点:增加代码层级,小型项目可能显得冗余。
🏗️ 案例
我们还是以电商中的商品服务(product)和 订单服务(orders)为例。
首先,看一下项目结构 🔍:
shell
├── django_repo
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── orders
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── repositories
│ │ └── order_repository.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── product
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── repositories
│ │ └── product_repository.py
│ ├── tests.py
│ └── views.py
└── utils
├── base_repository.py
└── response.py
公共的基础 repository (utils/base_repository.py):
python
from typing import TypeVar, Generic, Optional, List
from django.db import models
ModelType = TypeVar("ModelType", bound=models.Model)
class BaseRepository(Generic[ModelType]):
def __init__(self, model: type[ModelType]):
self.model = model
def get_all(self) -> List[ModelType]:
return list(self.model.objects.all())
def get_by_id(self, pk: int) -> Optional[ModelType]:
return self.model.objects.filter(id=pk).first()
def create(self, **kwargs) -> ModelType:
return self.model.objects.create(**kwargs)
def update(self, instance: ModelType, **kwargs) -> ModelType:
for attr, value in kwargs.items():
setattr(instance, attr, value)
instance.save()
return instance
def delete(self, instance: ModelType) -> None:
instance.delete()
简单统一响应(utils/response.py
python
from typing import TypeVar, Optional, Any, Union
from rest_framework.response import Response
from rest_framework import status
T = TypeVar('T')
class ApiResponse:
@staticmethod
def success(data: Optional[T] = None, msg: str = "success") -> Response:
return Response({
"code": 200,
"msg": msg,
"data": data
})
@staticmethod
def created(data: Optional[T] = None, msg: str = "created successfully") -> Response:
return Response({
"code": 201,
"msg": msg,
"data": data
}, status=status.HTTP_201_CREATED)
@staticmethod
def error(msg: str = "error", code: int = 400, data: Optional[Any] = None) -> Response:
return Response({
"code": code,
"msg": msg,
"data": data
}, status=status.HTTP_400_BAD_REQUEST)
商品服务
商品数据库模型(product/models.py)
python
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=10, decimal_places=2)
in_stock = models.BooleanField(default=True)
def __str__(self):
return self.name
产品 repository(product/repositories/product_repository.py)
python
from utils.base_repository import BaseRepository
from product.models import Product
class ProductRepository(BaseRepository):
def __init__(self):
super().__init__(Product)
def get_available_products(self):
return self.model.objects.filter(in_stock=True)
订单服务
订单数据库模型(order/models.py)
python
from django.db import models
from product.models import Product
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
ordered_at = models.DateTimeField(auto_now_add=True)
订单repository(order/repositories/order_repository.py)
python
from typing import List
from orders.models import Order
from product.models import Product
from utils.base_repository import BaseRepository
from product.repositories.product_repository import ProductRepository
class OrderRepository(BaseRepository[Order]):
def __init__(self):
super().__init__(Order)
self.product_repo = ProductRepository()
def create_order(self, product_id: int, quantity: int) -> Order:
product = self.product_repo.get_by_id(product_id)
if not product:
raise ValueError("Product not found")
if not product.in_stock:
raise ValueError("Product is out of stock")
return self.model.objects.create(product=product, quantity=quantity)
def get_all_orders(self) -> List[Order]:
return list(self.model.objects.select_related('product').all())
订单创建和查询接口(order/views.py)
python
from rest_framework.views import APIView
from .repositories.order_repository import OrderRepository
from .serializers import OrderSerializer, OrderCreateSerializer
from utils.response import ApiResponse
class OrderListCreate(APIView):
order_repo = OrderRepository()
def get(self, request):
orders = self.order_repo.get_all_orders()
serializer = OrderSerializer(orders, many=True)
return ApiResponse.success(data=serializer.data)
def post(self, request):
serializer = OrderCreateSerializer(data=request.data)
if not serializer.is_valid():
return ApiResponse.error(msg="Invalid data", data=serializer.errors)
try:
order = self.order_repo.create_order(
product_id=serializer.validated_data['product_id'],
quantity=serializer.validated_data['quantity']
)
return ApiResponse.created(data=OrderSerializer(order).data)
except ValueError as e:
return ApiResponse.error(msg=str(e))
订单接口的序列化器
python
from rest_framework import serializers
from .models import Order
from product.models import Product
class OrderCreateSerializer(serializers.Serializer):
product_id = serializers.IntegerField(required=True)
quantity = serializers.IntegerField(required=True, min_value=1)
def validate_product_id(self, value):
try:
product = Product.objects.get(id=value)
if not product.in_stock:
raise serializers.ValidationError("Product is out of stock")
return value
except Product.DoesNotExist:
raise serializers.ValidationError("Product not found")
class OrderSerializer(serializers.ModelSerializer):
product_name = serializers.CharField(source='product.name', read_only=True)
class Meta:
model = Order
fields = ['id', 'product', 'product_name', 'quantity', 'ordered_at']
OK,测试一下:

存储库模式 虽然很简单,不过确实在一定程度上能帮助我们写出干净的代码,对吧。
面试题评分标准
好了,自测看一下你能得什么等级吧!
shell
提交与说明要求:
1、使用 Django 框架,编写主要的核心代码,包括数据模型、视图或接口、缓存/锁机制等。
2、请在 Git 中进行多次提交并标注每次改动与原因
3、若是本地迭代修改,需在文档里记录每次改动的内容与原因。
2或3必须要有一份,否则视为不合格
以下是我们的评分评级标准,可参考:
等级
并发与架构
缓存与数据一致性
安全与异常处理
可扩展性与维护
S 级(卓越)
- 采用成熟的异步高并发方案(如 ASGI 框架、协程/线程池、微服务等),能在极高流量下保持低延迟
- 大规模分层或微服务拆分清晰,可灵活扩展出不同子系统,保证在极端高并发场景下也无超卖问题,系统表现稳定。
- 拥有多级或分布式缓存,高度完善的失效/更新策略。
- 优雅的容灾/降级方案,出现缓存故障或突发并发激增时可快速回退,依旧维护数据一致性。
-- 业务异常和系统异常分层捕获,并有完善的补偿或幂等机制;
-- 接口安全全面(加解密、签名、审计、敏感操作多级鉴权),日志精细到每个关键节点,足以支持安全审计和快速排障。
-- 代码结构高内聚低耦合,文档与注释详尽,具备完整的 Git 提交历史和高质量设计说明;
-- 可无缝对接高级功能(如分布式事务、熔断限流、机器学习推荐等),后续升级改造成本非常低。
A 级(优秀)
-- 较完善的"异步化"设计思想,如读写分离、Redis 队列或定时任务,能有效处理中高并发;
-- 订单与搜索逻辑边界清晰,有必要的锁或事务控管,保证不发生超卖; -- 模块划分合理,后续如需微服务拆分,改造成本可控。
-- 具备较成熟的缓存读写策略,正常场景下可确保库存与商品数据一致;
-- 偶尔在极端并发或缓存故障场景下,可能有短时滞后,但有一定的补救策略来保持主要功能可用。
-- 对主要业务异常与系统异常进行捕获与重试或补偿,日志可支持排错;
-- 接口具备一定的安全措施(身份鉴权、必要操作的审计),满足常规生产环境安全需求。
-- 代码结构整体清晰,设计文档与 Git 提交记录完整,能较好展现演进过程;
-- 具备对第三方组件(如消息队列、限流框架等)或其他业务模块的扩展接口。
B 级(良好)
- 同步为主,关键流程并发管控防超卖。
- 分层合理但耦合中等可维护,中等流量稳定。
- 基础缓存方案到位,可显著减少数据库查询压力。
- 简单失效策略,高频更新可能短暂滞后但无严重不一致。
- 常见异常捕获与日志,少数错误可能需人工补偿。
- 常规鉴权,满足大部分场景安全需求。
- 基本 Git 记录 / 设计文档(简略)。
- 升级异步 / 微服务需较多重构测试。
C 级(合格)
- 同步模式为主,低至中等并发,关键点用锁防超卖
- 分层弱,耦合高,缺乏高级并发手段
- 简单缓存思路,更新或失效策略较为原始,高并发可能缓存一致性差
- 能满足基础查询需求,无大面积数据不一致
- 仅捕获常规异常,日志缺乏深度,复杂问题需手动分析
- 简单鉴权,依赖外部网关
- Git 提交和设计说明可用,代码可读性一般
- 引入新组件需高改造成本
D 级(不合格)
缺乏并发设计,导致IO消耗高,同时没有防止超卖的机制。
- 无缓存或严重数据脱节,或者缓存命中率极低
- 异常处理缺失,日志无法支撑排障 / 审计
- 安全措施形同虚设
- 代码混乱无分层,维护 / 升级极困难
小结
- 直接使用 ORM 适合快速开发、业务简单的项目,注重简洁性。
- 存储库模式适合大型项目或需要频繁变更数据源的场景,注重代码的可维护性、可扩展性和可测试性。
写在最后
今天的分享就到这里。如果觉得不错,点赞
,在看
,关注
安排起来吧。