从零开始用Django搭建你的第一个Python网站:我在项目实践中踩过的坑和经验总结
去年刚加入公司的时候,我被分配到一个新成立的电商项目组,负责后端开发。作为团队中唯一一个之前接触过Django的开发者,我自然而然地承担起了技术选型、初期架构搭建以及新人培训的任务。
刚开始接到这个任务时,我内心是有点忐忑的。虽然在学校时做过几个小项目,也对Django有一定的了解,但真正面对一个需要上线运营的Web项目,心里还是没底。特别是在当时整个团队的技术栈还没有统一、运维流程也没有完善的情况下,每一个选择都显得格外重要。
也正是在这样的背景下,我一步步从0到1完成了我们项目的后端服务搭建,使用了Django框架,并结合了MySQL、Redis、Nginx、Gunicorn等一系列工具。现在回过头来看,虽然过程中踩了不少坑,但也收获良多。
这篇文章,我想以我真实参与的第一个Django项目为线索,带你一起从0开始搭建一个完整的Web站点,并分享我在实际工作中遇到的问题和解决方案。希望你看完以后,不仅能掌握Django的基本用法,更能理解在真实业务场景中如何做出合适的技术决策。
我们为什么选择Django?

当时的项目背景是做一个面向中小型商家的SaaS电商平台,要求快速上线、可扩展性强、稳定性高。我们团队成员有前端、后端和测试,规模不大,但节奏很快。
我们在做技术选型时考虑过Flask、FastAPI、Django这三种方案:
- Flask 灵活性很高,适合轻量级项目或者微服务,但我们担心后期系统变复杂之后维护起来困难。
- FastAPI 的异步支持很好,性能也不错,适合构建高性能的接口服务,但当时生态还不够成熟,尤其缺乏成熟的管理后台插件。
- Django 虽然“重”,但它自带的功能非常齐全:ORM、Admin管理后台、路由系统、模板引擎、安全机制等等一应俱全,非常适合快速开发一个结构清晰、功能完整的Web应用。
最终我们决定采用Django作为项目的主框架。事实证明,这个选择非常正确,在后续快速迭代的过程中,Django为我们节省了大量时间,尤其是在权限管理、数据库建模和后台搭建上提供了极大的便利。
搭建项目的第一步:创建工程和App

我们的项目代号叫shopkeeper,所以我就先来创建Django项目:
django-admin startproject shopkeeper
进入项目目录后,执行如下命令启动默认服务器看看是否正常:
cd shopkeeper
python manage.py runserver
访问 http://127.0.0.1:8000/,看到 Django 的欢迎页面说明环境已经OK了。
接下来就是划分模块,根据电商平台常见的功能模块,我们划分为几个核心App:
users:用户认证与权限管理products:商品信息管理orders:订单处理模块cart:购物车逻辑payments:支付流程管理
通过命令行创建App:
python manage.py startapp users
python manage.py startapp products
# 其他类似省略...
然后把这些App添加到settings.py的INSTALLED_APPS里:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 我们的app
'users.apps.UsersConfig',
'products.apps.ProductsConfig',
]
数据库设计:别让模型限制你未来的扩展性
作为一个电商平台,数据库设计至关重要。特别是像商品、分类、SKU这些模型,直接关系到后期数据查询效率和系统扩展性。
举个例子,在设计Product模型时,我们就遇到了一个问题:是否应该把库存、价格等字段放在Product本身,还是另外建立一个SKU表?
我们一开始的设计是这样:
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField(default=0)
这种设计对于简单的商品来说是够用的,但随着需求变化,比如我们要支持不同规格的商品(颜色、尺寸、重量),这种设计就完全无法满足需求了。
于是我们重构了模型,拆分成Product和ProductSKU两个表:
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
class ProductSKU(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='skus')
spec = models.JSONField() # 存储规格信息,如{"color": "red", "size": "XL"}
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField(default=0)
is_default = models.BooleanField(default=False)
这一改动极大地提高了系统的灵活性,也为后续引入搜索、筛选、推荐等功能打下了基础。
小经验分享:
设计数据库模型时,不要为了图方便而牺牲结构的合理性。有时候看起来简单的设计,在业务复杂度上升后反而会带来更大的问题。提前做好抽象,合理拆分模型,是非常关键的一环。
接口设计:RESTful风格 + DRF
前后端分离已经成为主流做法。在我们的项目中,前端使用Vue.js,后端提供标准的REST API接口。所以我们选择了 Django REST Framework(DRF)来构建我们的接口体系。
安装DRF很简单:
pip install djangorestframework
然后同样在settings.py中注册:
INSTALLED_APPS += ['rest_framework']
以用户登录为例,我们定义了一个序列化器(Serializer):
from rest_framework import serializers
from django.contrib.auth import authenticate
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField(style={'input_type': 'password'})
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("无效的用户名或密码")
再写一个视图函数:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class LoginView(APIView):
def post(self, request):
serializer = LoginSerializer(data=request.data)
if serializer.is_valid():
login(request, serializer.validated_data)
return Response({'success': True})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
接着配置URL路由:
from django.urls import path
from .views import LoginView
urlpatterns = [
path('login/', LoginView.as_view(), name='login'),
]
这套组合拳下来,一个标准的REST接口就成型了。
经验建议:
接口设计要尽量符合REST规范,保持命名统一、请求方式明确。在复杂的业务场景下,合理拆分接口版本、权限控制和异常处理机制也非常重要。
踩过的坑和解决方法
在整个开发过程中,当然也不是一帆风顺,下面分享几个印象比较深的“坑”:
坑一:ORM的select_related和prefetch_related没用好导致N+1问题
最初在获取商品列表时,我们用了类似这样的代码:
products = Product.objects.all()
for product in products:
print(product.category.name) # category 是外键
每遍历一个product都要单独查一次category表,造成了严重的性能问题。
后来我们改成了:
products = Product.objects.select_related('category').all()
这样就能一次性JOIN查询出来,避免N次重复请求。
经验总结:
在涉及外键查询时,一定要记得使用select_related(一对一、外键)和prefetch_related(多对多)来减少数据库请求次数。
坑二:静态文件部署配置错误导致前端图片加载失败
在生产环境中,Django默认不会处理静态资源。我们最开始忽略了这一点,导致上传的图片地址返回404。
解决方式是在settings.py里配置:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
然后在urls.py中临时加入静态文件路径(仅用于调试环境):
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
注意事项:
正式生产环境下,建议将静态资源交由Nginx处理,而不是用Django来做这件事情。
坑三:跨域请求问题(CORS)
由于前端运行在另一个域名下(例如 http://localhost:3000),我们经常遇到浏览器的CORS报错。
解决方法也很简单,装一个中间件:
pip install django-cors-headers
然后在settings.py中配置:
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
...
]
CORS_ORIGIN_WHITELIST = [
"http://localhost:3000",
"https://yourfrontenddomain.com"
]
部署上线:从小白到能跑得动的服务
当开发基本完成后,我们面临一个更重要的问题:如何部署上线?
我们用的是阿里云ECS服务器 + Nginx + Gunicorn + Supervisor的方式进行部署。
简单说一下流程:
1. 安装Gunicorn
pip install gunicorn
然后测试一下是否能启动项目:
gunicorn -w 4 -b 0.0.0.0:8000 shopkeeper.wsgi:application
2. 使用Supervisor管理进程
编写/etc/supervisor/conf.d/shopkeeper.conf文件:
[program:shopkeeper]
command=/path/to/venv/bin/gunicorn -w 4 -b 0.0.0.0:8000 shopkeeper.wsgi:application
directory=/path/to/project
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/shopkeeper.log
3. 使用Nginx反向代理
配置Nginx:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static/ {
alias /path/to/static/;
}
location /media/ {
alias /path/to/media/;
}
}
4. 申请SSL证书实现HTTPS
使用Let’s Encrypt免费证书生成:
certbot --nginx -d yourdomain.com
然后Nginx会自动配置成HTTPS。
这一整套下来,我们的服务基本上就可以稳定跑了。
总结一下:Django真的值得学习吗?
说实话,作为一个已经写了两年Python后端的开发者,我觉得Django依然是目前最适合入门和快速构建产品的Python Web框架之一。
它的优势在于:
- 成熟的生态系统:Admin、Auth、ORM、Session、缓存、模板系统等开箱即用。
- 社区活跃:文档丰富,社区活跃,遇到问题基本都有现成答案。
- 架构清晰:MVC分层明确,利于大型项目维护。
- 可扩展性强:配合DRF、Celery、Channels可以轻松扩展API、异步任务、WebSocket等功能。
虽然它不如FastAPI那样轻量和异步能力强,但在实际企业级开发中,Django提供的那一套“重武器”恰恰是最宝贵的资产。
最后给新手的一些建议
如果你打算学Django或者正在搭建你的第一个网站,这里是我的一些真心建议:
- 不要急着追求性能和并发,先把功能逻辑跑通再说。
- 数据库模型设计比你想的更重要,多画ER图,多想想未来可能的变化。
- 接口要用DRF规范写,不要随便return JSONResponse。
- 尽早使用Git进行版本控制,别等到出了问题才后悔。
- 学会阅读官方文档,别总想着抄别人的代码片段。
- 部署阶段别怕折腾,越早体验线上环境越好。
- 多去StackOverflow上看别人的回答,很多“难题”其实都有现成的答案。
写在最后:技术成长,是一个慢慢沉淀的过程
这篇文章写到这里,差不多也有接近3千字了。我尽量还原了自己当初的真实经历,包括踩过的坑、走过的弯路,以及一些技术上的思考。
其实从一个学生到真正意义上的工程师,中间差的就是“实战”。而Django,刚好是我们从学校走向工作的一个桥梁。
希望这篇文章对你有所帮助。如果你正在学习Django,或是打算入门Python Web开发,不妨动手试试看。哪怕只是一个最简单的博客系统也好,只要你亲手敲了代码,你就已经在进步的路上了。
也欢迎大家留言交流或者指出文中可能存在的技术错误,我们一起成长,一起进步 😄。

评论 0