安装完后在 INSTALLED_APPS 中添加 'django_extensions'
从零开始搭建一个Django网站:真实项目实战手记

各位好,我是后端开发五年多的程序员老赵。今天想和大家聊聊我第一次用 Django 搭建网站的真实经历。
那会儿我刚转行做后端不久,公司接了一个小型电商后台系统的需求。客户要求用 Python 技术栈,而我当时唯一接触过的就是 Django 框架了。虽然之前学过些基础语法,但真正要动手做一个完整的项目还是头一回。
初识挑战:需求与现实的碰撞
项目刚开始就遇到了第一个坎:需求模糊。产品经理只给了个大概的功能列表,比如用户管理、商品展示、订单跟踪这些,但具体怎么实现没有详细说明。那时候我还很天真地以为只要按照官方教程一步步来就行,但实际上真不是这么回事。
记得刚开始建模的时候,光是 User 表和 Address 表之间的关系我就纠结了好几天。要不要一对一?要不要允许用户有多个地址?这些细节不确认清楚,后面的业务逻辑根本没法展开。
更头疼的是性能问题。最开始在本地开发环境测试时一切正常,但部署到测试服务器上之后,访问量稍微大一点页面就开始卡顿。那个时候我才意识到框架用得熟不代表就能做好性能优化。
解决之道:Django 的正确打开方式
1. 项目结构规划
先说说我当时的项目结构。我们团队一共4个人,前后端分离。后端主要负责 RESTful 接口的设计和实现。整个项目的目录结构大致如下:
myproject/
├── config/ # Django 配置文件
├── apps/ # 应用模块
│ ├── users/
│ ├── products/
│ └── orders/
├── common/ # 公共组件
├── requirements.txt
└── manage.py
这种分层设计的好处在于后期维护和扩展都很方便。每个功能模块都独立成一个 app,通过 settings.py 中的 INSTALLED_APPS 来管理依赖关系。
2. 数据库设计那些事儿
数据库方面我们选用了 PostgreSQL,原因有几个:
- 支持 JSON 类型字段,对某些复杂数据存储很有帮助
- Django ORM 对它的支持最好
- 后期可能需要全文搜索等高级特性
以用户地址表为例,当时我们遇到了这样的设计难题:应该把地址信息单独拆成一张表,还是直接作为 User 表的一个 JSON 字段?
最终决定拆分出来的理由有两个:
- 地址信息可能会被其他模块复用(如订单记录中的收货地址)
- 单独存储便于做数据分析
class Address(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
recipient_name = models.CharField(max_length=100)
phone = models.CharField(max_length=20)
province = models.CharField(max_length=50)
city = models.CharField(max_length=50)
district = models.CharField(max_length=50)
detail = models.TextField()
is_default = models.BooleanField(default=False)
这个表设计后来证明是很明智的选择。当我们需要统计各地区用户分布时,写查询语句特别方便。
3. 接口设计的经验
接口设计方面有个血泪教训。最开始为了省事,所有接口都返回统一格式:
{
"code": 0,
"message": "success",
"data": {}
}
看起来挺标准是不是?但实际使用中却发现有些场景这样处理特别麻烦。比如分页列表接口,既要包含当前页的数据,又要返回总条数、当前页码等元信息。如果全部塞到 data 里,前端解析起来就很不方便。
后来改成针对不同类型的接口设计不同的响应结构:
// 列表接口
{
"total": 100,
"page": 1,
"size": 10,
"items": [...]
}
// 详情接口
{
"id": 1,
...
}
// 错误响应
{
"error": "invalid_params",
"message": "参数错误"
}
这样的改变让前后端协作顺畅了很多。
4. 性能优化实践
说到性能优化,这可能是让我成长最快的环节之一。
最初我们遇到的问题是 API 响应时间过长。经过分析发现主要是两个问题:
- 过度使用嵌套序列化器导致 N+1 查询
- 缺乏合适的索引
举个例子,订单详情接口包含了用户信息、商品信息等多个关联对象。最初的实现是这样:
class OrderDetailSerializer(serializers.ModelSerializer):
user = UserSerializer() # 嵌套序列化器
items = ItemSerializer(many=True)
class Meta:
model = Order
fields = '__all__'
这个简单的实现背后却隐藏着巨大的性能隐患。每个订单项都会触发一次新的数据库查询,如果有100个订单项就会执行100次查询!
解决办法是在获取 queryset 时使用 select_related 和 prefetch_related:
orders = Order.objects.select_related('user').prefetch_related(
Prefetch('items', queryset=Item.objects.select_related('product'))
).all()
索引优化方面,我们给经常用于查询的字段加上了合适的索引。比如订单状态变更频繁,我们就给 status 字段加了索引;商品分类页需要用到 category_id,也给它加了索引。
此外,我们还引入了缓存机制。对于读多写少的数据(如商品分类),我们使用了 Redis 缓存。对于重复请求较多的接口(如首页推荐商品),我们也做了短时间的缓存。
真实收获:不仅仅是技术提升
这次项目经历带给我的远不只是技术上的成长。
首先是架构思维的转变。以前觉得只要把代码写出来跑通就行,现在则会更多考虑系统的可扩展性、可维护性。比如我们会预留一些扩展点,为将来可能出现的新需求做好准备。
其次是性能意识的觉醒。现在的我会习惯性地用 django-debug-toolbar 检查查询情况,在开发阶段就注意避免性能陷阱。
最重要的一点体会是:好的代码不仅要自己看得懂,更要别人容易理解。所以在项目中期我们重新规范了命名规则,整理了文档,这些工作大大提高了团队协作效率。
给新手朋友的建议
如果你是 Django 新手,这里分享几个实用小技巧:
- 学会用 shell_plus 调试
pip install django-extensions
python manage.py shell_plus
这个增强版的 shell 会让你调试模型变得更高效。
善用 migrations 的反向迁移 当需要修改已有字段时,不要手动去改数据库。用 makemigrations 生成迁移文件,然后通过 migrate 更新数据库。这样可以保证多人协作时数据库结构的一致性。
设置合适的日志级别
LOGGING = {
'version': 1,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
}
}
}
这段配置可以把所有 SQL 查询打印出来,对排查性能问题很有帮助。
监控生产环境 上线后一定要安装监控插件,像 Sentry 这样的错误追踪系统是必备品。另外也要定期检查数据库慢查询日志。
不要忽视测试 单元测试和集成测试一定要写。Django 提供了非常好用的测试工具,利用 TestCase 写起测试来很方便。我们的代码覆盖率最后达到了 85%,这对质量保障起到了很大作用。
结语:技术这条路越走越宽
回过头来看,这个项目虽然规模不大,但却实实在在锻炼了我的工程能力。现在再看当年写的那些"糙快猛"的代码,确实有不少值得改进的地方,但正是这些实践经验才让我一步步成长为今天的开发者。
希望这篇文章能给你带来一些启发。记住,最好的学习方式永远是动手去做。不要害怕犯错,也不要担心起点低。只要坚持不断地实践和总结,你也能写出漂亮、稳健的 Django 项目。
如果这篇文章对你有帮助,欢迎留言交流你的学习心得。我在工作中也会继续分享更多的实战经验,让我们一起在这个充满挑战和技术魅力的道路上共同前行。

评论 0