1. 从零搭好Django开发环境:虚拟环境、版本选择与pip镜像源
很多新手学Django,第一关不是语法,而是环境。我刚接触时直接在系统Python里pip install django,结果没过多久就乱了——今天装这个库,明天那个项目要不同版本,系统Python环境被搞得一塌糊涂。后来才明白,Django项目必须建在独立的虚拟环境里,这一步省掉,后面全是坑。
1.1 先选Python版本,再选Django版本
先聊聊版本这件事。Django的版本节奏挺快,长期支持版(LTS)和非长期支持版交替发布。我的建议很直接:如果你是新手,或者要上生产环境,就选当前最新的LTS版本,比如Django 4.2系列或5.x的LTS,Python版本用3.10以上即可。不要追最新版本,因为它可能还有兼容性问题,很多第三方库还没来得及跟上。
怎么确认对应关系?直接看Django官方文档的版本兼容表,里面写得很清楚:Django 4.2支持Python 3.8到3.12,Django 5.x支持Python 3.10以上。这里有个经验:Python版本尽量用官方下载页的稳定版,别用奇怪的发行版,否则后面装依赖库时容易遇到编译错误。
1.2 虚拟环境创建实操
Windows和macOS/Linux的创建命令有一点点区别,但思路一样:
# 创建虚拟环境(venv是Python自带的,不需要额外装) python -m venv django_env # 激活虚拟环境 # Windows: django_env\Scripts\activate # macOS/Linux: source django_env/bin/activate激活成功后,命令行前面会出现(django_env)的标识,这就是进入虚拟环境的信号。之后所有的pip install和python命令,都会在这个隔离环境里运行,不会污染系统的Python。
1.3 pip配置国内镜像源:为什么必须做这一步
这一步经常被忽略,但实际影响巨大。默认的pip源在国外,下载速度很慢还容易超时,尤其是一些体积大的依赖包,比如Pillow、psycopg2这些,卡个十分钟很正常。我是直接把pip源换成国内镜像,一劳永逸:
# 在用户目录下创建pip配置文件 # Windows路径:C:\Users\你的用户名\pip\pip.ini # macOS/Linux路径:~/.config/pip/pip.conf [global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple trusted-host = pypi.tuna.tsinghua.edu.cn timeout = 120换完源之后,装Django的速度简直是天壤之别。实测下来,清华源的速度稳定在几MB/s,基本是秒装。
1.4 安装Django并验证
pip install django装完验证一下:
python -m django --version如果正常输出版本号,说明安装成功。这里有个细节:建议用python -m django而不是直接敲django-admin,因为有时候系统里存在多个Python版本,直接敲命令可能调用到错误的环境,而python -m django能保证用的是当前虚拟环境里的那一套。
还有一个小建议:顺手把pip升级到最新版:
pip install --upgrade pip新版pip对依赖解析更合理,安装过程中不容易出现"依赖冲突"这类烦人的问题。
2. 创建项目与第一个页面:把Hello World跑起来的完整链路
环境搞定之后,真正进入项目环节。Django的项目结构是两个层级:项目(project)和应用(app)。项目是整个网站的配置和入口,应用是具体的功能模块。先创建项目,再往里加应用。
2.1 项目初始化命令详解
django-admin startproject myblog这条命令会创建一个myblog目录,里面自动生成以下结构:
myblog/ ├── manage.py # 项目管理入口,所有命令都通过它执行 └── myblog/ ├── __init__.py ├── settings.py # 全站配置文件 ├── urls.py # 根URL路由 ├── asgi.py # 异步网关接口 └── wsgi.py # 同步网关接口很多人不明白manage.py是干什么的,我打个比方:它就像一个遥控器,启动服务器、创建应用、跑数据库迁移,全靠它。后面你会反复用到python manage.py开头的命令。
2.2 启动开发服务器,验证环境
cd myblog python manage.py runserver看到Starting development server at http://127.0.0.1:8000/这样的输出,就说明项目跑起来了。浏览器访问这个地址,如果出现一个火箭图标并显示"It worked!",恭喜,你的Django项目已经能运行了。
这里说两个新手容易困惑的点:
第一,开发服务器为什么是8000端口?因为8000是Django默认的开发端口,如果被占用,可以指定端口:
python manage.py runserver 8080第二,开发服务器能用于生产环境吗?绝对不能。它是一个轻量级的服务,是为了开发调试方便,性能和安全防护都不够。生产环境需要用Gunicorn或者uWSGI来跑Django,后面会讲到。
2.3 配置ALLOWED_HOSTS:频繁踩坑的重要设置
在settings.py里有个ALLOWED_HOSTS配置,默认是空的列表。如果设置不对,访问时会报DisallowedHost错误。开发阶段怎么配?
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']如果要让局域网内的手机或别的电脑访问你的开发服务器,可以加上本机的局域网IP:
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '192.168.1.100']这个配置在开发阶段不显眼,到部署上线时就是个关键项,域名要加进去,不然会报错。我现在写项目,第一步就把这个配好,别等出错了再回来找。
2.4 第一个视图:理解URL路由的工作机制
Django处理请求的方式可以概括为三步:URL找到视图、视图处理逻辑、视图返回响应。先写一个最简视图:
在myblog/urls.py同级的目录下,新建一个views.py文件(初始项目里可能没有这个文件,如果不存在就自己新建):
from django.http import HttpResponse def index(request): return HttpResponse("欢迎来到我的第一个Django页面")然后在myblog/urls.py里配置路由:
from django.contrib import admin from django.urls import path from . import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index, name='index'), ]保存后刷新浏览器,就能看到页面上显示"欢迎来到我的第一个Django页面"。虽然很简陋,但整个链路已经通了:请求进来 -> 路由匹配 -> 视图响应。
到这一步,你已经掌握了Django最基本的运作逻辑。接下来就是把这个简单的骨架,丰富成一个有实际功能的应用。
3. settings.py与数据库配置:新手最容易忽略的几个坑
settings.py是整个Django项目的枢纽,几乎所有的全局配置都在这一个文件里。很多新手对它只有一个"看到需要改就改"的模糊认知,其实每一组配置背后都有明确的逻辑。
3.1 中文和时区:不配置就会踩时间的坑
INSTALLED_APPS和MIDDLEWARE我建议先保持默认,新手阶段不要动它们。但有两项必须立刻改,否则后面项目一跑就容易出问题,就是语言和时区:
LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai'默认是en-us和UTC,不改成zh-hans和Asia/Shanghai的话,Django自带的admin后台会显示英文,而且数据库里存的时间比北京时间慢8个小时。这个坑我踩过很多次——尤其是后面做的功能里如果需要记录用户操作时间,不配时区,存进去的时间总是"错"的。
注意:如果你用了MySQL之类的数据库,数据库连接字符串里也要把时区设为
Asia/Shanghai,两边对齐才不会出问题。
3.2 数据库配置:从SQLite切换到MySQL的完整步骤
Django默认用的是SQLite,这对学习和小项目完全够用,零配置、文件型数据库,省心。但如果你打算做正式项目,或者需要更高的并发能力,建议切换到MySQL或PostgreSQL。
这里以MySQL为例。先在虚拟环境中装驱动:
pip install pymysql然后在myblog/__init__.py里加一行:
import pymysql pymysql.install_as_MySQLdb()再修改settings.py:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myblog', 'USER': 'root', 'PASSWORD': '你的密码', 'HOST': '127.0.0.1', 'PORT': '3306', } }这里有个高频报错点:字符集。MySQL默认的字符集在某些情况下是latin1,存中文会乱码或报错。建议在数据库连接配置里加上:
'OPTIONS': { 'charset': 'utf8mb4', },utf8mb4是完整版的UTF-8,能支持emoji表情,存中文毫无压力。
3.3 STATIC_ROOT与MEDIA_ROOT:文件管理的基本盘
静态文件和上传文件是Web项目的常态需求,Django对这两类文件有明确区分:
- 静态文件(CSS、JS、图片):属于项目自身的资源,由
STATICFILES_DIRS指定开发时的目录,STATIC_ROOT指定部署后收集资源的目录。 - 媒体文件(用户上传的图片、附件):由
MEDIA_ROOT和MEDIA_URL管理。
开发阶段的简单配置:
import os STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] STATIC_ROOT = os.path.join(BASE_DIR, 'collect_static') MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')我刚开始学的时候没认真区分这几个参数,结果在部署时发现静态文件找不到,页面纯文字完全没样式,排查了很久。后来形成习惯:项目创建好之后,第一时间把static和media目录建好,配置写完整,后面就不会乱。
3.4 迁移命令:数据库初始化的标准操作
配置好数据库之后,需要执行迁移命令,Django会按照INSTALLED_APPS里默认应用的数据模型,在数据库里自动创建对应的表:
python manage.py makemigrations python manage.py migratemakemigrations是生成迁移文件,migrate是执行迁移。第一次跑完,数据库里会出现一堆django_开头的表,这是Django自带的用户、权限、会话等功能的表。看到这些表,说明数据库连接成功了。
提示:每次修改了models.py,都要重新执行这两条命令,数据库表结构才会更新。这是Django的ORM体系正常工作的重要路径。
4. 创建app与数据模型:从ORM设计到admin后台
项目只是一个壳,真正的功能都写在app里。app你可以理解成一个个独立的功能模块——比如一个博客项目,可以有文章app、评论app、用户app。每个app负责自己那一摊事。
4.1 startapp创建一个文章应用
python manage.py startapp article这会在项目根目录生成一个article目录,里面自动包含models.py、views.py、admin.py、migrations/等文件。注意,这个app创建好后,要注册到settings.py里才生效:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'article', # 加上这一行 ]为什么需要注册?因为Django启动时会扫描INSTALLED_APPS里每个应用的路由、模板、迁移文件等资源。不注册的话,app的路由和模型都不会被加载,访问就404。
4.2 编写数据模型:定义文章表
在article/models.py里定义一个最简单的文章模型:
from django.db import models from django.contrib.auth.models import User class Article(models.Model): title = models.CharField(max_length=100, verbose_name='标题') content = models.TextField(verbose_name='正文') author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者') created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') updated_time = models.DateTimeField(auto_now=True, verbose_name='更新时间') class Meta: ordering = ['-created_time'] verbose_name = '文章' verbose_name_plural = verbose_name def __str__(self): return self.title这里说一下ORM的基本概念。每一行都对应数据库表里的一列,比如title = models.CharField(max_length=100)就是数据库里一个varchar(100)字段。ForeignKey表示外键关联,这里关联到Django自带的User表,一篇文章对应一个作者,一个用户可以有多篇文章。on_delete=models.CASCADE指定用户被删除时,他的文章一并删除——这个参数必须显式指定,否则Django会报错提示你补上。
auto_now_add和auto_now的区别经常有人搞混:auto_now_add是记录第一次创建时间,创建后不再变动;auto_now是每次保存都要更新时间。
4.3 生成并执行迁移:让表真正落地
写完模型后,执行一次迁移:
python manage.py makemigrations article python manage.py migrate想确认生成的SQL语句长什么样,可以用:
python manage.py sqlmigrate article 0001这会打印出Django帮你自动生成的CREATE TABLE语句。刚开始学的时候看一眼这个输出,能帮你把ORM和真实SQL对应起来,理解会更扎实。
4.4 注册到admin后台:零代码管理数据
Django自带的后台管理是它的加分项,注册一下模型就能在后台直接增删改查数据。打开article/admin.py:
from django.contrib import admin from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'author', 'created_time') search_fields = ('title', 'content')然后创建一个管理员账号:
python manage.py createsuperuser按提示输入用户名、邮箱、密码。启动服务器,访问http://127.0.0.1:8000/admin/,登录后就能看到文章管理界面。这是Django对开发效率的提升最直观的一个点——不需要手动写任何后台代码,就拥有了一套完整的内容管理系统。
在实际项目中,我习惯在list_display里把常用字段都列出来,比如标题、作者、时间,方便后台审核内容时一目了然。
5. 模板系统与静态文件:把页面做得像样一点
后端的数据模型已经通了,接下来要把数据展示给用户。Django用的是自家的模板系统,它的核心逻辑是:模板负责页面结构,视图负责传数据,二者通过上下文变量连接。
5.1 视图传数据到模板的基本方式
在article/views.py里写一个视图,把文章列表传给模板:
from django.shortcuts import render from .models import Article def article_list(request): articles = Article.objects.all() return render(request, 'article/list.html', {'articles': articles})render函数做了三件事:加载模板文件、传入上下文变量、返回渲染后的HTTP响应。注意article_list这个名字对应的URL路由,在项目根路由myblog/urls.py里配置:
from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('article/', include('article.urls')), ]然后在article/目录下新建urls.py:
from django.urls import path from . import views urlpatterns = [ path('', views.article_list, name='article_list'), ]这样访问/article/就能触发article_list视图。
5.2 创建模板文件并渲染数据
在article应用下新建templates/article/目录,创建list.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>文章列表</title> </head> <body> <h1>文章列表</h1> {% for article in articles %} <div> <h2>{{ article.title }}</h2> <p>{{ article.content | truncatechars:100 }}</p> <p>作者:{{ article.author.username }} | 发布时间:{{ article.created_time }}</p> </div> {% empty %} <p>还没有文章,快去后台添加吧。</p> {% endfor %} </body> </html>模板语法里,{% for %}是循环标签,{{ article.title }}是变量输出。truncatechars:100是个好用的过滤器,截断长文本,列表页展示摘要时特别常用。
5.3 静态文件加载:CSS和JS的正确姿势
页面光秃秃的不像话,需要加样式。在项目根目录创建static/css/目录,放一个简单的CSS文件,然后在模板顶部引入:
{% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>文章列表</title> <link rel="stylesheet" href="{% static 'css/style.css' %}"> </head>这里{% load static %}必须写在模板开头,它告诉Django这个模板要用到静态文件相关的模板标签。{% static 'css/style.css' %}会在渲染时拼上你在settings.py里配置的STATIC_URL,生成类似/static/css/style.css的完整路径。
5.4 模板继承:避免大量重复代码
随着页面增多,如果每个页面都复制一份完整的HTML骨架,代码会非常冗余。Django的模板继承机制就是专门解决这个问题的。
创建一个基础模板templates/base.html:
<!-- templates/base.html --> {% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block title %}我的网站{% endblock %}</title> <link rel="stylesheet" href="{% static 'css/style.css' %}"> </head> <body> <nav> <a href="/">首页</a> <a href="/article/">文章列表</a> </nav> <main> {% block content %} {% endblock %} </main> </body> </html>然后子模板只需要写属于自己的部分:
<!-- article/templates/article/list.html --> {% extends "base.html" %} {% block title %}文章列表{% endblock %} {% block content %} {% for article in articles %} <div> <h2>{{ article.title }}</h2> <p>{{ article.content | truncatechars:100 }}</p> </div> {% endfor %} {% endblock %}{% extends %}是继承标记,{% block %}是子模板要填充的区块。这种做法的好处是:改导航栏、改页脚,只需要动base.html一个文件,所有页面同步生效。
6. 表单、CSRF与Cookie/Token:让用户和系统产生交互
静态页面读起来没意思,真正的Web应用必须支持用户输入。表单处理是Django的强项,但也是新手最容易碰壁的地方——尤其是CSRF这个机制,第一次遇到会用"CSRF verification failed"直接把人搞懵。
6.1 一个简单但完整的表单流程
先做一个发布文章的页面。在article/views.py里加一个视图:
from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from .models import Article @login_required def create_article(request): if request.method == 'POST': title = request.POST.get('title') content = request.POST.get('content') if title and content: Article.objects.create( title=title, content=content, author=request.user, ) return redirect('article_list') return render(request, 'article/create.html')在article/urls.py里注册路由:
path('create/', views.create_article, name='create_article'),模板article/create.html:
{% extends "base.html" %} {% block content %} <h1>发布文章</h1> <form method="post"> {% csrf_token %} <input type="text" name="title" placeholder="标题" required> <textarea name="content" placeholder="正文" required></textarea> <button type="submit">发布</button> </form> {% endblock %}6.2 CSRF机制:为什么模板里必须加那一行
{% csrf_token %}这行简直是新手重灾区。有时候从网上复制代码忘了带它,提交表单就报错。它的本质是一个安全令牌:Django会在生成表单页面时,在渲染的HTML里埋入一个一次性token,提交时后端校验这个token。如果token不匹配,就拒绝请求。这套机制能有效防止CSRF攻击——第三方网站伪造表单提交到你的站点。
实际开发中,如果你用Django的render渲染表单模板,{% csrf_token %}是必须的。如果是前后端分离或者对接第三方API,则需要配合Cookie里的CSRF token和请求头里的X-CSRFToken字段来通过验证。这块逻辑在Django文档里有专门章节,值得深入读一遍。
6.3 Cookie与Token的简单实践
Django的会话系统默认将session数据存在数据库里,通过Cookie中的sessionid来标识用户身份。登录后Django会自动设置这个Cookie,后续请求带着它,后端就能识别出"这是哪个用户"。
那Token呢?在前后端分离或需要给第三方提供API时,光靠session就不够灵活了。Django官方推荐使用django-rest-framework的TokenAuthentication机制,它为用户生成一个token字符串,客户端在请求头里带上:
Authorization: Token 9944b09199c62bcf9418ad85ddd8bdf服务端校验这个token,就能确定用户身份。Token的好处是:不依赖Cookie,跨域请求也好使,方便移动端和第三方接入。
提示:如果你需要在Cookie里手动设置自定义token,可以用Django的
set_cookie方法,但要设置合理的过期时间,并用HttpOnly属性防止脚本读取,降低安全风险。
6.4 实际项目中的请求处理逻辑
综合来说,一个正常的表单处理流程是这样的:
- 用户访问表单页面,请求方式是GET,视图渲染表单模板并返回。
- 用户填写并提交,请求方式是POST,视图根据提交的数据进行处理。
- 数据校验后,要么保存并重定向到成功页面,要么返回错误信息并重新渲染表单。
@login_required会拦截未登录用户的访问,自动跳转到登录页,这是Django最常用的权限控制手段之一。
我在实际项目里,习惯把表单的数据校验写在forms.py里,而不是直接在视图里手动判断。Django的Form表单类自带字段类型校验、错误提示、渲染等功能,代码会比手写POST处理更规范,也更安全。
7. 从数据库查询到数据删除:ORM的核心操作技巧
前面我们已经在视图中用到了Article.objects.create()和Article.objects.all(),这里把ORM的增删改查完整梳理一遍。这套操作是Django后端日常开发最常用的技能,热搜里"django执行查询-删除对象"指的就是这一块。
7.1 新增数据的几种姿势
# 方式一:create方法 article = Article.objects.create(title='标题', content='内容', author=user) # 方式二:先实例化再save article = Article(title='标题', content='内容', author=user) article.save()两种方式效果基本一样,区别在于create封装了创建和保存两步,代码更简洁。需要处理额外逻辑时用先实例化再save的方式更灵活。
7.2 查询数据:复现日常开发最常用的查询条件
# 获取全部数据 articles = Article.objects.all() # 过滤:标题包含某关键词 articles = Article.objects.filter(title__icontains='django') # 单个对象 article = Article.objects.get(id=1) # 排序 articles = Article.objects.order_by('-created_time') # 限制数量 articles = Article.objects.all()[:5] # 统计数量 count = Article.objects.count()filter返回的是一个QuerySet集合,它支持链式调用,可以不断叠加过滤条件。这里要注意一个高发坑:get返回的是单个对象,如果查询结果不存在或有多条,都会抛出异常。不确定是否存在的时候,用filter(...).first()更安全,没有匹配时返回None,不会中断程序。
7.3 更新数据
# 方式一:实例操作 article = Article.objects.get(id=1) article.title = '新标题' article.save() # 方式二:批量更新 Article.objects.filter(author=user).update(title='统一标题')批量更新update就是一条UPDATE语句,性能比一条条循环快得多。但注意,批量更新不会触发模型里的save()方法,所以auto_now字段在批量更新时不会自动更新(Django 4.2以上的版本已支持update中显式处理这个参数,具体看版本文档)。吃不准的时候,先查文档。
7.4 删除对象:delete()方法的关键细节
删除是一个不可逆的操作,Django给了两种方式:
# 删除单个对象 article = Article.objects.get(id=1) article.delete() # 批量删除 Article.objects.filter(created_time__year__lt=2020).delete()删除操作有一个容易踩的点:如果模型之间存在外键关联并且on_delete设置为CASCADE,删除主记录时,关联的子记录也会一起被删除。比如你的评论模型外键关联Article,删掉文章会连带删掉它的所有评论。如果你希望删除文章时保留评论,要么把on_delete改为SET_NULL(子记录的外键字段允许为空),要么在业务逻辑里提前处理这些关联数据。
另外,delete()方法返回的是一个(总数, 每个类型删除数量)的元组,这在确认删除范围时很有用:
result = Article.objects.filter(id=1).delete() print(result) # (1, {'article.Article': 1})7.5 F表达式:处理并发场景下的字段更新
如果说前面的操作还比较基础,那F表达式就是进阶级的技巧。先看一个场景:文章点击量加一。最直观的写法是:
article = Article.objects.get(id=1) article.pv += 1 article.save()但这个写法在并发情况下有问题:两个请求同时读到PV=100,各自加1再写回,最终结果可能是101而不是102。用F表达式就能把一个数据库操作里完成"字段自增":
from django.db.models import F Article.objects.filter(id=1).update(pv=F('pv') + 1)F('pv')表示引用数据库里这一列的当前值,这个操作是在数据库层面原子执行的,不会出现并发竞争问题。做计数器、库存、点赞数这类功能时,务必用F表达式。这是很多从数据库入门就开始用Django过程中才逐渐理解的关键点。
8. WebSocket扩展与部署思路:上线前最后那几步
一个Django项目做到能增删改查、有后台、有页面,基本就是个完整的Web应用了。但如果你的项目需要实时推送——比如热搜词里提到的"python django websocket实现后台有数据前端推送"——那就需要更进一步的技术栈。
8.1 Django对WebSocket的支持方式
Django是一个同步框架,请求-响应模式处理得很好,但WebSocket是长连接、双向通信,Django原生没有直接支持。目前主流方案是Channels,它让Django具备了异步处理能力。
pip install channels然后在settings.py里把daphne和channels加入INSTALLED_APPS,并配置ASGI:
INSTALLED_APPS = [ 'daphne', 'channels', ... ] ASGI_APPLICATION = 'myblog.asgi.application'再配置一个简单的通道层:
CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels.layers.InMemoryChannelLayer', }, }InMemoryChannelLayer是内存通道层,只适合开发调试,生产环境建议用Redis作为通道层后端,支持跨进程通信。
后台推送的基本思路是:前端通过WebSocket连接到Django的Consumer,Consumer收到消息后,把数据推送到前端页面。例如开发一个通知功能,后台某个事件触发时,通过channel_layer.group_send把消息推给所有订阅了某个频道的前端。
我做过一个简单的实时日志查看器,就是用Channels把Django后台的日志流推送到浏览器页面,效果非常直观。但要提醒的是,Channels的异步特性对Django开发者来说需要适应,回调式的写法跟传统的同步视图区别很大,第一次接触时建议从官方教程的聊天室Demo入手,把Consumer和事件循环的机制跑通再说。
8.2 部署前必须检查的清单
开发环境的服务器不能直接用于生产,上线前有几件事必须处理:
- 关闭调试模式:
DEBUG = False,这会导致错误页面不再显示详情,避免敏感信息泄露。 - 处理静态文件和媒体文件:Django不擅长服务静态文件,生产中要交给Nginx处理,或用WhiteNoise这类中间件。所以前面我让你把
STATIC_ROOT配好,就是为此做的准备。 - 配置数据库:生产环境不要再用SQLite,建议MySQL或PostgreSQL,并做定期备份。
- 设置环境变量:数据库密码、Secret Key等敏感信息不要直接写死在settings.py,用环境变量或配置文件外部管理。
- 使用Gunicorn/uWSGI:多进程运行Django应用,
gunicorn myblog.wsgi:application这条命令要反复跑。
8.3 我的部署心得
我个人在实际部署时,比较常用"Gunicorn + Nginx + MySQL"这套组合。Nginx负责挡住外部流量、处理静态文件,Gunicorn跑Django的Python进程,MySQL存数据。域名配置好之后,在ALLOWED_HOSTS里加上自己的域名,用HTTPS证书把流量加密,这才算一个合格的生产环境。
部署过程中最容易翻车的是静态文件。开发时STATICFILES_DIRS指向的是源目录,部署时要用python manage.py collectstatic把所有静态文件集中收集到STATIC_ROOT,再让Nginx指向这个目录。我多次遇到部署后页面完全没有样式的问题,九成是这一步没处理。
9. 一些接近实战的补充建议
文章写到最后,分享几个我在Django实战中沉淀下来的小习惯。这些不是哪本教材里能找到的,纯属踩坑踩出来的经验。
第一,目录结构不要图省事。很多人把所有app的models都塞在一个文件里,项目小的时候无所谓,项目一大就乱了。我建议一个app尽量保持一个清晰的功能边界,比如用户、文章、评论、分类,各管各的。Django的哲学是"可复用app",你以后会在多个项目之间复用代码,边界清晰才能复用。
第二,及时做数据库备份和迁移文件的维护。每次makemigrations生成的迁移文件,建议及时提交到版本控制里,它们是数据库结构的变更记录,换机器、上线、回滚都要靠这些文件。
第三,善用Django的日志功能。在生产环境里,日志是诊断问题的第一手段。settings.py里用LOGGING配置一个文件日志,把Django请求、SQL执行、错误堆栈都记录下来,排查问题会轻松很多。
第四,坚持看官方文档。Django的文档质量在Web框架里算是顶级,从入门到进阶每个概念都有详细解释。遇到不懂的,先查官方文档再百度/谷歌,能少走很多弯路。
对于刚开始学Django的朋友,我的建议是走完一个完整的闭环:创建项目 -> 建app -> 定义模型 -> 写视图和模板 -> 加表单 -> 部署上线。哪怕做一个极简的博客系统,只要把这个闭环走完,你对Django的理解就能超过很多人。下一步再往深走,比如REST API、异步任务、缓存、消息队列,都是在这个基础上扩展的。先把地基打牢比啥都重要。