如何在Django中实现类似WordPress的动态页面创建功能
Django 实现类WordPress动态自定义页面方案
核心思路
通过「页面基础信息表+自定义内容区块表」的组合结构,解耦内容配置与模板代码,用单个视图匹配不同页面slug实现多独立页面的动态渲染,支持后台完全自定义页面内容,可直接复用现有博客的侧边栏组件、媒体资源管理等已有能力。
1. 模型设计
首先定义两类数据表,分别存储页面基础属性和页面内的所有内容区块:
from django.db import models from django.utils.text import slugify class Page(models.Model): BLOCK_TYPE_CHOICES = [ ('text', '富文本'), ('image', '单图'), ('video', '视频'), ('slider', '轮播图'), ('widget', '侧边栏组件'), ] title = models.CharField(max_length=200, verbose_name="页面标题") slug = models.SlugField(max_length=200, unique=True, verbose_name="页面标识") is_published = models.BooleanField(default=True, verbose_name="是否发布") template_name = models.CharField(max_length=100, default="custom_page.html", verbose_name="页面模板") create_time = models.DateTimeField(auto_now_add=True) update_time = models.DateTimeField(auto_now=True) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) super().save(*args, **kwargs) def __str__(self): return self.title class ContentBlock(models.Model): page = models.ForeignKey(Page, on_delete=models.CASCADE, related_name="blocks", verbose_name="所属页面") block_type = models.CharField(max_length=20, choices=Page.BLOCK_TYPE_CHOICES, verbose_name="区块类型") content = models.TextField(verbose_name="区块内容", help_text="根据类型填写:富文本/图片链接/视频链接/JSON格式的轮播图列表/组件名") sort_order = models.IntegerField(default=0, verbose_name="排序优先级(数字越小越靠前)") is_visible = models.BooleanField(default=True, verbose_name="是否显示") class Meta: ordering = ['sort_order']
2. 后台管理配置
将内容区块设置为页面的内联管理项,编辑页面时可直接增减、排序区块,无需跳转其他页面:
from django.contrib import admin from .models import Page, ContentBlock class ContentBlockInline(admin.TabularInline): model = ContentBlock extra = 1 fields = ['block_type', 'content', 'sort_order', 'is_visible'] @admin.register(Page) class PageAdmin(admin.ModelAdmin): list_display = ['title', 'slug', 'is_published', 'update_time'] prepopulated_fields = {"slug": ("title",)} inlines = [ContentBlockInline]
如果需要拖拽排序功能,可直接使用Django生态的可排序内联插件,无需修改核心逻辑。
3. 单视图实现多页面路由
写统一的页面处理视图,根据slug匹配对应页面,无需为每个独立页面单独编写视图:
from django.shortcuts import render, get_object_or_404 from .models import Page def custom_page_view(request, page_slug): page = get_object_or_404(Page, slug=page_slug, is_published=True) blocks = page.blocks.filter(is_visible=True) context = { 'page': page, 'blocks': blocks } return render(request, page.template_name, context)
在urls.py中添加路由配置即可覆盖所有自定义页面:
urlpatterns = [ # 原有博客路由保持不变 path('page/<slug:page_slug>/', custom_page_view, name='custom_page'), ]
4. 模板动态渲染
在通用模板custom_page.html中根据区块类型动态渲染对应内容,完全无需硬编码页面结构,新增区块类型只需扩展模板判断逻辑即可:
<!DOCTYPE html> <html> <head> <title>{{ page.title }}</title> </head> <body> <h1>{{ page.title }}</h1> {% for block in blocks %} {% if block.block_type == 'text' %} <div class="text-block"> {{ block.content|safe }} </div> {% elif block.block_type == 'image' %} <div class="image-block"> <img src="{{ block.content }}" alt="页面图片"> </div> {% elif block.block_type == 'video' %} <div class="video-block"> <video src="{{ block.content }}" controls></video> </div> {% elif block.block_type == 'slider' %} <div class="slider-block"> {% with slider_items=block.content|json_script:"slider-data" %} <!-- 可直接复用现有博客的轮播组件逻辑 --> {% endwith %} </div> {% elif block.block_type == 'widget' %} <div class="widget-block"> {% include f"widgets/{block.content}.html" %} </div> {% endif %} {% endfor %} </body> </html>
可扩展功能
- 支持为不同页面配置独立的SEO字段(meta标题、描述、关键词),直接添加在Page模型字段中即可
- 可增加页面权限控制,指定特定登录用户才能访问
- 对发布后的页面做缓存,降低服务器负载
- 支持页面草稿、版本回溯功能,只需扩展Page模型状态字段即可
内容的提问来源于stack exchange,提问作者B Syal
相关产品推荐
相关产品推荐

