feat: 代码精读与注释

master
unknown 3 months ago
parent 54cadcf84d
commit 5d2ebbb8ea

@ -2,14 +2,20 @@ from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
# HJJ: 使用装饰器注册CustomUser模型到Django管理后台
@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
# HJJ: 指定管理的模型为CustomUser
model = CustomUser
# HJJ: 定义管理后台列表页显示的字段
list_display = ['username', 'email', 'is_staff', 'date_joined']
# HJJ: 定义管理后台可用的过滤器
list_filter = ['is_staff', 'is_active', 'date_joined']
# HJJ: 在原有字段集基础上添加扩展信息字段集
fieldsets = UserAdmin.fieldsets + (
('扩展信息', {'fields': ('bio', 'avatar', 'location', 'website')}),
)
# HJJ: 在添加用户表单中添加扩展信息字段集
add_fieldsets = UserAdmin.add_fieldsets + (
('扩展信息', {'fields': ('bio', 'avatar', 'location', 'website')}),
)

@ -2,12 +2,18 @@ from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
# HJJ: 自定义用户创建表单继承Django内置的UserCreationForm
class CustomUserCreationForm(UserCreationForm):
class Meta:
# HJJ: 指定表单对应的模型为CustomUser
model = CustomUser
# HJJ: 定义表单中包含的字段:用户名、邮箱、个人简介、头像、位置、网站
fields = ('username', 'email', 'bio', 'avatar', 'location', 'website')
# HJJ: 自定义用户信息修改表单继承Django内置的UserChangeForm
class CustomUserChangeForm(UserChangeForm):
class Meta:
# HJJ: 指定表单对应的模型为CustomUser
model = CustomUser
# HJJ: 定义表单中包含的字段:用户名、邮箱、个人简介、头像、位置、网站
fields = ('username', 'email', 'bio', 'avatar', 'location', 'website')

@ -1,16 +1,30 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
"""自定义用户模型"""
"""自定义用户模型
继承Django内置的AbstractUser类扩展用户基本信息
"""
# MYT个人简介字段最大长度500字符可为空
bio = models.TextField(max_length=500, blank=True, verbose_name="个人简介")
# MYT头像字段图片将上传到avatars/年/月/目录,可为空
avatar = models.ImageField(upload_to='avatars/%Y/%m/', blank=True, null=True, verbose_name="头像")
# MYT所在地字段最大长度100字符可为空
location = models.CharField(max_length=100, blank=True, verbose_name="所在地")
# MYT个人网站字段URL格式可为空
website = models.URLField(blank=True, verbose_name="个人网站")
class Meta:
# MYT 在Django admin中显示的单数名称
verbose_name = "用户"
# MYT在Django admin中显示的复数名称
verbose_name_plural = "用户"
def __str__(self):
"""字符串表示方法,返回用户名用于显示"""
return self.username

@ -2,9 +2,14 @@ from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
# HJJ: 定义用户认证相关的URL路由配置
urlpatterns = [
# HJJ: 用户注册页面路由指向views.register视图函数
path('register/', views.register, name='register'),
# HJJ: 用户登录页面路由指向views.user_login视图函数
path('login/', views.user_login, name='login'),
# HJJ: 用户登出路由使用Django内置的LogoutView视图
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
# HJJ: 用户个人资料页面路由指向views.profile视图函数
path('profile/', views.profile, name='profile'),
]

@ -5,33 +5,58 @@ from django.contrib.auth.decorators import login_required
from .forms import CustomUserCreationForm
def register(request):
"""用户注册视图"""
"""用户注册视图
处理用户注册流程支持GET和POST请求
GET显示空注册表单
POST处理表单提交验证并创建用户
"""
if request.method == 'POST':
# 实例化表单包含POST数据和上传的文件如头像
form = CustomUserCreationForm(request.POST, request.FILES)
if form.is_valid():
# 表单验证通过,保存用户到数据库
user = form.save()
# 注册成功后自动登录用户
login(request, user)
# 重定向到首页
return redirect('index')
else:
# MYTGET请求创建空表单实例
form = CustomUserCreationForm()
# MYT渲染注册模板传递表单对象
return render(request, 'accounts/register.html', {'form': form})
def user_login(request):
"""用户登录视图"""
"""用户登录视图
处理用户登录认证流程
GET显示登录表单
POST验证用户凭证并登录
"""
if request.method == 'POST':
# MYT实例化认证表单包含请求和POST数据
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
# MYT从清洗后的表单数据获取用户名和密码
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
# MYT认证用户凭证
user = authenticate(username=username, password=password)
if user is not None:
# MYT认证成功登录用户
login(request, user)
# MYT重定向到首页
return redirect('index')
else:
# MYTGET请求创建空认证表单
form = AuthenticationForm()
# MYT渲染登录模板传递表单对象
return render(request, 'accounts/login.html', {'form': form})
@login_required
@login_required # MYT装饰器确保只有登录用户才能访问此视图
def profile(request):
"""用户个人资料页面"""
"""用户个人资料页面
显示当前登录用户的个人信息
需要用户登录后才能访问
"""
# MYT直接将请求中的用户对象传递给模板
return render(request, 'accounts/profile.html', {'user': request.user})

@ -1,38 +1,59 @@
from django.contrib import admin
from .models import Category, PrimaryTag, SecondaryTag, Post
# HJH使用装饰器注册Category模型到Django管理后台
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
# HJH定义在管理后台列表页面显示的字段
list_display = ['name', 'icon', 'order', 'description']
# HJH定义可以在列表页面直接编辑的字段无需进入编辑页面
list_editable = ['order', 'icon']
# HJH定义可以通过搜索框搜索的字段
search_fields = ['name']
# HJH使用装饰器注册PrimaryTag模型到Django管理后台
@admin.register(PrimaryTag)
class PrimaryTagAdmin(admin.ModelAdmin):
# HJH定义在管理后台列表页面显示的字段
list_display = ['name', 'color']
# HJH定义可以通过搜索框搜索的字段
search_fields = ['name']
# HJH使用装饰器注册SecondaryTag模型到Django管理后台
@admin.register(SecondaryTag)
class SecondaryTagAdmin(admin.ModelAdmin):
# HJH定义在管理后台列表页面显示的字段
list_display = ['name', 'tag_type', 'parent_tag']
# HJH定义在列表页面右侧的过滤器可以按这些字段快速筛选
list_filter = ['tag_type', 'parent_tag']
# HJH定义可以通过搜索框搜索的字段
search_fields = ['name']
# HJH使用装饰器注册Post模型到Django管理后台
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
# HJH定义在管理后台列表页面显示的字段
list_display = ['title', 'category', 'author', 'status', 'views', 'created_time']
# HJH定义在列表页面右侧的过滤器可以按这些字段快速筛选
list_filter = ['category', 'status', 'created_time']
# HJH定义可以通过搜索框搜索的字段支持标题和内容搜索
search_fields = ['title', 'content']
# HJH使用水平选择器显示多对多字段方便用户选择多个标签
filter_horizontal = ['primary_tags', 'secondary_tags']
# HJH定义在编辑页面中只读的字段用户不能修改这些字段
readonly_fields = ['views', 'created_time', 'updated_time']
# HJH定义字段分组显示使编辑页面更加清晰有序
fieldsets = (
# HJH基本信息分组包含文章的核心信息
('基本信息', {
'fields': ('title', 'content', 'excerpt', 'author', 'category', 'featured_image')
}),
# HJH标签管理分组包含一级和二级标签选择
('标签管理', {
'fields': ('primary_tags', 'secondary_tags')
}),
# HJH状态信息分组包含文章状态和统计信息
('状态信息', {
'fields': ('status', 'views', 'created_time', 'updated_time')
}),
)
)

@ -2,5 +2,7 @@ from django.apps import AppConfig
class BlogConfig(AppConfig):
# HJH设置默认主键字段类型为BigAutoField支持更大的自增ID
default_auto_field = 'django.db.models.BigAutoField'
name = 'blog'
# HJH定义应用的名称需要与settings.py中INSTALLED_APPS的配置一致
name = 'blog'

@ -3,25 +3,27 @@ from django.shortcuts import render
from .models import Post, Category
def index(request):
# 获取所有文章或根据需求筛选
# LXC: 获取所有文章按创建时间倒序排列取前10篇
post_list = Post.objects.all().order_by('-created_time')[:10]
# 分类映射信息
# LXC: 定义分类信息字典
category_info = {
'name': '非遗传承',
'label': '',
'desc': '探索南京非物质文化遗产的独特魅力与传承故事'
}
# LXC: 构建上下文数据
context = {
'post_list': post_list,
'category_info': category_info,
'current_path': request.path
}
# LXC: 渲染首页模板并返回响应
return render(request, 'blog/index.html', context)
def category_view(request, category_id):
# 分类映射信息
# LXC: 定义分类映射字典,包含每个分类的详细信息
category_map = {
1: {'name': '传统工艺', 'label': '巧夺天工·工艺', 'desc': '探索南京传统手工艺的精湛技艺与匠心传承'},
2: {'name': '表演艺术', 'label': '梨园雅韵·表演', 'desc': '感受南京传统表演艺术的独特韵味与舞台魅力'},
@ -30,15 +32,18 @@ def category_view(request, category_id):
5: {'name': '传承人物', 'label': '匠心传承·人物', 'desc': '认识南京非物质文化遗产的传承人与守护者'},
}
# LXC: 根据分类ID获取对应的分类信息如果不存在则使用默认值
category_info = category_map.get(category_id, {'name': '非遗传承', 'label': '', 'desc': '探索南京非物质文化遗产的独特魅力'})
# 根据分类ID获取对应的文章
# LXC: 根据分类ID获取对应的文章,按创建时间倒序排列
posts = Post.objects.filter(category_id=category_id).order_by('-created_time')
# LXC: 构建上下文数据
context = {
'post_list': posts,
'category_info': category_info,
'current_path': request.path,
'current_category_id': category_id
}
# LXC: 渲染分类页面模板并返回响应
return render(request, 'blog/index.html', context)

@ -3,33 +3,47 @@ from django.conf import settings # 新增导入
class Category(models.Model):
"""非遗分类"""
# HJH分类名称字段最大长度100字符必须唯一用于后台显示名称
name = models.CharField(max_length=100, unique=True, verbose_name="分类名称")
# HJH分类描述字段Text类型允许长文本blank=True表示可为空
description = models.TextField(blank=True, verbose_name="分类描述")
# HJH分类图标字段存储图标字符或表情默认使用🏮表情
icon = models.CharField(max_length=50, default="🏮", verbose_name="分类图标")
# HJH显示顺序字段整数类型用于控制分类在前端的显示顺序
order = models.IntegerField(default=0, verbose_name="显示顺序")
class Meta:
# HJH设置模型在Django管理后台的单数显示名称
verbose_name = "非遗分类"
# HJH设置模型在Django管理后台的复数显示名称
verbose_name_plural = "非遗分类"
# HJH默认按order字段升序排列数字小的排在前面
ordering = ['order']
def __str__(self):
# HJH定义对象的字符串表示形式在管理后台和shell中显示分类名称
return self.name
class PrimaryTag(models.Model):
"""一级标签"""
# HJH一级标签名称字段最大长度100字符必须唯一
name = models.CharField(max_length=100, unique=True, verbose_name="标签名称")
# HJH标签颜色字段存储十六进制颜色值默认值为棕色#8b4513
color = models.CharField(max_length=7, default="#8b4513", verbose_name="标签颜色")
class Meta:
# HJH设置模型在Django管理后台的单数显示名称
verbose_name = "一级标签"
# HJH设置模型在Django管理后台的复数显示名称
verbose_name_plural = "一级标签"
def __str__(self):
# HJH定义对象的字符串表示形式返回标签名称
return self.name
class SecondaryTag(models.Model):
"""二级标签"""
# HJH定义二级标签的类型选择项每个元组包含(数据库值, 显示名称)
TAG_TYPE_CHOICES = [
('geo', '地理空间'),
('theme', '主题维度'),
@ -38,41 +52,64 @@ class SecondaryTag(models.Model):
('time', '时间节庆'),
]
# HJH二级标签名称字段最大长度100字符
name = models.CharField(max_length=100, verbose_name="标签名称")
# HJH标签类型字段使用预定义的选择项max_length=20足够存储所有选项
tag_type = models.CharField(max_length=20, choices=TAG_TYPE_CHOICES, verbose_name="标签类型")
# HJH外键关联到一级标签on_delete=CASCADE表示父标签删除时子标签也删除
parent_tag = models.ForeignKey(PrimaryTag, on_delete=models.CASCADE, null=True, blank=True, verbose_name="父级标签")
class Meta:
# HJH设置模型在Django管理后台的单数显示名称
verbose_name = "二级标签"
# HJH设置模型在Django管理后台的复数显示名称
verbose_name_plural = "二级标签"
def __str__(self):
# HJH定义对象的字符串表示形式返回标签名称和类型显示名称
return f"{self.name} ({self.get_tag_type_display()})"
class Post(models.Model):
"""非遗文章"""
# HJH定义文章状态的选择项用于status字段
STATUS_CHOICES = [
('draft', '草稿'),
('published', '已发布'),
]
# HJH文章标题字段最大长度200字符
title = models.CharField(max_length=200, verbose_name="文章标题")
# HJH文章内容字段Text类型支持长文本内容
content = models.TextField(verbose_name="文章内容")
# HJH文章摘要字段最大长度300字符blank=True表示可为空
excerpt = models.TextField(max_length=300, blank=True, verbose_name="文章摘要")
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="作者") # 修改这行
# HJH外键关联到用户模型使用Django设置中的AUTH_USER_MODEL确保兼容性
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="作者")
# HJH外键关联到分类模型文章必须属于一个分类
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="分类")
# HJH多对多关联到一级标签blank=True表示可以为空标签集合
primary_tags = models.ManyToManyField(PrimaryTag, blank=True, verbose_name="一级标签")
# HJH多对多关联到二级标签blank=True表示可以为空标签集合
secondary_tags = models.ManyToManyField(SecondaryTag, blank=True, verbose_name="二级标签")
# HJH特色图片字段图片上传到posts/年/月/目录,可为空
featured_image = models.ImageField(upload_to='posts/%Y/%m/', blank=True, null=True, verbose_name="特色图片")
# HJH阅读量字段PositiveIntegerField确保非负整数默认值为0
views = models.PositiveIntegerField(default=0, verbose_name="阅读量")
# HJH文章状态字段使用选择项默认状态为'published'已发布
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='published', verbose_name="状态")
# HJH创建时间字段auto_now_add=True在对象创建时自动设置当前时间
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
# HJH更新时间字段auto_now=True在对象保存时自动更新为当前时间
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
# HJH设置模型在Django管理后台的单数显示名称
verbose_name = "非遗文章"
# HJH设置模型在Django管理后台的复数显示名称
verbose_name_plural = "非遗文章"
# HJH默认按创建时间降序排列最新的文章显示在最前面
ordering = ['-created_time']
def __str__(self):
return self.title
# HJH定义对象的字符串表示形式返回文章标题
return self.title

@ -1,9 +1,12 @@
# blog/urls.py
from django.urls import path
from . import views
# LXC: 定义URL模式列表
urlpatterns = [
# LXC: 首页URL对应index视图函数
path('', views.index, name='index'),
# LXC: 分类页面URL包含分类ID参数对应category_view视图函数
path('category/<int:category_id>/', views.category_view, name='category'),
# LXC: 文章详情页URL包含文章ID参数对应detail视图函数
path('post/<int:post_id>/', views.detail, name='detail'),
]

@ -4,9 +4,12 @@ from .models import Post, Category
def index(request):
"""首页视图"""
# LXC: 获取已发布的文章按创建时间倒序排列取前10篇
post_list = Post.objects.filter(status='published').order_by('-created_time')[:10]
# LXC: 获取已发布的文章按浏览量倒序排列取前5篇作为热门文章
hot_posts = Post.objects.filter(status='published').order_by('-views')[:5]
# LXC: 构建上下文数据,包含文章列表、热门文章、分类信息等
context = {
'post_list': post_list,
'hot_posts': hot_posts,
@ -18,13 +21,17 @@ def index(request):
'current_path': request.path,
'current_category_id': None
}
# LXC: 渲染首页模板并返回响应
return render(request, 'blog/index.html', context)
def category_view(request, category_id):
"""分类页面视图"""
# LXC: 根据分类ID获取分类对象如果不存在则返回404
category = get_object_or_404(Category, id=category_id)
# LXC: 获取热门文章列表
hot_posts = Post.objects.filter(status='published').order_by('-views')[:5]
# LXC: 定义分类映射字典,包含每个分类的标签和描述
category_map = {
1: {'label': '巧夺天工·工艺', 'desc': '探索南京传统手工艺的精湛技艺与匠心传承'},
2: {'label': '梨园雅韵·表演', 'desc': '感受南京传统表演艺术的独特韵味与舞台魅力'},
@ -33,13 +40,16 @@ def category_view(request, category_id):
5: {'label': '匠心传承·人物', 'desc': '认识南京非物质文化遗产的传承人与守护者'},
}
# LXC: 根据分类ID获取对应的分类信息如果不存在则使用默认值
category_info = category_map.get(category_id, {
'label': category.name,
'desc': f'探索南京{category.name}的独特魅力'
})
# LXC: 获取该分类下已发布的文章,按创建时间倒序排列
posts = Post.objects.filter(category=category, status='published').order_by('-created_time')
# LXC: 构建上下文数据
context = {
'post_list': posts,
'hot_posts': hot_posts,
@ -52,24 +62,31 @@ def category_view(request, category_id):
'current_category_id': category_id
}
# LXC: 渲染分类页面模板并返回响应
return render(request, 'blog/index.html', context)
def detail(request, post_id):
"""文章详情页"""
# LXC: 根据文章ID获取已发布的文章对象如果不存在则返回404
post = get_object_or_404(Post, id=post_id, status='published')
# LXC: 增加文章浏览量并保存到数据库
post.views += 1
post.save()
# 使用新的 related_name 查询
# LXC: 导入评论模型和表单
from comments.models import Comment
from comments.forms import CommentForm
comments = post.comments.all().order_by('-created_time') # 使用 comments 而不是 comment_set
# LXC: 获取该文章的所有评论,按创建时间倒序排列
comments = post.comments.all().order_by('-created_time')
# LXC: 创建评论表单实例
comment_form = CommentForm()
# LXC: 构建上下文数据
context = {
'post': post,
'comments': comments,
'comment_form': comment_form,
}
# LXC: 渲染文章详情页模板并返回响应
return render(request, 'blog/detail.html', context)

@ -1,10 +1,30 @@
# comments/admin.py
#zy:comments/admin.py
#zy: 评论(Comment)模型的管理后台配置文件
#zy: 该文件用于配置Django管理后台中评论模型的显示和行为
#zy: 导入Django管理后台模块用于注册模型和管理配置
from django.contrib import admin
#zy: 从当前包(models模块)导入Comment模型
from .models import Comment
#zy: 使用装饰器注册Comment模型到管理后台并指定使用CommentAdmin作为管理类
@admin.register(Comment)
#zy: 定义评论模型的管理配置类继承自admin.ModelAdmin
class CommentAdmin(admin.ModelAdmin):
#zy: 设置在管理后台列表页面显示的字段列
# 将显示:作者(author)、关联文章(post)、创建时间(created_time)
list_display = ['author', 'post', 'created_time']
# zy:设置右侧过滤侧边栏的过滤条件
#zy: 允许用户按照创建时间(created_time)对评论进行筛选
list_filter = ['created_time']
# zy:设置搜索字段,在列表页顶部显示搜索框
# zy:支持按作者用户名(author__username)和评论内容(content)进行搜索
# author__username表示通过外键关系搜索作者的用户名字段
search_fields = ['author__username', 'content']
# zy:设置只读字段,在编辑页面中这些字段将显示为不可编辑状态
# zy:创建时间(created_time)通常应该设为只读,防止用户修改
readonly_fields = ['created_time']

@ -1,18 +1,31 @@
# comments/forms.py
# zy:comments/forms.py
#zy:评论(Comment)模型的表单定义文件
#zy: 该文件定义了用于创建和编辑评论的表单类
#zy: 导入Django表单模块提供表单相关的基类和功能
from django import forms
#zy: 从当前包(models模块)导入Comment模型
from .models import Comment
#zy: 定义评论表单类继承自ModelForm自动根据模型字段生成表单
class CommentForm(forms.ModelForm):
# zy:定义表单的元数据类,用于配置表单与模型的关联和行为
class Meta:
# zy:指定表单关联的模型为Comment
model = Comment
#zy: 指定表单中包含的字段,这里只包含评论内容字段
fields = ['content']
#zy: 配置字段的小部件(Widget)属性控制表单字段的HTML渲染
widgets = {
# 为content字段配置Textarea文本域小部件
'content': forms.Textarea(attrs={
'rows': 4,
'placeholder': '请输入您的评论...',
'class': 'comment-textarea'
'rows': 4, # 设置文本域行数为4行
'placeholder': '请输入您的评论...', # 设置占位符文本
'class': 'comment-textarea' # 设置CSS类名用于样式控制
})
}
# zy:配置字段的标签显示
labels = {
'content': ''
'content': '' # zy:将content字段的标签设为空不显示标签文本
}

@ -1,27 +1,39 @@
# comments/models.py
# DZQ: 评论模块的数据模型定义文件
from django.db import models
from django.conf import settings
# DZQ: 评论数据模型类 - 用于存储博客文章的评论信息
class Comment(models.Model):
# DZQ: 外键关联到博客文章CASCADE表示文章删除时评论也删除
post = models.ForeignKey(
'blog.Post',
on_delete=models.CASCADE,
verbose_name="所属文章",
related_name="comments" # 添加 related_name
related_name="comments" #DZQ: 通过related_name可从文章对象反向查询评论
)
# DZQ: 外键关联到用户模型CASCADE表示用户删除时评论也删除
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
verbose_name="评论者",
related_name="comments" # 添加 related_name
related_name="comments" # DZQ: 通过related_name可从用户对象反向查询评论
)
# DZQ: 评论内容字段使用TextField支持长文本
content = models.TextField(verbose_name="评论内容")
# DZQ: 评论时间字段auto_now_add=True表示创建时自动设置当前时间
created_time = models.DateTimeField(auto_now_add=True, verbose_name="评论时间")
# DZQ: 模型元数据配置类
class Meta:
verbose_name = "评论"
verbose_name_plural = "评论"
ordering = ['-created_time']
verbose_name = "评论" # DZQ: 单数形式的显示名称
verbose_name_plural = "评论" # DZQ: 复数形式的显示名称
ordering = ['-created_time'] #DZQ: 默认按评论时间倒序排列
#DZQ: 对象字符串表示方法用于在admin等界面显示
def __str__(self):
return f'{self.author.username} 评论了《{self.post.title}'

@ -1,9 +1,32 @@
# comments/urls.py
# zy:comments/urls.py
# zy:评论(Comment)应用的URL路由配置文件
# zy:该文件定义了评论功能的所有URL模式和对应的视图处理
#zy: 导入Django的URL路由配置模块path函数用于定义URL模式
from django.urls import path
#zy: 从当前包(views模块)导入所有视图函数或类
from . import views
# zy:定义应用的命名空间用于URL反向解析时区分不同应用的相同名称URL
#zy: 当在模板中使用{% url 'comments:add_comment' post_id=1 %}时会指向此应用的add_comment URL
app_name = 'comments'
# zy:定义URL模式列表Django会按顺序匹配这些模式
# zy:每个path()函数定义一个URL模式及其对应的视图
urlpatterns = [
path('post/<int:post_id>/comment/', views.add_comment, name='add_comment'),
# zy:定义添加评论的URL模式
path(
# URL路径模式使用尖括号定义路径参数
# - 'post/' 固定文本部分
# - '<int:post_id>' 路径转换器匹配整数并将其作为post_id参数传递给视图
# - '/comment/' 固定文本部分
'post/<int:post_id>/comment/',
# zy:对应的视图函数,处理添加评论的请求
views.add_comment,
# zy:URL模式的名称用于在模板和代码中进行反向解析
name='add_comment'
),
]

@ -1,4 +1,6 @@
# comments/views.py
# DZQ: 评论模块的视图函数文件处理评论相关的HTTP请求
from django.shortcuts import get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
@ -6,20 +8,31 @@ from .models import Comment
from .forms import CommentForm
from blog.models import Post
@login_required
# DZQ: 添加评论视图函数 - 处理用户提交评论的请求
@login_required # DZQ: 登录要求装饰器,确保只有登录用户才能发表评论
def add_comment(request, post_id):
"""添加评论"""
# DZQ: 获取指定ID且状态为已发布的文章如果不存在则返回404错误
post = get_object_or_404(Post, id=post_id, status='published')
# DZQ: 只处理POST请求GET请求直接重定向
if request.method == 'POST':
# DZQ: 实例化评论表单传入POST数据
form = CommentForm(request.POST)
# DZQ: 表单验证通过的处理逻辑
if form.is_valid():
# DZQ: commit=False表示先不保存到数据库允许设置额外字段
comment = form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
comment.post = post # DZQ: 设置评论关联的文章
comment.author = request.user # DZQ: 设置评论作者为当前登录用户
comment.save() # DZQ: 将评论保存到数据库
# DZQ: 添加成功消息,将在下次请求时显示给用户
messages.success(request, '评论发表成功!')
else:
# DZQ: 表单验证失败,添加错误消息
messages.error(request, '评论发表失败,请检查输入内容。')
# DZQ: 重定向回文章详情页面,无论评论成功与否都返回文章页面
return redirect('blog:detail', post_id=post_id)

@ -1,19 +1,4 @@
"""
URL configuration for mysite project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
# mysite/urls.py
from django.contrib import admin
from django.urls import include, path
@ -22,4 +7,4 @@ urlpatterns = [
path('', include('blog.urls')),
path('accounts/', include('accounts.urls')),
path('comments/', include('comments.urls', namespace='comments')),
]
]

@ -537,13 +537,12 @@
<div class="sidebar-widget">
<h3 class="widget-title">非遗项目</h3>
<div class="tag-cloud">
<!-- 这里链接到具体的分类页面,假设这些项目都在工艺分类下 -->
<a href="{% url 'category' 1 %}" class="cloud-tag">南京绒花</a>
<a href="{% url 'category' 2 %}" class="cloud-tag">南京白局</a>
<a href="{% url 'category' 2 %}" class="cloud-tag">南京评话</a>
<a href="{% url 'category' 3 %}" class="cloud-tag">秦淮灯会</a>
<a href="{% url 'category' 3 %}" class="cloud-tag">南京板鸭</a>
<a href="{% url 'category' 4 %}" class="cloud-tag">南京白话</a>
<a href="{% url 'category' 1 %}" class="cloud-tag">南京云锦</a>
<a href="{% url 'category' 1 %}" class="cloud-tag">南京白局</a>
<a href="{% url 'category' 1 %}" class="cloud-tag">南京评话</a>
<a href="{% url 'category' 1 %}" class="cloud-tag">秦淮灯会</a>
<a href="{% url 'category' 1 %}" class="cloud-tag">南京板鸭</a>
<a href="{% url 'category' 1 %}" class="cloud-tag">南京白话</a>
</div>
</div>
</aside>

@ -9,7 +9,7 @@
<div class="post-meta">
<span><i>👁️</i> {{ post.views }} 次阅读</span>
<span><i>💬</i> {{ post.comment_set.count }} 条评论</span>
<span><i>💬</i> {{ post.comments.count }} 条评论</span>
<span><i>📅</i> {{ post.created_time|date:"Y-m-d" }}</span>
<!-- 显示文章所属分类 -->
<span><i>🏷️</i> {{ post.category.name }}</span>

Loading…
Cancel
Save