简介:基于Django开发的新闻网站及后台管理系统源码,适合正在学习Python Web开发的初中级开发者,可用于掌握用户认证、内容发布、分类管理等完整业务场景。压缩包共2000个文件、13.62MB,包含大量png/svg图标资源、js/css前端样式、145个pyc缓存和85个py核心代码、52个html模板,以及少量json、xml等配置文件,目录结构清晰,便于逐模块研读。已有748人学习下载。源码涵盖Django的MTV架构、ORM数据库操作、URL路由、模板渲染、内置admin后台等关键知识点,并附有bootstrap、AdminLTE等常用框架的集成示例,可帮助读者将理论落地为实际项目。通过阅读与二次开发,能有效理解新闻网站从模型设计、视图逻辑到前端展示的完整链路,是一份适合实操练习和项目起步的参考代码。
1. 为什么说“Django 新闻网站 + 后台管理”是新手性价比最高的实战项目
如果你在网上搜过“Django 项目实战”,大概率看到的是博客、Todo List 或者商城。这些项目不是说没用,而是离真实业务太远——博客没有审核流,Todo List 没有权限模型,商城的前台逻辑又太重,新手光是把订单状态机理清楚就得耗掉大半精力。而新闻网站恰好落在一个甜点上:前台是典型的“读多写少”内容展示,后台是标准的“增删改查 + 权限控制”,两个部分加起来,正好把 Django 最值钱的能力——ORM、Admin、表单校验、模板渲染、缓存策略——全部覆盖到。这也是很多培训机构和企业内部实训选它当教学项目的原因:能把这一套源码吃透,你基本就具备了上手中小型内容管理系统(CMS)的开发能力。
我见过不少把这份源码下载下来却跑不起来的人,问题往往不是代码本身,而是卡在 Python 版本、虚拟环境、静态文件路径这类“环境玄学”上。所以这篇文章我就从零开始,按照“能跑通 → 能看懂 → 能改 → 能上线”的顺序,把整个项目的关键节点拆开讲清楚。新手可以照着命令一步步复现,熟手可以直接跳到第 5 章看避坑清单,那里写的是我实际维护这类项目时踩过的血泪经验。
2. 用 Django 在本地跑通新闻站的最小命令集:从空白目录到能看到首页
2.1 先确认版本:Python 3.10 + Django 4.2 是最稳的组合
网上很多老教程还在用 Python 3.6 + Django 2.2,那套组合在 2025 年已经非常痛苦——第三方库兼容性差,pip install经常报错,而且 Django 2.2 早就停止了安全维护。如果你拿到的源码包是近两年的,大概率基于 Django 3.2 或 4.x 写的。我的建议是:先看你源码里requirements.txt写的什么版本,没有的话就统一用 Python 3.10 + Django 4.2。这个组合经过大量生产环境验证,既有新特性,又没激进到 Django 5.0 那种连url()写法都要改的程度。
# 创建独立虚拟环境,避免污染系统 Python python3 -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖(源码包里有 requirements.txt 就直接用) pip install django==4.2 celery redis pillow # 安装完验证版本 python -m django --version这里有个容易忽略的细节:pillow一定要装。新闻网站几乎必然涉及图片上传,而 Django 的ImageField底层依赖 Pillow 库。我见过有人折腾了半天ImageField报错,最后发现只是没装这个。另外celery和redis是给异步任务准备的——比如新闻发布后自动生成缩略图、定时抓取 RSS 源——如果你只是想先跑起来,这两个可以先不装,但建议装上,后面做性能优化时用得上。
2.2 创建项目和应用:新闻网站通常拆成三个 app
一个常见的拆分方式是news(前台新闻展示)、backend(后台管理逻辑)、users(用户与权限)。这么做不是为了装模作样,而是 Django 的设计哲学就是“一个应用负责一件事”。如果你把新闻模型和用户模型塞在同一个models.py里,前期没事,后期加需求时你会想骂人。
# 创建项目骨架 django-admin startproject news_site # 进入项目目录后创建三个应用 cd news_site python manage.py startapp news python manage.py startapp backend python manage.py startapp users # 顺手把数据库迁移跑一遍,确保基础表建好 python manage.py migrate注意startproject和startapp的区别:前者生成整个项目的配置目录(settings.py、urls.py等),后者只生成一个应用模块。新手常犯的错误是在startproject之前就直接startapp,导致应用目录和项目配置目录平级,INSTALLED_APPS里根本找不到。跑完migrate后,你会看到 Django 自动创建了auth_user、django_session等内置表,这些是后台管理系统能登录的底层依赖。
2.3 配置 settings.py:三个必须改的地方
settings.py是 Django 项目的中枢神经,但新手往往不敢动它。我建议先改三处:INSTALLED_APPS把刚建的三个 app 加进去;LANGUAGE_CODE改成zh-hans,TIME_ZONE改成Asia/Shanghai;STATIC_URL和MEDIA_URL配置好。这三处不动,后面步步是坑。
# settings.py 关键配置 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'news', 'backend', 'users', ] # 时区配置:显示中文,使用北京时间 LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_TZ = True # 保持 True,数据库存 UTC,展示时转本地时间 # 静态文件和媒体文件 STATIC_URL = '/static/' STATICFILES_DIRS = [] MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'USE_TZ = True这个很多人不理解。它的意思是数据库里存的是 UTC 时间,展示时按TIME_ZONE转成北京时间。如果你改成False,数据库直接存本地时间,看似方便,但一旦将来做服务器迁移或者跨时区用户访问,时间就会乱掉。我的习惯是永远保持True,只在模板渲染时用{{ article.publish_time|localtime }}转换显示。
2.4 跑起开发服务器:验证你的数据库连接
python manage.py runserver如果你看到Starting development server at http://127.0.0.1:8000/,说明基础环境已经通了。但此时访问首页会报 404,因为你还没有配置 URL 路由,也没有写任何视图。这一步的目标只是确认 Django 能正常启动、数据库迁移无误。不要急着往下写功能,先把python manage.py check跑一遍,它会检查所有配置项是否合法。我遇到过一个情况:数据库连接出错,runserver不报错,但一访问任意页面就抛OperationalError,这种坑等你上线后再发现就晚了。
3. 把后台管理从“能用”调到“好用”:Admin 配置与 RBAC 权限设计
3.1 注册模型:让 Django Admin 认识你的新闻表
Django 的 Admin 后台最诱人的地方在于:你只要把模型注册进去,它自动帮你生成增删改查界面。但默认界面很丑,字段平铺,不看代码根本不知道编辑页是什么业务逻辑。所以第一个要做的就是在backend/admin.py里注册模型,并配置list_display让列表页显示关键字段。
# backend/admin.py from django.contrib import admin from news.models import Article, Category, Tag @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): # 列表页显示的字段 list_display = ('title', 'category', 'author', 'status', 'view_count', 'publish_time') # 可点击进入编辑页的字段,一般留标题 list_display_links = ('title',) # 右侧筛选栏 list_filter = ('status', 'category', 'publish_time') # 搜索框 search_fields = ('title', 'summary', 'content') # 默认排序:最新发布在前 ordering = ('-publish_time',) # 自动填充 slug 字段 prepopulated_fields = {'slug': ('title',)} @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ('name', 'slug', 'parent', 'created_time') prepopulated_fields = {'slug': ('name',)}这段代码里有几个细节值得解释。list_display_links如果不设置,默认是每个字段都能点进编辑页,容易误操作;prepopulated_fields是博文类系统的刚需——它会在编辑标题时自动生成 URL 别名,省去手动敲 slug 的麻烦。list_filter选publish_time是必须的,因为运营人员尤其关心“某个时间段发布了哪些新闻”,有这个筛选项,后台操作效率能提升不少。
3.2 自定义 ModelForm:给后台编辑页加上效验逻辑
默认的 Admin 编辑页是 Django 根据模型字段自动推导的,但业务规则往往比模型复杂。比如“新闻标题不能超过 50 字”“草稿状态不能设置发布时间”。这类约束不能只放在前端,要在后端用ModelForm强制校验。
# backend/forms.py from django import forms from news.models import Article class ArticleAdminForm(forms.ModelForm): class Meta: model = Article fields = '__all__' def clean_title(self): title = self.cleaned_data['title'] if len(title) > 50: raise forms.ValidationError('标题不能超过50个字符') return title def clean(self): cleaned_data = super().clean() status = cleaned_data.get('status') publish_time = cleaned_data.get('publish_time') # 草稿状态不允许设置发布时间 if status == 'draft' and publish_time: raise forms.ValidationError('草稿状态不能设置发布时间,请先改为待审核或已发布') return cleaned_data然后在admin.py里把form = ArticleAdminForm加到ArticleAdmin类上。这里要特别说说clean和clean_fieldname的区别:clean_fieldname只针对单个字段,clean可以处理跨字段的联合校验——比如上面这个例子,判断状态和发布时间的关系。这两个钩子函数是 Django 表单系统最精华的部分,也是最容易被新手忽略的。很多线上内容管理系统出现“草稿竟然发布了”“空标题竟然提交成功”这类事故,根源就是只做了前端校验,没做后端 ModelForm 校验。
3.3 RBAC 权限设计:编辑、审核、管理员三种角色怎么做
新闻后台的权限模型通常不是“管理员/普通用户”这么简单,而是需要至少三级角色:编辑(能创建和修改草稿)、审核员(能把草稿改为待发布)、管理员(能发布和删除)。Django 内置的auth应用提供了Group和Permission模型,我们不需要自己写 RBAC 系统,只需要在后台配置组和权限即可。
# users/models.py from django.db import models from django.contrib.auth.models import User class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') role = models.CharField( max_length=10, choices=[('editor', '编辑'), ('reviewer', '审核员'), ('admin', '管理员')], default='editor' ) phone = models.CharField(max_length=11, blank=True) def __str__(self): return f'{self.user.username} - {self.get_role_display()}'然后在 Django Admin 里创建三个组:编辑组只勾选news.article.add_article和change_article权限,审核员额外勾选publish_article的自定义权限,管理员直接is_superuser=True。这里有个坑:Django 默认的change_article权限包含修改文章状态,如果你的业务要求审核员不能改正文但能改状态,那就要自定义权限。常见做法是在news/models.py里加一个自定义权限:
class Article(models.Model): # ... class Meta: permissions = [ ('publish_article', '可以发布文章'), ('reject_article', '可以驳回文章'), ]然后你需要执行python manage.py makemigrations news && python manage.py migrate,这个权限才会被创建到数据库里。很多新手改了权限定义却不迁移,导致后台勾选时找不到“发布文章”这个权限项——这不是 Django 的问题,是你忘了迁移。
4. 新闻发布流程的数据建模与表单校验:核心模型和字段设计
4.1 一篇文章需要哪些字段:从标题到 SEO 元信息
新闻网站的模型设计直接决定后台的可用性。字段太少,前台展示捉襟见肘;字段太多,运营人员填写成本高,后台页面像在做问卷调查。我总结的黄金字段组合是:标题、摘要、正文、封面图、分类、标签、作者、状态、发布时间、浏览量、SEO 关键词。
# news/models.py from django.db import models from django.utils import timezone from django.contrib.auth.models import User class Category(models.Model): name = models.CharField('分类名称', max_length=30) slug = models.SlugField('URL标识', unique=True) parent = models.ForeignKey( 'self', on_delete=models.CASCADE, null=True, blank=True, related_name='children', verbose_name='父分类' ) class Meta: verbose_name = '新闻分类' verbose_name_plural = '新闻分类' def __str__(self): return self.name class Article(models.Model): STATUS_CHOICES = [ ('draft', '草稿'), ('pending', '待审核'), ('published', '已发布'), ('rejected', '已驳回'), ] title = models.CharField('标题', max_length=100) slug = models.SlugField('URL标识', unique=True) summary = models.TextField('摘要', max_length=300, blank=True) content = models.TextField('正文') cover_image = models.ImageField('封面图', upload_to='covers/%Y/%m/', blank=True) category = models.ForeignKey(Category, on_delete=models.PROTECT, verbose_name='分类') tags = models.ManyToManyField('Tag', blank=True, verbose_name='标签') author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者') status = models.CharField('状态', max_length=10, choices=STATUS_CHOICES, default='draft') publish_time = models.DateTimeField('发布时间', null=True, blank=True) view_count = models.PositiveIntegerField('浏览量', default=0) seo_keywords = models.CharField('SEO关键词', max_length=100, blank=True) class Meta: verbose_name = '文章' verbose_name_plural = '文章' ordering = ['-publish_time'] def __str__(self): return self.title class Tag(models.Model): name = models.CharField('标签名', max_length=20, unique=True) def __str__(self): return self.name注意category用了on_delete=models.PROTECT,这是故意的。如果某分类下还有文章,就禁止删除该分类,避免前台出现“无头文章”。相反,author用了CASCADE,因为用户删除时文章跟着删是合理行为。这里给新手一个忠告:on_delete不要一律写成CASCADE,要根据业务逻辑逐个判断,这是设计数据模型的核心素养。
4.2 表单校验与编辑页逻辑:让运营人员一用就会
后台表单除了要校验字段,还要处理一些“业务默认值”——比如新文章创建时状态自动设为草稿,首次保存时自动把当前用户设为作者。这类逻辑可以写在ArticleAdmin.save_model方法里。
# backend/admin.py 中新增 def save_model(self, request, obj, form, change): if not change: # 新建文章时,作者自动设为当前登录用户 obj.author = request.user obj.status = 'draft' super().save_model(request, obj, form, change)这段代码解决了两个常见的坑:一是后台编辑文章时作者下拉框为空(因为没设置author默认值),二是新建文章状态为“已发布”(因为默认值设置不对)。change参数区分新建和编辑场景——新建时只有change=False,编辑时change=True。这个参数经常被忽略,但它是 Django Admin 扩展的核心入口之一。
4.3 前台新闻页怎么写:从 Django 模板到标准查询
有了后台的数据,前台就要做新闻列表页、详情页、分类页三个视图。这里要刻意避开的陷阱是 N+1 查询——list 页面如果每篇文章都单独查询分类和标签,数据库压力会非常大。正确做法是用select_related和prefetch_related。
# news/views.py from django.shortcuts import get_object_or_404, render from .models import Article, Category def article_list(request, category_slug=None): articles = Article.objects.filter(status='published') if category_slug: articles = articles.filter(category__slug=category_slug) # select_related 用于 ForeignKey 字段 articles = articles.select_related('author', 'category').prefetch_related('tags') return render(request, 'news/article_list.html', {'articles': articles}) def article_detail(request, slug): article = get_object_or_404( Article.objects.select_related('author', 'category').prefetch_related('tags'), slug=slug, status='published' ) # 浏览量 +1 的严谨写法 Article.objects.filter(pk=article.pk).update(view_count=models.F('view_count') + 1) return render(request, 'news/article_detail.html', {'article': article})F('view_count') + 1这个写法很关键。如果你用article.view_count += 1; article.save(),在并发场景下会发生“丢失更新”——两个用户同时点开文章,浏览量只加了一次。而F表达式是在数据库层面原子操作,不会出现这个竞态问题。模板里列表页和详情页的写法差异不大,几乎只有 CSS 类名的区别。唯一要提醒的是:模板标签里要用{{ article.publish_time|date:"Y-m-d H:i" }}过滤时间格式,否则会输出类似2025-04-13T12:30:00+08:00这种。别问我是怎么知道的。
5. Django 后台管理系统常见的 5 个坑:从静态文件到多表查询
5.1 静态文件在 Admin 后台显示不出来的玄学
现象:python manage.py runserver启动后,刷新后台页面,所有 CSS、JS、图片全挂,页面处于“裸奔”状态。浏览器 console 里报 404,路径指向/static/admin/css/base.css之类。
原因:这是 DEBUG 模式和静态文件服务的经典问题。Django 开发服务器默认不处理STATIC_URL指向的静态文件,除非你在urls.py里手动加上static()辅助函数。大部分教程没写这一步,导致新手直接用默认配置打开后台时一片狼藉。
解决:在项目的urls.py中修改if settings.DEBUG部分,加上静态文件服务路由。不要用STATICFILES_DIRS里面去添加 Django 自带的 admin 静态目录,那个目录在 site-packages 里,你不可能去改它。
# urls.py from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), path('news/', include('news.urls')), ] if settings.DEBUG: urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)如果你的源码包里已经有这句,但后台还是裸奔,那就检查STATIC_ROOT是否配置了,并且最好在项目根目录下建立一个static文件夹,然后执行python manage.py collectstatic,把所有 app 内 static 目录的文件收拢到一个地方。这是个容易混淆的点:STATICFILES_DIRS是开发时收集静态文件资源的额外路径,STATIC_ROOT是collectstatic产出文件的存储路径。
5.2 多表查询时你怎么就查出重复数据了
现象:前台新闻列表页出现了重复的新闻条目,每篇文章出现了两次甚至三次。翻了代码,Article.objects.filter(status='published')完全看不出毛病。
原因:这是经典的多表连接引发的笛卡尔积问题。如果你的Article通过tags字段和多个标签关联,那么查文章时会按标签数生成多行。虽然 Django ORM 会自动去重主键,但在通过filter筛选标签时,结果集会膨胀。
解决:用distinct()强制去重。
articles = Article.objects.filter(status='published', tags__name__in=['财经', '电商']).distinct()这里特别强调:distinct()要放在查询最后,如果在中间用会导致字段不一致。另一个隐患是如果你用了annotate计数(比如统计每篇文章标签数量),distinct()会破坏annotate结果,所以要根据场景判断要不要去重。我见过有人为了避免歧义,直接写only('id')去重,那是下策,会丢失模型字段。
5.3 CSRF 校验失败:后台登录后提交任何表单都报错
现象:后台登录进去之后,点击“保存”按钮,页面弹出CSRF verification failed. Request aborted.的红色错误页。开发环境、测试环境、生产环境都会遇到。
原因:很多新闻网站后台允许用户从“首页”直接进后台,没有经过 Django 的登录页。CSRF 中间件要求所有 POST 请求带有csrfmiddlewaretoken,如果模板里的表单没有加这个 token,就会报错。另外,如果用户浏览器清除缓存,或从收藏夹直接进入后台编辑页,token 也可能失效。
解决:在自定义模板的表单中加入{% csrf_token %},并在settings.py里确保CsrfViewMiddleware存在。最容易被忽视的是:如果你的后台编辑页是 A 页面跳转到 B 页面,B 页面是redirect回 A 页面时,浏览器地址栏中的 URL 带有上一个页面的 token,也会触发这个错误。所以要检查重定向逻辑。
# 在模板的表单中 <form method="post" action="{% url 'backend:article_edit' article.pk %}"> {% csrf_token %} <!-- 表单字段 --> </form>5.4 时区问题的暗坑:同一个新闻,发布时间差 8 小时
现象:在后台把发布时间设置为某日凌晨 1 点,前台页面显示却是前一天下午 5 点。数据库里存的值和展示值看起来完全对不上。
原因:USE_TZ = True时,Django 数据库里存 UTC 时间,模板引擎按 Django 的TIME_ZONE设置展示。如果你在settings.py里漏写了'zh-hans'和Asia/Shanghai,默认就是 UTC,差出 8 个小时。另一个原因是datetime.datetime.now()和timezone.now()混用——前者返回本地当前时间,后者返回 UTC 时间。
解决:统一使用django.utils.timezone.now()。模板中渲染时用{{ article.publish_time|date:"Y-m-d H:i" }}而不是直接输出原始时间。更细致的做法是:在ArticleAdmin.save_model里强制obj.publish_time = timezone.localtime(timezone.now()),确保入库的是本地时间的 UTC 表示。
5.5 数据库迁移冲突:改模型后 migrate 总是报“检测到未知操作”
现象:你对Article模型加了一个字段,执行makemigrations时提示“No changes detected”,但数据库里确实没有这个字段。或者在多人协作时,A 改了模型,B 的本地迁移文件与 A 冲突。
原因:前者是因为模型文件里的字段名和已有迁移文件冲突,或者 app 没有正确加到INSTALLED_APPS。后者是典型的“迁移文件不同步”问题,Django 的迁移系统是基于迁移文件的,不是基于模型当前状态的。如果你手动改了迁移文件,Django 的迁移状态就会错乱。
解决:如果本地没有重要数据,直接删掉 app 下的migrations文件夹(保留__init__.py),然后重新makemigrations和migrate。如果线上有数据,就需要检查迁移顺序,用python manage.py migrate app_name zero回滚,再重新迁移。我给自己定个规矩:每次改动模型,跑完迁移后立刻python manage.py showmigrations确认状态,绝不跳步。
6. 从“能跑”到“能上线”:把这套源码改成可交付项目的三个技巧
6.1 用 Gunicorn + Nginx 部署,别再用 runserver
开发环境用runserver没问题,但生产环境它性能极差,单线程、不支持高并发、安全性也堪忧。我一般用 Gunicorn 作为 WSGI 服务器,Nginx 做反向代理和静态文件服务。这是目前 Django 部署最稳的默认组合。
# 安装 Gunicorn pip install gunicorn # 启动命令(4 个 worker,每个 worker 处理并发请求) gunicorn news_site.wsgi:application -w 4 -b 127.0.0.1:8000Nginx 配置里,把/static/和/media/直接交给 Nginx 处理,Django 只处理动态请求。这是个核心优化:Django 处理静态文件的效率远低于 Nginx,生产环境必须分流。
server { listen 80; server_name yourdomain.com; location /static/ { alias /path/to/your/project/static/; } location /media/ { alias /path/to/your/project/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }6.2 用缓存解决首页高并发读取的瓶颈
新闻站的典型特征是“首页被高频访问,但内容变化不频繁”。这时候 Django 的缓存框架就派上大用场了。最简单的做法是用内存缓存或 Redis 缓存,配合cache_page装饰器,直接把整个首页缓存在内存里。
# news/views.py from django.views.decorators.cache import cache_page from django.core.cache import cache @cache_page(60 * 5) # 缓存 5 分钟 def article_list(request, category_slug=None): # ...但这里有个陷阱:缓存的是整个 HTML 页面,如果用户登录后显示“用户名”,就会泄露用户信息。所以更精确的做法是只缓存新闻列表数据,而不是渲染后的页面。我通常这么做:把查询结果序列化成 JSON 存到 Redis,然后前端用 JS 渲染——但那样就破坏了 Django 模板的语义。折中方案是用cache.set()缓存查询集:
articles = cache.get(f'articles_{category_slug}') if not articles: articles = list(Article.objects.filter(status='published').select_related(...)) cache.set(f'articles_{category_slug}', articles, 300)这里的关键是list()强制把 QuerySet 转换为 Python 列表,否则 Django 可能懒加载导致缓存没用。手动查了这么多项目,我发现很多人以为@cache_page就是救世主,但到了生产环境发现首页还是有压力——检查才发现缓存的是数据库查询,不是渲染后的结果。
6.3 用 Django-Q 或 Celery 做定时任务:定时刷新首页缓存和推送新闻
新闻网站有“整点左右推送最新新闻”的需求,或者每隔 10 分钟生成首页静态化页面。这类定时任务用 Celery 杀鸡用牛刀,如果你只是想给这个项目加个亮点,用 Django-Q 就够了。它集成在 Django 里,不用单独维护 worker 进程池,对新手非常友好。
# 在 backend/tasks.py from django_q.tasks import schedule from django.utils import timezone from datetime import timedelta def refresh_homepage_cache(): cache.delete('news_index_cache') # 重新生成首页数据 return True # 每 10 分钟执行一次 schedule( 'backend.tasks.refresh_homepage_cache', schedule_type='I', # I = 间隔时间 minutes=10 )这个写法的好处是代码量少、可读性强,而且不需要单独配置消息队列中间件。如果将来这个新闻站流量涨到日活百万级别,你再迁移到 Celery 也不迟——核心逻辑还在tasks.py里,只是调度框架换了。我的习惯是:先保证业务逻辑正确,再考虑架构扩展,不要一上来就上全量组件,否则你一个新手光是把 Redis、Celery、RabbitMQ 全部跑通就已经耗尽热情了。
6.4 上线前的检查清单:你不想在凌晨三点被叫醒
我每次部署这类新闻站,都会在python manage.py check --deploy跑一遍,它会自动检查 DEBUG 开关、安全中间件、CSRF 配置等 20 多项风险。这项命令生出的报告长度很感人,但你要优先处理几条:DEBUG必须为 False;ALLOWED_HOSTS必须配置自己的域名;SECRET_KEY不要写在代码仓库里,用环境变量注入;数据库建议换掉 SQLite,用 PostgreSQL 或者 MySQL。
pip install psycopg2-binary然后在settings.py里改数据库配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'news_site', 'USER': 'news_user', 'PASSWORD': 'your_secure_password', 'HOST': '127.0.0.1', 'PORT': '5432', } }这里的坑是:如果你本地开发用的是 SQLite,迁移文件和数据格式都兼容 PostgreSQL 吗?答案是迁移文件基本兼容,但数据要重新导入。所以我建议从一开始就用 PostgreSQL 或 MySQL 开发,不要中途换库。另一个坑是:从 SQLite 迁移到 PostgreSQL 时,JSONField可能会有序列化问题。这类问题在中后期特别容易让人崩溃,所以这块的规划要尽早做。
6.5 最后说一个我自己的习惯:维护一个“线上问题和修复”的文档
我第一次用 Django 做新闻站部署时,凌晨三点被 Nginx 的 502 错误叫醒,排查发现是gunicorn的 worker 全部卡死。后来我学乖了,在项目根目录放一个OPS.md,每次线上操作、每次踩坑、每个命令,全部记下来。比如这次部署用到的 gunicorn 启动命令、Nginx 配置路径、缓存的 key 名称、定时任务的调度间隔,全部写在里面。
这个文档的价值在于,当这个项目被半年后的你接手时,你不需要靠回忆去翻代码,直接看 OPS.md 就能知道这个系统的运转逻辑。我做技术这些年,最贵重的东西从来不是代码本身,而是代码背后的决策记录。希望这篇文章能帮你把这套 Django 新闻站真正跑起来、用起来——从“能跑”到“能上线”的距离,往往就差一本踩坑笔记。
本文还有配套的精品资源,点击获取