DJCMS 技术文档

基于 Django 6.0 的内容管理系统 — 完整技术架构与开发参考手册

一、技术架构

1.1 架构概述

DJCMS 采用 Django MVT(Model-View-Template)架构,分为四个独立应用模块:

┌─────────────────────────────────────────────────────────────┐ │ 客户端层 │ │ 浏览器(前台) │ 浏览器(管理后台) │ API客户端/移动端 │ └───────┬────────┴────────┬────────────┴────────┬────────────┘ │ │ │ ┌───────▼─────────────────▼──────────────────────▼────────────┐ │ URL 路由层 │ │ cms_front.urls │ cms_admin.urls │ cms_api.urls │ └───────┬──────────┴────────┬─────────┴────────┬─────────────┘ │ │ │ ┌───────▼───────────────────▼────────────────────▼────────────┐ │ 视图层 │ │ cms_front.views │ cms_admin.views │ cms_api.views │ └───────┬───────────┴────────┬──────────┴────────┬────────────┘ │ │ │ ┌───────▼────────────────────▼─────────────────────▼──────────┐ │ 业务逻辑层 │ │ cms_core.models │ cms_core.forms │ context_processors │ └───────┬───────────┴────────┬──────────┴──────────┬──────────┘ │ │ │ ┌───────▼────────────────────▼───────────────────────▼────────┐ │ 数据持久层 │ │ Django ORM → SQLite / MySQL / PostgreSQL │ └─────────────────────────────────────────────────────────────┘

1.2 应用职责划分

应用职责依赖方向
cms_core数据模型、表单、上下文处理器、Admin注册、站点地图被其他所有应用依赖
cms_admin管理后台视图(仪表盘、媒体库、站点设置)依赖 cms_core
cms_front前台展示视图(首页、文章、搜索、评论、订阅)依赖 cms_core
cms_apiRESTful API(序列化器、视图集、权限)依赖 cms_core
djcms项目配置、中间件、根路由依赖所有应用

1.3 请求处理流程

HTTP Request │ ▼ CorsMiddleware ──── 跨域处理 │ ▼ SecurityMiddleware ──── 安全头注入 │ ▼ SessionMiddleware ──── 会话加载 │ ▼ CommonMiddleware ──── URL规范化 │ ▼ CsrfViewMiddleware ──── CSRF验证 │ ▼ AuthenticationMiddleware ──── 用户认证 │ ▼ MessageMiddleware ──── 消息加载 │ ▼ XFrameOptionsMiddleware ──── 点击劫持防护 │ ▼ AccountMiddleware ──── AllAuth账户 │ ▼ URLRedirectMiddleware ──── 自定义URL重定向 │ ▼ URL Router ──── 路由分发 │ ├── /cms-admin/* → cms_admin.views ├── /api/* → cms_api.views ├── /admin/* → Django Admin └── /* → cms_front.views │ ▼ View ──── 业务处理 │ ▼ Model / Serializer ──── 数据操作 │ ▼ Template / JSON ──── 响应渲染 │ ▼ HTTP Response

二、数据模型

2.1 模型继承体系

所有业务模型继承自 TimeStampedModel 抽象基类,自动包含 created_atupdated_at 时间戳字段。

class TimeStampedModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    class Meta:
        abstract = True

继承关系:

TimeStampedModel (abstract) ├── Category ├── Page ├── Article ├── Media ├── Comment ├── Menu │ └── MenuItem (通过 ForeignKey 关联) ├── NewsletterSubscriber ├── URLRedirect ├── ContentBlock └── Notification AuthorProfile (独立,OneToOneField → User) SiteSettings (独立,单例模式)

2.2 模型关系图

User ─────────────────────────────────────────────────────┐ │ 1 │ ├── 1:1 ── AuthorProfile │ │ (user) │ │ │ ├── 1:N ── Article (author) │ │ │ 1 │ │ ├── N:1 ── Category (articles) │ │ │ │ 1 │ │ │ └── 1:N ── Category (children) │ │ │ (parent, self-ref) │ │ │ │ │ ├── N:M ── Tags (TaggableManager) │ │ │ │ │ └── 1:N ── Comment (article) │ │ │ 1 │ │ └── 1:N ── Comment (replies) │ │ (parent, self-ref) │ │ │ ├── 1:N ── Page (author) │ │ │ ├── 1:N ── Media (uploaded_by) │ │ │ └── 1:N ── Notification (user) │ Menu ── 1:N ── MenuItem ├── N:1 ── Page ├── N:1 ── Article └── N:1 ── Category SiteSettings (单例,pk=1) NewsletterSubscriber (独立) URLRedirect (独立) ContentBlock (独立)

2.3 模型详细定义

Category(分类)

字段类型约束说明
nameCharField(100)必填分类名称
slugSlugField(120)unique, 自动生成URL别名
descriptionTextField可选描述
parentForeignKey(self)SET_NULL, 可选父分类(无限级嵌套)
iconCharField(50)可选Font Awesome图标类名
sort_orderIntegerField默认0排序权重
is_activeBooleanField默认True是否启用
seo_titleCharField(120)可选SEO标题
seo_descriptionTextField可选SEO描述
created_atDateTimeFieldauto_now_add创建时间
updated_atDateTimeFieldauto_now更新时间

关键方法

  • save() — slug为空时自动从name生成
  • total_articles — 属性,返回已发布文章数
  • get_absolute_url() — 返回 /category/<slug>/

Meta配置ordering = ['sort_order', 'name']


Article(文章)

字段类型约束说明
titleCharField(200)必填标题
slugSlugField(250)uniqueURL别名
contentRichTextUploadingField必填CKEditor富文本内容
excerptTextField可选摘要,为空时自动截取前200字符
featured_imageImageField可选上传至 articles/
categoryForeignKey(Category)SET_NULL, 可选所属分类
tagsTaggableManager可选django-taggit标签
authorForeignKey(User)SET_NULL, 可选作者
statusCharField(20)draft/published/archived状态
is_activeBooleanField默认True是否启用
is_featuredBooleanField默认False是否推荐
allow_commentsBooleanField默认True允许评论
publish_dateDateTimeField默认now发布时间
view_countPositiveIntegerField默认0浏览次数
seo_titleCharField(120)可选SEO标题
seo_descriptionTextField可选SEO描述
seo_keywordsCharField(255)可选SEO关键词

数据库索引

  • (status, is_active, publish_date) — 列表查询优化
  • (slug) — URL查找优化

关键方法

  • save() — excerpt为空时自动从content中去除HTML标签后截取前200字符
  • get_absolute_url() — 返回 /articles/<year>/<month>/<day>/<slug>/

Page(页面)

字段类型约束说明
titleCharField(200)必填标题
slugSlugField(250)uniqueURL别名
contentRichTextUploadingField必填富文本内容
excerptTextField可选摘要
featured_imageImageField可选上传至 pages/
statusCharField(20)draft/published状态
is_activeBooleanField默认True是否启用
is_homeBooleanField默认False设为首页(全局唯一)
templateCharField(100)默认 cms_front/page.html模板路径
authorForeignKey(User)SET_NULL, 可选作者
publish_dateDateTimeField默认now发布时间
view_countPositiveIntegerField默认0浏览次数
seo_title / seo_description / seo_keywords可选SEO字段

关键方法

  • save() — is_home=True时,自动将其他页面的is_home设为False(确保唯一)
  • get_absolute_url() — 首页返回 /,其他返回 /<slug>/

Media(媒体)

字段类型约束说明
titleCharField(200)必填标题
fileFileField必填上传至 uploads/%Y/%m/
thumbnailImageField可选上传至 uploads/thumbnails/
media_typeCharField(20)image/document/video/audio/other类型
alt_textCharField(255)可选替代文本
captionCharField(500)可选说明
descriptionTextField可选描述
file_sizePositiveIntegerField默认0自动从文件获取
widthPositiveIntegerField可选图片宽度
heightPositiveIntegerField可选图片高度
uploaded_byForeignKey(User)SET_NULL, 可选上传者
is_activeBooleanField默认True是否启用

属性方法

  • url — 返回文件URL
  • size_display — 自动格式化为 B/KB/MB

SiteSettings(站点设置)— 单例模式

实现原理

class SiteSettings(models.Model):
    # ... 30+ 字段 ...

    def save(self, *args, **kwargs):
        SiteSettings.objects.exclude(pk=self.pk).delete()
        super().save(*args, **kwargs)

    @classmethod
    def get_settings(cls):
        obj, created = cls.objects.get_or_create(pk=1)
        return obj
  • save() 时删除除自身外的所有记录,确保数据库中只有一条记录
  • get_settings() 类方法始终返回 pk=1 的实例,不存在则创建
  • Admin 中 has_add_permission() 在已有记录时返回 False,has_delete_permission() 始终返回 False

字段分组

分组字段
基本信息site_name, site_description, site_logo, site_favicon, footer_text
SEOmeta_title, meta_description, meta_keywords
联系方式contact_email, contact_phone, contact_address
社交媒体facebook_url, twitter_url, instagram_url, linkedin_url, youtube_url, github_url
分析代码google_analytics_id, custom_head_html, custom_footer_html
邮件订阅enable_newsletter, newsletter_description
邮件服务smtp_host, smtp_port, smtp_user, smtp_password, smtp_use_tls, notification_email
系统设置posts_per_page, enable_comments, maintenance_mode, cache_timeout, enable_dark_mode, enable_lazy_loading

Comment(评论)

字段类型约束说明
articleForeignKey(Article)CASCADE所属文章
author_nameCharField(100)必填评论者昵称
author_emailEmailField必填评论者邮箱
author_websiteURLField可选评论者网站
contentTextField必填评论内容
is_approvedBooleanField默认False审核状态
parentForeignKey(self)CASCADE, 可选父评论(嵌套回复)
ip_addressGenericIPAddressField可选IP地址

Menu / MenuItem(导航菜单)

Menu:name, slug, description

MenuItem

字段类型说明
menuForeignKey(Menu)所属菜单
parentForeignKey(self)父菜单项(多级菜单)
titleCharField(100)标题
link_typeCharField(20)page/article/category/custom
link_urlCharField(500)自定义链接URL
pageForeignKey(Page)关联页面
articleForeignKey(Article)关联文章
categoryForeignKey(Category)关联分类
iconCharField(50)图标
targetCharField(20)_self/_blank
sort_orderIntegerField排序
is_activeBooleanField是否启用

get_url() 方法:根据 link_type 智能返回URL:

  • pagepage.get_absolute_url()
  • articlearticle.get_absolute_url()
  • categorycategory.get_absolute_url()
  • customlink_url

NewsletterSubscriber(邮件订阅者)

字段类型说明
emailEmailField(unique)邮箱
is_activeBooleanField是否激活
subscribed_atDateTimeField订阅时间
unsubscribed_atDateTimeField退订时间
tokenCharField(100, unique)验证令牌(UUID自动生成)

save() 方法:token为空时自动生成 uuid.uuid4().hex


URLRedirect(URL重定向)

字段类型说明
old_pathCharField(500, unique, db_index)原路径
new_pathCharField(500)新路径
redirect_typeCharField(3)301/302
is_activeBooleanField是否启用
hit_countPositiveIntegerField命中次数

ContentBlock(内容块)

字段类型说明
nameCharField(100, unique)名称
slugSlugField(120, unique)标识符
block_typeCharField(20)html/text/banner/sidebar/footer
contentTextField内容
imageImageField图片
link_urlURLField链接URL
is_activeBooleanField是否启用
sort_orderIntegerField排序

Notification(通知)

字段类型说明
titleCharField(200)标题
messageTextField消息内容
notification_typeCharField(20)info/success/warning/danger
is_readBooleanField是否已读
userForeignKey(User, 可选)所属用户(null=全局通知)
urlCharField(500)相关链接

AuthorProfile(作者档案)

字段类型说明
userOneToOneField(User)关联用户
bioTextField个人简介
avatarImageField头像(上传至 avatars/
websiteURLField个人网站
twitterCharField(100)Twitter账号
githubCharField(100)GitHub账号
job_titleCharField(100)职位
display_emailBooleanField是否公开邮箱

属性方法

  • article_count — 该作者已发布文章数
  • total_views — 该作者文章总浏览量

三、URL路由

3.1 根路由配置

文件:djcms/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),           # Django默认Admin
    path('cms-admin/', include('cms_admin.urls')),  # 自定义管理后台
    path('ckeditor/', include('ckeditor_uploader.urls')),  # CKEditor上传
    path('api/', include('cms_api.urls')),      # REST API
    path('accounts/', include('allauth.urls')), # 用户认证
    path('sitemap.xml', sitemap, {...}),        # 站点地图
    path('', include('cms_front.urls')),        # 前台(catch-all,必须在最后)
]

3.2 前台路由

文件:cms_front/urls.py,命名空间:cms_front

URL模式视图函数名称HTTP方法
/homehomeGET
/articles/article_listarticle_listGET
/articles/<int:year>/<int:month>/<int:day>/<slug:slug>/article_detailarticle_detailGET/POST
/category/<slug:slug>/category_detailcategory_detailGET
/tag/<slug:tag>/tag_detailtag_detailGET
/search/searchsearchGET
/contact/contactcontactGET/POST
/author/<str:username>/author_detailauthor_detailGET
/newsletter/subscribe/newsletter_subscribenewsletter_subscribePOST
/newsletter/unsubscribe/<str:token>/newsletter_unsubscribenewsletter_unsubscribeGET
/api/subscribe/subscribe_apisubscribe_apiPOST
/<slug:slug>/page_detailpage_detailGET

查询参数

视图参数说明
article_list?category=<slug>按分类筛选
article_list?tag=<name>按标签筛选
article_list?q=<keyword>搜索关键词
article_list?year=<YYYY>按年份筛选
article_list?month=<MM>按月份筛选
article_list?page=<N>分页页码
search?q=<keyword>搜索关键词(最少2字符)

3.3 管理后台路由

文件:cms_admin/urls.py,命名空间:cms_admin

URL模式视图函数名称装饰器
/cms-admin/dashboard/dashboarddashboard@staff_member_required
/cms-admin/media-library/media_librarymedia_library@staff_member_required
/cms-admin/settings/site_settings_viewsite_settings@staff_member_required

3.4 API路由

文件:cms_api/urls.py,命名空间:cms_api

URL模式视图方法权限
/api/articles/ArticleViewSetGET/POSTIsAdminOrReadOnly
/api/articles/<slug>/ArticleViewSetGET/PUT/PATCH/DELETEIsAdminOrReadOnly
/api/articles/archive/ArticleViewSet.archiveGETAllowAny
/api/articles/<slug>/increment_view/ArticleViewSet.increment_viewPOSTAllowAny
/api/categories/CategoryViewSetGET/POSTIsAdminOrReadOnly
/api/categories/<slug>/CategoryViewSetGET/PUT/PATCH/DELETEIsAdminOrReadOnly
/api/pages/PageViewSetGET/POSTIsAdminOrReadOnly
/api/pages/<slug>/PageViewSetGET/PUT/PATCH/DELETEIsAdminOrReadOnly
/api/pages/<slug>/increment_view/PageViewSet.increment_viewPOSTAllowAny
/api/media/MediaViewSetGET/POSTIsAdminUser
/api/media/<pk>/MediaViewSetGET/PUT/PATCH/DELETEIsAdminUser
/api/comments/CommentListCreateViewGET/POST读取公开/创建公开
/api/settings/SiteSettingsViewGET/PUT/PATCHIsAdminUser
/api/stats/site_statsGETAllowAny
/api/search/search_apiGETAllowAny

四、视图层

4.1 前台视图

home(request)

首页视图。优先检查是否设置了自定义首页(Page.is_home=True),有则渲染该页面;否则展示默认首页,包含推荐文章、最新文章、分类探索、标签云、站点统计。

数据查询

featured_articles = Article.objects.filter(
    status='published', is_active=True, is_featured=True
).select_related('author', 'category')[:6]

latest_articles = Article.objects.filter(
    status='published', is_active=True
).select_related('author', 'category')[:9]

categories = Category.objects.filter(
    is_active=True, articles__status='published'
).annotate(article_count=Count('articles')).order_by('-article_count')[:8]

tag_cloud = Article.tags.most_common()[:15]

优化:使用 select_related 预加载 author 和 category,避免 N+1 查询。


article_detail(request, year, month, day, slug)

文章详情视图。通过年月日+slug精确定位文章,自动递增浏览量,展示评论、相关文章、上下篇导航。

关键逻辑

# 精确日期范围查询
target_date = datetime(year, month, day, tzinfo=get_current_timezone())
next_date = target_date + timedelta(days=1)
article = get_object_or_404(Article, slug=slug, publish_date__gte=target_date, publish_date__lt=next_date)

# 浏览量递增(使用update避免竞态条件)
Article.objects.filter(pk=article.pk).update(view_count=article.view_count + 1)

# 评论处理(POST提交,需审核)
if request.method == 'POST':
    form = CommentForm(request.POST)
    if form.is_valid():
        comment = form.save(commit=False)
        comment.article = article
        comment.ip_address = request.META.get('REMOTE_ADDR', '')
        comment.save()

article_list(request)

文章列表视图。支持多维度筛选(分类/标签/搜索/日期),使用 Django Paginator 分页,右侧栏展示分类、近期文章、归档、标签云。

筛选逻辑

if category_slug:
    articles_list = articles_list.filter(category__slug=category_slug)
if tag:
    articles_list = articles_list.filter(tags__name__iexact=tag)
if search_query:
    articles_list = articles_list.filter(
        Q(title__icontains=search_query) |
        Q(content__icontains=search_query) |
        Q(excerpt__icontains=search_query)
    )

contact(request)

联系表单视图。POST提交时发送邮件至站点配置的通知邮箱,使用 Django send_mail


newsletter_subscribe(request)

AJAX订阅端点。仅接受POST请求,返回JSON响应。

@require_POST
def newsletter_subscribe(request):
    form = NewsletterForm(request.POST)
    if form.is_valid():
        form.save()
        return JsonResponse({'success': True, 'message': '订阅成功!'})
    else:
        return JsonResponse({'success': False, 'message': errors[0]})

subscribe_api(request)

API订阅端点。支持JSON body和表单数据两种格式,豁免CSRF(@csrf_exempt)。

4.2 管理后台视图

dashboard(request)

控制台仪表盘。装饰器 @staff_member_required 限制仅管理员访问。

统计数据:文章数/发布数/草稿数/归档数、页面数、分类数、媒体数、评论数/待审核数、订阅数、重定向数、总浏览量、未读通知数。

图表数据(JSON序列化传给 Chart.js):

  • daily_chart — 30天发布趋势(逐日查询)
  • status_chart — 文章状态分布
  • category_chart — 分类文章分布
  • media_chart — 媒体类型分布

site_settings_view(request)

站点设置视图。POST时手动处理各类型字段:

# 字符串/文本字段
for field in fields:
    setattr(settings, field, request.POST.get(field, ''))

# 布尔字段(checkbox未选中时不出现在POST中)
for field in bool_fields:
    setattr(settings, field, field in request.POST)

# 整数字段
for field in int_fields:
    setattr(settings, field, int(request.POST[field]))

# 文件字段
for field in fields:
    if field in request.FILES:
        setattr(settings, field, request.FILES[field])

4.3 API视图

自定义权限类

class IsAdminOrReadOnly(permissions.BasePermission):
    """管理员可写,所有人可读"""
    def has_permission(self, request, view):
        if request.method in permissions.SAFE_METHODS:
            return True
        return request.user and request.user.is_staff

ArticleViewSet

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.filter(is_active=True)
    serializer_class = ArticleSerializer
    lookup_field = 'slug'
    permission_classes = [IsAdminOrReadOnly]
    filterset_fields = ['category', 'author', 'status']

    @action(detail=True, methods=['post'])
    def increment_view(self, request, slug=None):
        """浏览量递增"""
        article = self.get_object()
        article.view_count += 1
        article.save(update_fields=['view_count'])
        return Response({'view_count': article.view_count})

    @action(detail=False, methods=['get'])
    def archive(self, request):
        """文章归档(按年月分组)"""
        ...

CommentListCreateView

class CommentListCreateView(generics.ListCreateAPIView):
    queryset = Comment.objects.all()

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return CommentCreateSerializer  # 不含is_approved
        return CommentSerializer  # is_approved只读

site_stats(request)

公开统计接口,返回站点核心数据。

search_api(request)

全局搜索接口,同时搜索文章和页面,返回合并结果。


五、序列化器

文件:cms_api/serializers.py

5.1 序列化器列表

序列化器模型用途特殊处理
CategorySerializerCategory详情article_counturl
CategoryListSerializerCategory列表精简版
PageSerializerPage详情author_name
PageListSerializerPage列表精简版
ArticleSerializerArticle详情TaggitSerializer混入,含 author_name/category_name/category_slug/url
ArticleListSerializerArticle列表精简版
MediaSerializerMedia详情/列表uploaded_by_name/url/size_display
CommentSerializerComment读取is_approved 只读
CommentCreateSerializerComment创建不含 is_approved(默认待审核)
SiteSettingsSerializerSiteSettings读写全字段
NewsletterSubscriberSerializerNewsletterSubscriber读写
URLRedirectSerializerURLRedirect读写
ContentBlockSerializerContentBlock读写
NotificationSerializerNotification读写
AuthorProfileSerializerAuthorProfile详情username/article_count/total_views

5.2 标签序列化

ArticleSerializer 使用 TaggitSerializer 混入处理 django-taggit 标签:

class ArticleSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField()
    author_name = serializers.CharField(source='author.username', read_only=True)
    category_name = serializers.CharField(source='category.name', read_only=True)
    url = serializers.SerializerMethodField()

    def get_url(self, obj):
        return obj.get_absolute_url()

六、表单

文件:cms_core/forms.py

6.1 CommentForm

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['author_name', 'author_email', 'author_website', 'content']

    # 验证规则
    clean_author_name(): len >= 2
    clean_content(): len >= 5

6.2 NewsletterForm

class NewsletterForm(forms.ModelForm):
    class Meta:
        model = NewsletterSubscriber
        fields = ['email']

    # 验证规则
    clean_email(): 检查 NewsletterSubscriber.objects.filter(email=email, is_active=True).exists()

6.3 ContactForm

class ContactForm(forms.Form):
    name = CharField(max_length=100)      # len >= 2
    email = EmailField()
    subject = CharField(max_length=200)
    message = CharField(widget=Textarea)   # len >= 10

七、中间件

7.1 URLRedirectMiddleware

文件:djcms/middleware.py

功能:在请求处理前检查自定义URL重定向规则。

处理逻辑

class URLRedirectMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        path = request.path

        # 跳过管理后台、API、CKEditor路径
        skip_prefixes = ['/admin/', '/cms-admin/', '/api/', '/ckeditor/', '/static/', '/media/']
        if any(path.startswith(prefix) for prefix in skip_prefixes):
            return self.get_response(request)

        # 查询匹配的重定向规则
        try:
            redirect = URLRedirect.objects.get(old_path=path, is_active=True)
            redirect.hit_count += 1
            redirect.save(update_fields=['hit_count'])
            return HttpResponsePermanentRedirect if redirect.redirect_type == '301' else HttpResponseRedirect
        except URLRedirect.DoesNotExist:
            pass

        return self.get_response(request)

性能考虑:每次请求都查询数据库。生产环境建议添加缓存层。


八、上下文处理器

文件:cms_core/context_processors.py

函数 site_settings(request) 向所有模板注入全局变量:

变量类型来源说明
site_settingsSiteSettingsget_settings()完整站点设置对象
site_namestrsettings.site_name站点名称
site_descriptionstrsettings.site_description站点描述
site_logoImageFieldsettings.site_logoLogo
categoriesQuerySetCategory查询前10个有文章的活跃分类
main_menuMenuMenu.objects.get(slug='main')主导航菜单(含prefetch items)
footer_textstrsettings.footer_text页脚文本
contact_emailstrsettings.contact_email联系邮箱
ga_idstrsettings.google_analytics_idGA追踪ID
enable_commentsboolsettings.enable_comments评论开关
enable_newsletterboolsettings.enable_newsletter订阅开关
enable_dark_modeboolsettings.enable_dark_mode暗色模式开关
posts_per_pageintsettings.posts_per_page每页文章数
unread_notificationsintNotification查询未读通知数(仅管理员)
sidebar_blocksQuerySetContentBlock查询侧边栏内容块
calculate_reading_timefunction本地函数阅读时间计算(500字/分钟)
newsletter_descriptionstrsettings.newsletter_description订阅说明
published_pagesQuerySetPage查询已发布非首页页面列表

calculate_reading_time(html_content):去除HTML标签后按500字/分钟计算阅读时间,最少1分钟。


九、Admin后台配置

文件:cms_core/admin.py

9.1 全局配置

admin.site.site_header = 'DJCMS 内容管理系统'
admin.site.site_title = 'DJCMS 管理后台'
admin.site.index_title = '仪表盘'

9.2 各模型Admin配置

模型Admin类list_displaylist_filtersearch_fields特殊功能
CategoryCategoryAdminname, slug, parent_link, sort_order, article_count, is_activeis_activename, descriptionslug自动填充,SEO折叠区
PagePageAdmintitle, slug, status_badge, is_home_badge, author, publish_date, view_countstatus, is_active, is_home, publish_datetitle, content自动设置author,date_hierarchy
ArticleArticleAdmintitle, category_link, author, status_badge, is_featured_badge, publish_date, view_count, tag_liststatus, is_active, is_featured, category, publish_datetitle, content, excerpt6个批量操作,prefetch_related('tags')
MediaMediaAdminpreview, title, media_type_badge, size_display, uploaded_by, created_atmedia_type, created_attitle, alt_text, caption图片预览(60px/400px),自动设置uploaded_by
CommentCommentAdminauthor_name, article_link, content_preview, is_approved_badge, created_atis_approved, created_atauthor_name, author_email, content批量审核/取消审核
MenuMenuAdminname, slug, item_countname, descriptionMenuItem内联编辑(TabularInline)
MenuItemMenuItemAdmintitle, menu, link_type, link_url_display, sort_order, is_activemenu, link_type, is_activetitle, link_urlsort_order可编辑
SiteSettingsSiteSettingsAdmin单例模式,7个fieldsets
NewsletterSubscriberNewsletterSubscriberAdminemail, is_active, subscribed_at, unsubscribed_atis_active, subscribed_atemail导出CSV,token只读
URLRedirectURLRedirectAdminold_path, new_path, redirect_type, is_active, hit_count, created_atredirect_type, is_activeold_path, new_pathhit_count只读
ContentBlockContentBlockAdminname, slug, block_type, is_active, sort_orderblock_type, is_activename, contentslug自动填充
NotificationNotificationAdmintitle, notification_type, user, is_read, created_atnotification_type, is_read, created_attitle, message批量标记已读/未读
AuthorProfileAuthorProfileAdminuser, job_title, article_count, total_views, websiteuser__username, bio, job_titlearticle_count/total_views只读

9.3 批量操作

ArticleAdmin

操作说明
make_published批量设为已发布
make_draft批量设为草稿
make_featured批量设为推荐
unmake_featured批量取消推荐
export_as_json导出选中文章为JSON文件
export_as_csv导出选中文章为CSV文件(含BOM头)

CommentAdminapprove_commentsdisapprove_comments

NotificationAdminmark_as_readmark_as_unread

NewsletterSubscriberAdminexport_subscribers(CSV)


十、模板系统

10.1 模板继承结构

前台

base.html (前台基础模板) ├── cms_front/home.html ├── cms_front/article_list.html ├── cms_front/article_detail.html ├── cms_front/category_detail.html ├── cms_front/tag_detail.html ├── cms_front/search.html ├── cms_front/contact.html ├── cms_front/author_detail.html └── cms_front/page.html

后台

admin/base.html (后台基础模板,覆盖Django默认) ├── admin/base_site.html ├── admin/dashboard.html ├── admin/media_library.html ├── admin/site_settings.html ├── admin/login.html ├── admin/change_list.html ├── admin/change_form.html ├── admin/change_list_object_tools.html ├── admin/delete_confirmation.html ├── admin/object_history.html └── admin/search_form.html

10.2 前台基础模板 block 结构

base.html 定义的 block:

Block说明
{% block title %}页面标题
{% block extra_css %}额外CSS
{% block content %}主内容区
{% block extra_js %}额外JS

10.3 后台基础模板结构

admin/base.html 采用左侧边栏 + 右侧内容区布局:

<div id="container">
    <aside class="sidebar">...</aside>     <!-- 固定侧边栏 -->
    <div class="content-wrapper">
        <nav class="content-topbar">...</nav>  <!-- 面包屑导航 -->
        <main class="main">
            <div class="main-content">
                {% block content %}{% endblock %}
            </div>
        </main>
    </div>
</div>

十一、静态资源

11.1 CSS架构

style.css(前台,863行)

CSS变量体系:

:root {
    --primary: #4361ee;
    --primary-dark: #3a56d4;
    --secondary: #6c757d;
    --success: #06d6a0;
    --danger: #ef476f;
    --warning: #ffd166;
    --info: #118ab2;
    --dark: #1a1a2e;
    --light: #f8f9fa;
    --gray-50: #f9fafb;
    --gray-100: #f3f4f6;
    --sidebar-width: 260px;
    --transition: all 0.3s ease;
}

核心样式模块:

  • 导航栏(毛玻璃效果 backdrop-filter: blur(10px)
  • 推荐文章大图展示区
  • 文章卡片(悬浮动画 transform: translateY(-4px)
  • 分类卡片(悬浮变色边框)
  • 标签云
  • CTA区域
  • 文章内容排版(标题/代码/引用/表格)
  • 分页器
  • 表单样式
  • 阅读进度条
  • 返回顶部按钮
  • TOC目录
  • 骨架屏加载动画

admin.css(后台,556行)

完整设计系统,包含:

  • CSS变量(颜色/阴影/圆角/过渡)
  • 侧边栏深色渐变(#0f172a → #1a2332
  • 内容区布局
  • 数据表格样式
  • 变更列表/表单覆盖
  • 仪表盘统计卡片/图表/快捷操作
  • 响应式(@media (max-width: 992px) 侧边栏收缩)

dark-mode.css(暗色模式,145行)

深蓝紫色调(#1a1a2e / #16213e),覆盖所有组件。

ckeditor.css(编辑器内容样式,82行)

CKEditor内容区域排版,确保编辑器内显示效果与前台一致。

11.2 JavaScript

文件:static/js/main.js(170行)

功能实现方式
Tooltip初始化bootstrap.Tooltip
锚点平滑滚动scrollIntoView({ behavior: 'smooth' })
Alert自动关闭setTimeout 5秒后 bootstrap.Alert.close()
表单防重复提交时 button.disabled = true + 添加loading文字
返回顶部scrollY > 300 时显示按钮
图片懒加载IntersectionObserver + 降级方案
导航栏高亮location.pathname 匹配
邮件订阅AJAXfetch() + showToast()
showToast(type, message)Bootstrap Toast组件
formatDate(dateStr)日期格式化
truncateText(text, length)文本截断

十二、SEO站点地图

文件:cms_core/sitemaps.py

Sitemap类包含内容changefreqprioritylastmod
PageSitemap已发布活跃页面weekly0.8updated_at
ArticleSitemap已发布活跃文章daily0.6updated_at
CategorySitemap活跃分类weekly0.5
StaticViewSitemap首页/文章列表/联系我们daily0.5

URL配置:

path('sitemap.xml', sitemap, {
    'sitemaps': {
        'pages': PageSitemap,
        'articles': ArticleSitemap,
        'categories': CategorySitemap,
        'static': StaticViewSitemap,
    }
}),

十三、管理命令

13.1 generate_articles

文件:cms_core/management/commands/generate_articles.py

用途:批量生成测试文章数据。

用法

python manage.py generate_articles

特点

  • 内置约150篇预定义文章数据
  • 涵盖 Python/JavaScript/Web/DevOps/设计/生活/教程 分类
  • 随机分配作者、发布日期、浏览量
  • 15%概率设为推荐文章
  • 90%概率为已发布状态
  • 自动处理slug冲突
  • 幂等操作(已存在的slug会自动追加后缀)

十四、种子数据

文件:seed_data.py

用途:初始化项目基础数据。

用法

python seed_data.py

创建内容

  • 8个分类(含父子关系)
  • 12篇精选文章(含完整HTML内容和标签)
  • 2个页面(关于我们、隐私政策)
  • 作者档案
  • 主导航菜单(含菜单项)
  • 站点设置
  • 侧边栏内容块

十五、配置参考

15.1 Django设置

文件:djcms/settings.py

配置项说明
DEBUGTrue调试模式
LANGUAGE_CODEzh-hans中文简体
TIME_ZONEAsia/Shanghai上海时区
SITE_ID1Django Sites
LOGIN_REDIRECT_URL/cms-admin/dashboard/登录后跳转
LOGIN_URL/admin/login/登录页URL
EMAIL_BACKENDconsole开发环境邮件输出到控制台

15.2 CKEditor配置

配置项
CKEDITOR_UPLOAD_PATHuploads/
CKEDITOR_IMAGE_BACKENDpillow
默认工具栏full
自定义工具栏CMS(完整)、CMS_Simple(精简)
内容CSS/static/css/ckeditor.css
高度500px
插件codesnippet, widget, lineutils, image2, uploadimage, filebrowser

15.3 REST Framework配置

配置项
默认权限IsAuthenticatedOrReadOnly
分页PageNumberPagination,每页20条
认证SessionAuthentication + BasicAuthentication
渲染器JSONRenderer + BrowsableAPIRenderer

15.4 CORS配置

配置项
CORS_ALLOW_ALL_ORIGINSTrue
CORS_ALLOW_CREDENTIALSTrue

15.5 日志配置

LOGGING = {
    'handlers': {
        'file': {
            'level': 'ERROR',
            'class': 'logging.FileHandler',
            'filename': BASE_DIR / 'logs' / 'django.log',
        }
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'ERROR',
        }
    }
}

十六、数据库迁移

16.1 迁移历史

迁移日期内容
0001_initial2026-05-09创建7个模型:Menu, SiteSettings, Category, Article, Comment, Media, Page, MenuItem + 2个索引
0002_contentblock_...2026-05-09新增5个模型:ContentBlock, NewsletterSubscriber, URLRedirect, AuthorProfile, Notification + SiteSettings新增6个字段

16.2 数据库切换

从 SQLite 切换到 PostgreSQL/MySQL:

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'djcms',
        'USER': 'djcms_user',
        'PASSWORD': 'your_password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

十七、第三方依赖

包名版本用途安装命令
Django6.0.5Web框架pip install django
django-ckeditor富文本编辑器pip install django-ckeditor
django-ckeditor-uploaderCKEditor文件上传pip install django-ckeditor-uploader
django-taggit标签系统pip install django-taggit
djangorestframeworkREST APIpip install djangorestframework
django-cors-headersCORS跨域pip install django-cors-headers
django-allauth用户认证/社交登录pip install django-allauth
Pillow图片处理pip install Pillow

一键安装:

pip install django django-ckeditor django-ckeditor-uploader django-taggit djangorestframework django-cors-headers django-allauth Pillow

十八、开发指南

18.1 开发环境搭建

# 1. 克隆项目
git clone <repository_url>
cd djcms

# 2. 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate   # Windows

# 3. 安装依赖
pip install django django-ckeditor django-ckeditor-uploader django-taggit \
    djangorestframework django-cors-headers django-allauth Pillow

# 4. 初始化数据库
python manage.py migrate

# 5. 加载种子数据
python seed_data.py

# 6. 生成测试文章(可选)
python manage.py generate_articles

# 7. 创建超级管理员
python manage.py createsuperuser

# 8. 启动开发服务器
python manage.py runserver 0.0.0.0:8000

18.2 访问地址

页面URL
前台首页http://localhost:8000/
管理后台http://localhost:8000/cms-admin/dashboard/
Django Adminhttp://localhost:8000/admin/
API根路径http://localhost:8000/api/
站点地图http://localhost:8000/sitemap.xml

18.3 新增功能模块

  1. cms_core/models.py 中定义模型
  2. 运行 python manage.py makemigrationspython manage.py migrate
  3. cms_core/admin.py 中注册Admin
  4. cms_front/views.py 中创建视图
  5. cms_front/urls.py 中添加路由
  6. templates/cms_front/ 中创建模板
  7. 如需API,在 cms_api/serializers.pycms_api/views.py 中添加

18.4 代码规范

  • 模型字段使用中文 verbose_name
  • 模型 Meta 使用中文 verbose_nameverbose_name_plural
  • 视图函数使用 docstring 注释
  • Admin 类使用 format_htmlmark_safe 渲染 HTML
  • 表单使用 Bootstrap form-control 样式
  • CSS 使用变量体系保持一致性
  • 查询使用 select_related / prefetch_related 优化

十九、生产部署

19.1 部署架构

Internet → Nginx (反向代理/静态文件/HTTPS) → Gunicorn (WSGI) → Django ↓ PostgreSQL / MySQL

19.2 Gunicorn配置

pip install gunicorn
gunicorn djcms.wsgi:application --bind 0.0.0.0:8000 --workers 4

19.3 Nginx配置示例

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location /static/ {
        alias /path/to/djcms/staticfiles/;
    }

    location /media/ {
        alias /path/to/djcms/media/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

19.4 生产环境检查清单

项目操作
DEBUG = False关闭调试模式
SECRET_KEY使用环境变量,不硬编码
ALLOWED_HOSTS设置具体域名
DATABASE切换至 PostgreSQL/MySQL
EMAIL_BACKEND切换至 SMTP
CORS_ALLOW_ALL_ORIGINS设置为 False
SECURE_SSL_REDIRECT启用 HTTPS 重定向
SESSION_COOKIE_SECURE启用安全 Cookie
CSRF_COOKIE_SECURE启用安全 CSRF Cookie
collectstatic运行 python manage.py collectstatic
日志确认 logs/ 目录可写

二十、性能优化建议

优化方向建议
数据库查询使用 select_related/prefetch_related 避免 N+1;添加数据库索引
缓存引入 Redis 缓存热点数据(站点设置、导航菜单、分类列表)
URL重定向中间件添加缓存层,避免每次请求查询数据库
静态文件使用 CDN 或 Nginx 直接服务;启用 Gzip/Brotli 压缩
图片使用 WebP 格式;配置延迟加载;使用对象存储
分页大数据量时使用 Paginatorcount 缓存
API添加 API 频率限制(rest_framework.throttling
会话生产环境使用 Redis/数据库存储会话