Django入门教程:搭建你的第一个Python网站
上周五晚上,我正坐在深圳南山科技园的工位上,盯着屏幕上凌晨三点的部署日志发呆。隔壁组的产品经理刚刚提了个“小需求”——做一个内部数据展示页,用来监控我们每天爬取的百万级商品信息。我心想,不就是个前端页面嘛,但转念一想,我们后端全是Java栈,Spring Boot + MyBatis 配得整整齐齐,临时加个展示服务?别闹了,光是启动时间就能把我熬到天亮。
就在这时,我想起了Django——这个被我们DBA圈子里私下称为“全栈懒人神器”的Python框架。虽然我是个从Oracle时代摸爬滚打过来的老DBA,后来转做后端开发,对数据库有执念到连JSON字段都要建索引那种,但不得不说,Django在快速原型开发上的表现,真香。
为什么选Django?别跟我说Java!
我知道,很多Java老铁看到Python就皱眉:“性能不行”、“动态类型太危险”、“上线出问题谁背锅?”——这些我都懂。我自己也维护过几个高并发的Java服务,GC调优、线程池配置、连接池参数,哪一样不是血泪史?
但兄弟们,工具要分场景用。你总不能拿电焊枪去修手表吧?
这次的需求很简单:把MySQL里已经爬好的商品数据(没错,是我们自己写的爬虫抓的)通过一个网页展示出来,支持基本搜索和分页。没复杂业务逻辑,没高并发要求(顶多十个运营同事同时看),甚至不需要用户登录。这种场景下,用Spring Boot从零搭一套CRUD,还要配Thymeleaf或者前后端分离再搞个Vue,属实是杀鸡用牛刀。
而Django呢?自带ORM、Admin后台、路由、模板引擎,开箱即用。更重要的是——它对数据库的操作非常“尊重”。作为一个DBA出身的人,我特别看重这一点:Django ORM生成的SQL虽然不是最高效的,但它不会胡乱JOIN,不会N+1查询(只要你注意点),而且migration机制让我能清晰追踪每一次表结构变更。这比某些ORM动不动就SELECT * 强太多了。
动手!从零开始搭个站
环境准备
首先确保你装了Python(建议3.8+),然后:
pip install django
创建项目:
django-admin startproject myshop
cd myshop
python manage.py startapp products
这时候目录结构大概是这样:
myshop/
├── manage.py
├── myshop/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── products/
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
└── views.py
数据库设计:DBA的执念时刻
我们的商品数据来自爬虫,字段大概有:title, price, category, source_url, created_at。作为DBA,我第一反应不是写代码,而是想索引怎么建。
在 products/models.py 中定义模型:
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=255, db_index=True) # 标题常用于搜索,加索引
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.CharField(max_length=100, db_index=True) # 分类筛选也常用
source_url = models.URLField(unique=True) # 爬虫去重靠这个
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
class Meta:
db_table = 'product' # 明确指定表名,DBA习惯
ordering = ['-created_at'] # 默认按时间倒序
注意:我特意给 title、category、created_at 加了 db_index=True。因为在实际使用中,运营肯定要按分类筛选、按时间排序、按关键词搜标题。没有索引?等数据量上来,页面加载5秒起步,产品经理又要半夜打电话问“是不是数据库挂了”。
然后生成并执行迁移:
python manage.py makemigrations
python manage.py migrate
这条命令会自动生成SQL并在数据库里建表。你可以用 --sql 参数看看它到底干了啥:
python manage.py sqlmigrate products 0001
输出类似:
CREATE TABLE `product` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`title` varchar(255) NOT NULL,
`price` numeric(10, 2) NOT NULL,
...
);
CREATE INDEX `product_title_idx` ON `product` (`title`);
...
看到没?索引名都给你规范好了。这种可控性,让我这个前DBA感到安心。
快速接入已有数据:爬虫成果不浪费
我们的爬虫是用Scrapy写的,数据已经存在MySQL里。Django ORM可以直接读,但要注意两点:
- 字段类型匹配(比如爬虫存的是字符串价格,这里要用Decimal)
- 主键是否一致
如果表已经存在,可以用 python manage.py inspectdb 自动生成模型,然后手动调整。
不过为了演示,我们就假装数据是Django自己插入的。你可以在 python manage.py shell 里手动加几条:
from products.models import Product
Product.objects.create(
title="iPhone 15",
price=5999.00,
category="手机",
source_url="https://example.com/iphone15"
)
写个视图:别怕前端
很多后端(包括曾经的我)看到“前端”两个字就头大。但Django的模板系统真的很轻量。
先在 products/views.py 里写个简单视图:
from django.shortcuts import render
from .models import Product
def product_list(request):
query = request.GET.get('q')
category = request.GET.get('category')
products = Product.objects.all()
if query:
products = products.filter(title__icontains=query)
if category:
products = products.filter(category=category)
# 分页很重要!不然10万条数据一次查出来,DB直接躺平
from django.core.paginator import Paginator
paginator = Paginator(products, 20) # 每页20条
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'products/list.html', {'page_obj': page_obj})
然后在 templates/products/list.html 写模板(记得在 settings.py 里配置 TEMPLATES 路径):
<!DOCTYPE html>
<html>
<head>
<title>商品列表</title>
</head>
<body>
<form method="get">
<input type="text" name="q" value="{{ request.GET.q }}" placeholder="搜索标题">
<select name="category">
<option value="">全部分类</option>
<!-- 这里可以动态加载分类,简化起见写死 -->
<option value="手机">手机</option>
<option value="电脑">电脑</option>
</select>
<button type="submit">搜索</button>
</form>
<ul>
{% for product in page_obj %}
<li>{{ product.title }} - ¥{{ product.price }} ({{ product.category }})</li>
{% endfor %}
</ul>
<!-- 分页导航 -->
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page=1">« 首页</a>
<a href="?page={{ page_obj.previous_page_number }}">上一页</a>
{% endif %}
<span>第 {{ page_obj.number }} 页,共 {{ page_obj.paginator.num_pages }} 页</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一页</a>
<a href="?page={{ page_obj.paginator.num_pages }}">末页 »</a>
{% endif %}
</div>
</body>
</html>
最后在 myshop/urls.py 和 products/urls.py 里配路由(略,标准操作)。
跑起来:
python manage.py runserver
访问 http://127.0.0.1:8000/products/,搞定!
生产环境?别慌,DBA来兜底
当然,上面只是本地开发。真要上线,还得考虑:
- 数据库连接池:Django默认没连接池,高并发下会频繁创建销毁连接。建议用
django-db-geventpool或者前面加个PgBouncer(如果你用PostgreSQL)。 - 静态文件:开发时Django能serve static,但生产必须用Nginx。
- 安全:
DEBUG=False,设置ALLOWED_HOSTS,别把admin后台暴露出去。 - 性能:如果数据量大,记得用
select_related/prefetch_related优化查询,避免N+1。
我们团队上线时就踩过坑:有一次忘了关DEBUG,结果某个页面报错直接把数据库密码打印出来了……运维大哥差点把我祭天。
对比一下:Django vs Spring Boot 做同样事情
| 维度 | Django | Spring Boot |
|---|---|---|
| 启动速度 | 几秒 | 10s+(带JVM) |
| 代码量 | ~50行核心逻辑 | ~200行(含配置类) |
| 数据库操作 | ORM简洁,SQL可控 | MyBatis需手写SQL或XML |
| 前端集成 | 内置模板,快速渲染 | 通常前后端分离,需额外搭前端 |
| 学习曲线 | Python基础即可 | 需理解Spring生态 |
| 适合场景 | 内部工具、MVP、数据展示 | 高并发、复杂业务系统 |
所以,别一上来就说“我们公司全是Java,不用Python”。技术选型要看需求,不是看信仰。
最后说两句
其实我写这篇教程,是因为上周有个实习生问我:“哥,我想做个个人博客,该用啥?” 我脱口而出:“Django啊!” 他一脸疑惑:“不是应该学Spring Cloud Alibaba全家桶吗?”
我笑了。年轻人,等你经历过双11凌晨三点排查慢SQL,经历过因为一个N+1查询导致数据库CPU 100%,你就会明白——有时候,简单就是最大的优雅。
Django可能不适合做交易核心系统,但它绝对是快速验证想法、搭建内部工具、展示爬虫成果的最佳拍档。尤其对我们这种对数据库有执念的人来说,它既给了你便利,又没剥夺你对数据的控制权。
好了,教程就到这里。我要回去继续盯着我的Java服务了——毕竟,明天又是需求评审日,产品经理说要加个“智能推荐”功能……
(完)
P.S. 如果你也是DBA转后端,或者对数据库有莫名执念,欢迎留言交流。顺便求问:有没有人用Django + ClickHouse 做实时分析的?我们正在考虑替换部分MySQL聚合查询……

评论 0