diff --git a/src%2FDjangoBlog-master/accounts代码注释 b/src%2FDjangoBlog-master/accounts代码注释
new file mode 100644
index 0000000..66dc905
--- /dev/null
+++ b/src%2FDjangoBlog-master/accounts代码注释
@@ -0,0 +1 @@
+undefined
\ No newline at end of file
diff --git a/src%2FDjangoBlog-master/代码注释 b/src%2FDjangoBlog-master/代码注释
new file mode 100644
index 0000000..e69de29
diff --git a/src/DjangoBlog-master/accounts/admin.py b/src/DjangoBlog-master/accounts/admin.py
index 32e483c..13b3f92 100644
--- a/src/DjangoBlog-master/accounts/admin.py
+++ b/src/DjangoBlog-master/accounts/admin.py
@@ -9,14 +9,22 @@ from .models import BlogUser
class BlogUserCreationForm(forms.ModelForm):
+ """
+ 自定义用户创建表单,用于Django管理后台
+ 扩展了默认的用户创建功能,添加密码确认逻辑
+ """
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
model = BlogUser
- fields = ('email',)
+ fields = ('email',) #ZXY: 创建用户时只需要邮箱字段
def clean_password2(self):
+ """
+ 验证两次输入的密码是否一致
+ 这是Django表单的标准验证方法命名
+ """
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
@@ -25,19 +33,25 @@ class BlogUserCreationForm(forms.ModelForm):
return password2
def save(self, commit=True):
- # Save the provided password in hashed format
+ """
+ 保存用户实例,处理密码哈希化和来源标记
+ """
user = super().save(commit=False)
- user.set_password(self.cleaned_data["password1"])
+ user.set_password(self.cleaned_data["password1"]) #ZXY: 使用Django的密码哈希
if commit:
- user.source = 'adminsite'
+ user.source = 'adminsite' #ZXY: 标记用户来源为管理后台
user.save()
return user
class BlogUserChangeForm(UserChangeForm):
+ """
+ 自定义用户修改表单
+ 继承Django默认的用户修改表单,可以自定义字段显示
+ """
class Meta:
model = BlogUser
- fields = '__all__'
+ fields = '__all__' #ZXY: 包含所有字段
field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
@@ -45,8 +59,14 @@ class BlogUserChangeForm(UserChangeForm):
class BlogUserAdmin(UserAdmin):
- form = BlogUserChangeForm
- add_form = BlogUserCreationForm
+ """
+ 自定义用户管理类
+ 配置Django管理后台中用户的显示和编辑方式
+ """
+ form = BlogUserChangeForm #ZXY: 修改用户时使用的表单
+ add_form = BlogUserCreationForm #ZXY: 创建用户时使用的表单
+
+ #ZXY: 列表页显示的字段
list_display = (
'id',
'nickname',
@@ -54,6 +74,6 @@ class BlogUserAdmin(UserAdmin):
'email',
'last_login',
'date_joined',
- 'source')
- list_display_links = ('id', 'username')
- ordering = ('-id',)
+ 'source') #ZXY: 显示用户来源
+ list_display_links = ('id', 'username') #ZXY: 可点击跳转到编辑页的字段
+ ordering = ('-id',) #ZXY: 按ID倒序排列
diff --git a/src/DjangoBlog-master/accounts/apps.py b/src/DjangoBlog-master/accounts/apps.py
index 9b3fc5a..c60fbcd 100644
--- a/src/DjangoBlog-master/accounts/apps.py
+++ b/src/DjangoBlog-master/accounts/apps.py
@@ -2,4 +2,8 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
- name = 'accounts'
+ """
+ 账户应用的Django配置类
+ 用于配置应用的元数据和启动时的初始化行为
+ """
+ name = 'accounts' #ZXY: 应用的Python路径
diff --git a/src/DjangoBlog-master/accounts/forms.py b/src/DjangoBlog-master/accounts/forms.py
index fce4137..0ca5373 100644
--- a/src/DjangoBlog-master/accounts/forms.py
+++ b/src/DjangoBlog-master/accounts/forms.py
@@ -9,8 +9,13 @@ from .models import BlogUser
class LoginForm(AuthenticationForm):
+ """
+ 用户登录表单
+ 继承Django的AuthenticationForm,添加Bootstrap样式支持
+ """
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
+ #ZXY: 自定义表单字段的widget,添加placeholder和CSS类
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
self.fields['password'].widget = widgets.PasswordInput(
@@ -18,9 +23,14 @@ class LoginForm(AuthenticationForm):
class RegisterForm(UserCreationForm):
+ """
+ 用户注册表单
+ 扩展Django的UserCreationForm,添加邮箱字段和样式支持
+ """
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
+ #ZXY: 为所有字段添加Bootstrap样式和占位符文本
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
self.fields['email'].widget = widgets.EmailInput(
@@ -31,17 +41,26 @@ class RegisterForm(UserCreationForm):
attrs={'placeholder': "repeat password", "class": "form-control"})
def clean_email(self):
+ """
+ 邮箱验证方法
+ 确保注册的邮箱地址在系统中是唯一的
+ """
email = self.cleaned_data['email']
if get_user_model().objects.filter(email=email).exists():
raise ValidationError(_("email already exists"))
return email
class Meta:
- model = get_user_model()
- fields = ("username", "email")
+ model = get_user_model() #ZXY: 使用项目中自定义的用户模型
+ fields = ("username", "email") #ZXY: 表单包含的字段
class ForgetPasswordForm(forms.Form):
+ """
+ 忘记密码重置表单
+ 包含新密码设置、邮箱验证和验证码确认
+ """
+ #ZXY: 新密码字段 - 使用Django的密码输入组件
new_password1 = forms.CharField(
label=_("New password"),
widget=forms.PasswordInput(
@@ -52,6 +71,7 @@ class ForgetPasswordForm(forms.Form):
),
)
+ #ZXY: 确认密码字段 - 标签没有国际化,需要改进
new_password2 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(
@@ -62,6 +82,7 @@ class ForgetPasswordForm(forms.Form):
),
)
+ #ZXY: 邮箱字段 - 用于识别用户和发送验证码
email = forms.EmailField(
label='邮箱',
widget=forms.TextInput(
@@ -72,6 +93,7 @@ class ForgetPasswordForm(forms.Form):
),
)
+ #ZXY: 验证码字段 - 用于安全验证
code = forms.CharField(
label=_('Code'),
widget=forms.TextInput(
@@ -83,15 +105,22 @@ class ForgetPasswordForm(forms.Form):
)
def clean_new_password2(self):
+ """
+ 验证两次输入的新密码是否一致
+ 并使用Django的密码验证器检查密码强度
+ """
password1 = self.data.get("new_password1")
password2 = self.data.get("new_password2")
if password1 and password2 and password1 != password2:
raise ValidationError(_("passwords do not match"))
- password_validation.validate_password(password2)
+ password_validation.validate_password(password2) #ZXY: Django密码强度验证
return password2
def clean_email(self):
+ """
+ 验证邮箱是否存在系统中
+ """
user_email = self.cleaned_data.get("email")
if not BlogUser.objects.filter(
email=user_email
@@ -101,6 +130,10 @@ class ForgetPasswordForm(forms.Form):
return user_email
def clean_code(self):
+ """
+ 验证邮箱验证码是否正确
+ 调用utils模块的验证功能
+ """
code = self.cleaned_data.get("code")
error = utils.verify(
email=self.cleaned_data.get("email"),
@@ -112,6 +145,10 @@ class ForgetPasswordForm(forms.Form):
class ForgetPasswordCodeForm(forms.Form):
+ """
+ 获取忘记密码验证码的表单
+ 简单的邮箱输入表单,用于请求发送验证码邮件
+ """
email = forms.EmailField(
label=_('Email'),
)
diff --git a/src/DjangoBlog-master/accounts/migrations/0001_initial.py b/src/DjangoBlog-master/accounts/migrations/0001_initial.py
index d2fbcab..b6bbe8e 100644
--- a/src/DjangoBlog-master/accounts/migrations/0001_initial.py
+++ b/src/DjangoBlog-master/accounts/migrations/0001_initial.py
@@ -7,18 +7,26 @@ import django.utils.timezone
class Migration(migrations.Migration):
+ """
+ 初始迁移类 - 创建BlogUser用户模型的数据库表结构
+ 迁移是Django用于管理数据库模式变更的机制
+ """
- initial = True
+ initial = True #ZXY: 标记这是应用的初始迁移
dependencies = [
- ('auth', '0012_alter_user_first_name_max_length'),
+ ('auth', '0012_alter_user_first_name_max_length'), #ZXY: 依赖Django认证系统的迁移
]
operations = [
+ #ZXY: 创建BlogUser表的操作
migrations.CreateModel(
name='BlogUser',
fields=[
+ #ZXY: ID主键字段 - Django自动创建的自增BigInteger主键
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+
+ #ZXY: Django内置用户模型的标准字段 ↓
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
@@ -29,20 +37,25 @@ class Migration(migrations.Migration):
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+
+ #ZXY: 自定义扩展字段 ↓
('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
+ ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), #ZXY: 记录创建时间
+ ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), #ZXY: 最后修改时间
+ ('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')), #ZXY: 用户注册来源
+
+ #ZXY: Django权限系统的多对多关系字段
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
- 'verbose_name': '用户',
- 'verbose_name_plural': '用户',
- 'ordering': ['-id'],
- 'get_latest_by': 'id',
+ 'verbose_name': '用户', #ZXY: 单数显示名称
+ 'verbose_name_plural': '用户', #ZXY: 复数显示名称
+ 'ordering': ['-id'], #ZXY: 默认按ID倒序排列
+ 'get_latest_by': 'id', #ZXY: 获取最新记录的依据字段
},
managers=[
+ #ZXY: 指定模型管理器,使用Django默认的用户管理器
('objects', django.contrib.auth.models.UserManager()),
],
),
diff --git a/src/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py b/src/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
index 1a9f509..4c4cc76 100644
--- a/src/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
+++ b/src/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
@@ -1,43 +1,65 @@
# Generated by Django 4.2.5 on 2023-09-06 13:13
+#ZXY: 第二次迁移 - 主要进行字段名称国际化和结构调整
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
+ """
+ 第二次迁移 - 修复字段命名和国际化问题
+ 这次迁移展示了数据库模式演变的常见模式
+ """
dependencies = [
- ('accounts', '0001_initial'),
+ ('accounts', '0001_initial'), #ZXY: 依赖第一次迁移
]
operations = [
+ #ZXY: 操作1: 修改模型选项 - 将中文显示名改为英文
migrations.AlterModelOptions(
name='bloguser',
- options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'},
+ options={
+ 'get_latest_by': 'id',
+ 'ordering': ['-id'],
+ 'verbose_name': 'user', #ZXY: 改为英文单数
+ 'verbose_name_plural': 'user'}, #ZXY: 改为英文复数
),
+
+ #ZXY: 操作2: 删除旧的创建时间字段
migrations.RemoveField(
model_name='bloguser',
- name='created_time',
+ name='created_time', #ZXY: 移除旧的字段命名
),
+
+ #ZXY: 操作3: 删除旧的修改时间字段
migrations.RemoveField(
model_name='bloguser',
- name='last_mod_time',
+ name='last_mod_time', #ZXY: 移除旧的字段命名
),
+
+ #ZXY: 操作4: 添加新的创建时间字段(国际化命名)
migrations.AddField(
model_name='bloguser',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+
+ #ZXY: 操作5: 添加新的修改时间字段(国际化命名)
migrations.AddField(
model_name='bloguser',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
+
+ #ZXY: 操作6: 修改昵称字段的显示名称(国际化)
migrations.AlterField(
model_name='bloguser',
name='nickname',
- field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
+ field=models.CharField(blank=True, max_length=100, verbose_name='nick name'), #ZXY 改为英文
),
+
+ #ZXY: 操作7: 修改来源字段的显示名称(国际化)
migrations.AlterField(
model_name='bloguser',
name='source',
diff --git a/src/DjangoBlog-master/accounts/models.py b/src/DjangoBlog-master/accounts/models.py
index 3baddbb..76dc501 100644
--- a/src/DjangoBlog-master/accounts/models.py
+++ b/src/DjangoBlog-master/accounts/models.py
@@ -9,27 +9,72 @@ from djangoblog.utils import get_current_site
# Create your models here.
class BlogUser(AbstractUser):
- nickname = models.CharField(_('nick name'), max_length=100, blank=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_modify_time = models.DateTimeField(_('last modify time'), default=now)
- source = models.CharField(_('create source'), max_length=100, blank=True)
+ """
+ 博客用户模型,继承自Django内置的AbstractUser基类
+ 扩展了标准用户模型,增加博客系统特有的字段
+ """
+
+ #ZXY: 昵称字段:用户显示名称,可为空
+ nickname = models.CharField(
+ _('nick name'), #ZXY: 国际化标签,在admin中显示为"昵称"
+ max_length=100, #ZXY: 最大长度100个字符
+ blank=True) #ZXY: 允许为空,非必填字段
+
+ #ZXY: 创建时间:记录用户账号创建的时间点
+ creation_time = models.DateTimeField(
+ _('creation time'), #ZXY: 显示为"创建时间"
+ default=now) #ZXY: 默认值为当前时间,自动记录创建时间戳
+
+ #ZXY: 最后修改时间:记录用户信息最后一次修改的时间
+ last_modify_time = models.DateTimeField(
+ _('last modify time'), #ZXY: 显示为"最后修改时间"
+ default=now) #ZXY: 默认值为当前时间
+
+ #ZXY: 注册来源:记录用户是通过什么渠道注册的
+ source = models.CharField(
+ _('create source'), #ZXY: 显示为"创建来源"
+ max_length=100, #ZXY: 最大长度100个字符
+ blank=True) #ZXY: 允许为空
def get_absolute_url(self):
+ """
+ 获取用户详情页的URL路径(不含域名)
+ 用于Django的通用视图和admin中的"查看现场"链接
+ Returns:
+ str: 用户详情页的URL路径,如 '/author/testuser/'
+ """
return reverse(
'blog:author_detail', kwargs={
- 'author_name': self.username})
+ 'author_name': self.username}) #ZXY: 使用用户名作为URL参数
def __str__(self):
+ """
+ 定义模型的字符串表示,在admin和shell中显示
+ 这里使用邮箱而不是用户名,便于管理员识别用户
+ Returns:
+ str: 用户的邮箱地址
+ """
return self.email
def get_full_url(self):
- site = get_current_site().domain
- url = "https://{site}{path}".format(site=site,
- path=self.get_absolute_url())
+ """
+ 获取包含完整域名的用户详情页URL
+ 用于邮件通知、外部链接等需要完整URL的场景
+ Returns:
+ str: 完整的用户URL,如 'https://example.com/author/testuser/'
+ """
+ site = get_current_site().domain #ZXY: 获取当前站点的域名
+ url = "https://{site}{path}".format(
+ site=site,
+ path=self.get_absolute_url()) #ZXY: 组合域名和路径
return url
class Meta:
- ordering = ['-id']
- verbose_name = _('user')
- verbose_name_plural = verbose_name
- get_latest_by = 'id'
+ """
+ 模型的元数据配置类
+ 用于定义模型级别的选项和配置
+ """
+ ordering = ['-id'] #ZXY: 默认按ID降序排列,新的用户显示在前面
+ verbose_name = _('user') #ZXY: 单数形式的显示名称
+ verbose_name_plural = verbose_name #ZXY: 复数形式与单数相同
+ get_latest_by = 'id' #ZXY: 指定获取最新记录的字段
diff --git a/src/DjangoBlog-master/accounts/tests.py b/src/DjangoBlog-master/accounts/tests.py
index 6893411..04e6b67 100644
--- a/src/DjangoBlog-master/accounts/tests.py
+++ b/src/DjangoBlog-master/accounts/tests.py
@@ -12,78 +12,121 @@ from . import utils
# Create your tests here.
class AccountTest(TestCase):
+ """
+ 账户功能测试套件
+ 测试用户注册、登录、密码重置等核心功能的正确性
+ 继承Django的TestCase,提供数据库事务支持和断言方法
+ """
+
def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
+ """
+ 测试前置设置方法
+ 在每个测试方法执行前运行,用于准备测试数据
+ 避免测试间的数据污染,确保测试独立性
+ """
+ self.client = Client() #ZXY: Django测试客户端,用于模拟HTTP请求
+ self.factory = RequestFactory() #ZXY: 请求工厂,用于创建请求对象进行视图测试
+ #ZXY: 创建测试用户 - 使用create_user方法创建普通用户
self.blog_user = BlogUser.objects.create_user(
username="test",
email="admin@admin.com",
- password="12345678"
+ password="12345678" #ZXY: 测试密码,实际项目中应使用更强密码
)
- self.new_test = "xxx123--="
+ self.new_test = "xxx123--=" #ZXY: 测试用的新密码
def test_validate_account(self):
- site = get_current_site().domain
+ """
+ 测试账户验证功能
+ 验证超级用户创建、登录权限和管理后台访问
+ 这是一个综合性测试,覆盖用户生命周期多个环节
+ """
+ site = get_current_site().domain #ZXY: 获取当前站点域名
+ #ZXY: 创建超级用户 - 测试管理员账户创建功能
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
- password="qwer!@#$ggg")
- testuser = BlogUser.objects.get(username='liangliangyy1')
+ password="qwer!@#$ggg") #ZXY: 复杂密码测试
+ testuser = BlogUser.objects.get(username='liangliangyy1') #ZXY: 从数据库检索用户验证创建成功
+ #ZXY: 测试登录功能 - 使用测试客户端模拟用户登录
loginresult = self.client.login(
username='liangliangyy1',
password='qwer!@#$ggg')
- self.assertEqual(loginresult, True)
+ self.assertEqual(loginresult, True) #ZXY: 断言登录成功返回True
+
+ #ZXY: 测试管理后台访问权限 - 超级用户应该能访问/admin/
response = self.client.get('/admin/')
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.status_code, 200) #ZXY: 断言HTTP 200响应
+ #ZXY: 创建测试分类 - 为后续文章测试准备数据
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
category.last_modify_time = timezone.now()
category.save()
+ #ZXY: 创建测试文章 - 验证用户与文章的关联关系
article = Article()
article.title = "nicetitleaaa"
article.body = "nicecontentaaa"
- article.author = user
+ article.author = user #ZXY: 关联刚创建的超级用户
article.category = category
- article.type = 'a'
- article.status = 'p'
+ article.type = 'a' #ZXY: 文章类型
+ article.status = 'p' #ZXY: 发布状态
article.save()
+ #ZXY: 测试文章管理页面访问 - 验证用户对自有文章的编辑权限
response = self.client.get(article.get_admin_url())
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.status_code, 200) #ZXY: 断言可以正常访问文章管理页
def test_validate_register(self):
+ """
+ 测试用户注册完整流程
+ 覆盖注册表单提交、邮箱验证、账户激活等关键步骤
+ 验证新用户能否成功注册并正常使用系统
+ """
+ #ZXY: 验证注册前用户不存在
self.assertEquals(
0, len(
BlogUser.objects.filter(
- email='user123@user.com')))
+ email='user123@user.com'))) #ZXY: 断言邮箱尚未注册
+
+ #ZXY: 提交注册表单 - 模拟用户注册请求
response = self.client.post(reverse('account:register'), {
'username': 'user1233',
'email': 'user123@user.com',
- 'password1': 'password123!q@wE#R$T',
- 'password2': 'password123!q@wE#R$T',
+ 'password1': 'password123!q@wE#R$T', #ZXY: 强密码测试
+ 'password2': 'password123!q@wE#R$T', #ZXY: 确认密码
})
+
+ #ZXY: 验证用户创建成功
self.assertEquals(
1, len(
BlogUser.objects.filter(
- email='user123@user.com')))
- user = BlogUser.objects.filter(email='user123@user.com')[0]
+ email='user123@user.com'))) #ZXY: 断言用户已创建
+ user = BlogUser.objects.filter(email='user123@user.com')[0] #ZXY: 获取新创建的用户
+
+ #ZXY: 生成邮箱验证签名 - 测试安全验证机制
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
path = reverse('accounts:result')
url = '{path}?type=validation&id={id}&sign={sign}'.format(
- path=path, id=user.id, sign=sign)
+ path=path, id=user.id, sign=sign) #ZXY: 构建验证URL
+
+ #ZXY: 测试邮箱验证页面访问
response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.status_code, 200) #ZXY: 断言验证页面可访问
+ #ZXY: 测试新用户登录功能
self.client.login(username='user1233', password='password123!q@wE#R$T')
user = BlogUser.objects.filter(email='user123@user.com')[0]
+
+ #ZXY: 提升用户权限进行进一步测试
user.is_superuser = True
user.is_staff = True
user.save()
- delete_sidebar_cache()
+ delete_sidebar_cache() #ZXY: 清理缓存
+
+ #ZXY: 创建测试数据验证完整功能流程
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
@@ -100,85 +143,118 @@ class AccountTest(TestCase):
article.status = 'p'
article.save()
+ #ZXY: 测试文章管理权限
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200)
+ #ZXY: 测试退出登录功能
response = self.client.get(reverse('account:logout'))
- self.assertIn(response.status_code, [301, 302, 200])
+ self.assertIn(response.status_code, [301, 302, 200]) #ZXY: 退出可能重定向
+ #ZXY: 验证退出后权限失效
response = self.client.get(article.get_admin_url())
- self.assertIn(response.status_code, [301, 302, 200])
+ self.assertIn(response.status_code, [301, 302, 200]) #ZXY: 应该无法直接访问
+ #ZXY: 测试重新登录
response = self.client.post(reverse('account:login'), {
'username': 'user1233',
'password': 'password123'
})
self.assertIn(response.status_code, [301, 302, 200])
+ #ZXY: 验证重新登录后的权限恢复
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
def test_verify_email_code(self):
+ """
+ 测试邮箱验证码功能
+ 验证验证码的生成、发送、存储和验证全流程
+ 这是密码重置功能的核心安全机制测试
+ """
to_email = "admin@admin.com"
- code = generate_code()
+ code = generate_code() #ZXY: 生成随机验证码
+
+ #ZXY: 测试验证码存储功能
utils.set_code(to_email, code)
+ #ZXY: 测试邮件发送功能 - 在实际环境中会真正发送邮件
utils.send_verify_email(to_email, code)
+ #ZXY: 测试正确验证码验证
err = utils.verify("admin@admin.com", code)
- self.assertEqual(err, None)
+ self.assertEqual(err, None) #ZXY: 断言验证成功,返回None
+ #ZXY: 测试错误验证码验证
err = utils.verify("admin@123.com", code)
- self.assertEqual(type(err), str)
+ self.assertEqual(type(err), str) #ZXY: 断言验证失败返回错误信息字符串
def test_forget_password_email_code_success(self):
+ """
+ 测试忘记密码验证码请求 - 成功场景
+ 验证系统能正确处理合法的密码重置请求
+ """
resp = self.client.post(
path=reverse("account:forget_password_code"),
- data=dict(email="admin@admin.com")
+ data=dict(email="admin@admin.com") #ZXY: 使用已注册的邮箱
)
- self.assertEqual(resp.status_code, 200)
- self.assertEqual(resp.content.decode("utf-8"), "ok")
+ self.assertEqual(resp.status_code, 200) #ZXY: 断言请求成功
+ self.assertEqual(resp.content.decode("utf-8"), "ok") #ZXY: 断言返回正确消息
def test_forget_password_email_code_fail(self):
+ """
+ 测试忘记密码验证码请求 - 失败场景
+ 验证系统能正确处理各种异常输入情况
+ """
+ #ZXY: 测试空数据提交
resp = self.client.post(
path=reverse("account:forget_password_code"),
- data=dict()
+ data=dict() #ZXY: 空数据
)
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
+ self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱") #ZXY: 断言错误处理
+ #ZXY: 测试无效邮箱格式
resp = self.client.post(
path=reverse("account:forget_password_code"),
- data=dict(email="admin@com")
+ data=dict(email="admin@com") #ZXY: 无效邮箱格式
)
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
+ self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱") #ZXY: 断言格式验证
def test_forget_password_email_success(self):
+ """
+ 测试完整密码重置流程 - 成功场景
+ 验证用户能通过验证码成功重置密码
+ """
code = generate_code()
- utils.set_code(self.blog_user.email, code)
+ utils.set_code(self.blog_user.email, code) #ZXY: 存储验证码
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email,
- code=code,
+ code=code, #ZXY: 正确的验证码
)
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
- self.assertEqual(resp.status_code, 302)
+ self.assertEqual(resp.status_code, 302) #ZXY: 断言重定向响应
- # 验证用户密码是否修改成功
+ #ZXY: 验证用户密码是否修改成功 - 从数据库重新获取用户验证密码变更
blog_user = BlogUser.objects.filter(
email=self.blog_user.email,
).first() # type: BlogUser
- self.assertNotEqual(blog_user, None)
- self.assertEqual(blog_user.check_password(data["new_password1"]), True)
+ self.assertNotEqual(blog_user, None) #ZXY: 断言用户存在
+ self.assertEqual(blog_user.check_password(data["new_password1"]), True) #ZXY: 断言密码验证通过
def test_forget_password_email_not_user(self):
+ """
+ 测试密码重置 - 用户不存在场景
+ 验证系统对未注册邮箱的处理
+ """
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
- email="123@123.com",
+ email="123@123.com", #ZXY: 未注册的邮箱
code="123456",
)
resp = self.client.post(
@@ -186,22 +262,26 @@ class AccountTest(TestCase):
data=data
)
- self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.status_code, 200) #ZXY: 断言停留在原页面(表单验证失败)
def test_forget_password_email_code_error(self):
+ """
+ 测试密码重置 - 验证码错误场景
+ 验证系统对错误验证码的拒绝处理
+ """
code = generate_code()
- utils.set_code(self.blog_user.email, code)
+ utils.set_code(self.blog_user.email, code) #ZXY: 存储正确验证码
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email,
- code="111111",
+ code="111111", #ZXY: 错误的验证码
)
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
- self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.status_code, 200) #ZXY: 断言表单验证失败,停留在原页面
diff --git a/src/DjangoBlog-master/accounts/urls.py b/src/DjangoBlog-master/accounts/urls.py
index 107a801..9aa7f71 100644
--- a/src/DjangoBlog-master/accounts/urls.py
+++ b/src/DjangoBlog-master/accounts/urls.py
@@ -4,25 +4,41 @@ from django.urls import re_path
from . import views
from .forms import LoginForm
-app_name = "accounts"
+app_name = "accounts" #ZXY: 应用命名空间,用于反向解析URL时避免冲突
-urlpatterns = [re_path(r'^login/$',
- views.LoginView.as_view(success_url='/'),
- name='login',
- kwargs={'authentication_form': LoginForm}),
+"""
+账户模块的URL配置
+定义用户相关的所有页面路由和对应的视图处理函数
+"""
+urlpatterns = [
+ #ZXY: 登录页面 - 使用正则表达式匹配路径
+ re_path(r'^login/$',
+ views.LoginView.as_view(success_url='/'), #ZXY: 类视图,登录成功后跳转到首页
+ name='login', #ZXY: URL名称,用于反向解析
+ kwargs={'authentication_form': LoginForm}), #ZXY: 传递额外的参数到视图
+
+ #ZXY: 注册页面
re_path(r'^register/$',
- views.RegisterView.as_view(success_url="/"),
+ views.RegisterView.as_view(success_url="/"), #ZXY: 注册成功后跳转到首页
name='register'),
+
+ #ZXY: 退出登录
re_path(r'^logout/$',
- views.LogoutView.as_view(),
+ views.LogoutView.as_view(), #ZXY: 退出登录视图
name='logout'),
+
+ #ZXY: 账户操作结果页面 - 使用path函数,更现代的URL定义方式
path(r'account/result.html',
- views.account_result,
+ views.account_result, #ZXY: 函数视图
name='result'),
+
+ #ZXY: 忘记密码页面
re_path(r'^forget_password/$',
- views.ForgetPasswordView.as_view(),
+ views.ForgetPasswordView.as_view(), #ZXY: 密码重置表单视图
name='forget_password'),
+
+ #ZXY: 获取忘记密码验证码
re_path(r'^forget_password_code/$',
- views.ForgetPasswordEmailCode.as_view(),
+ views.ForgetPasswordEmailCode.as_view(), #ZXY: 验证码发送API
name='forget_password_code'),
]
diff --git a/src/DjangoBlog-master/accounts/user_login_backend.py b/src/DjangoBlog-master/accounts/user_login_backend.py
index 73cdca1..65a0a4a 100644
--- a/src/DjangoBlog-master/accounts/user_login_backend.py
+++ b/src/DjangoBlog-master/accounts/user_login_backend.py
@@ -4,23 +4,69 @@ from django.contrib.auth.backends import ModelBackend
class EmailOrUsernameModelBackend(ModelBackend):
"""
- 允许使用用户名或邮箱登录
+ 自定义用户认证后端
+ 扩展Django默认认证,支持使用邮箱或用户名登录
+
+ 继承自ModelBackend,重写authenticate方法实现多字段登录
+ Django默认只支持用户名登录,这个后端增加了邮箱登录支持
"""
def authenticate(self, request, username=None, password=None, **kwargs):
+ """
+ 用户认证核心方法
+ 根据输入判断是邮箱还是用户名,然后进行密码验证
+
+ Args:
+ request: HttpRequest对象,包含请求信息
+ username (str): 用户输入的用户名或邮箱
+ password (str): 用户输入的密码
+ **kwargs: 其他可能的参数
+
+ Returns:
+ User: 认证成功返回用户对象
+ None: 认证失败返回None
+
+ Logic:
+ 1. 判断输入是否包含@符号 → 邮箱登录
+ 2. 不包含@符号 → 用户名登录
+ 3. 查询数据库获取用户
+ 4. 验证密码是否正确
+ """
+
+ #ZXY: 步骤1:判断登录方式(邮箱 or 用户名)
if '@' in username:
- kwargs = {'email': username}
+ #ZXY: 输入包含@符号,认为是邮箱登录
+ kwargs = {'email': username} #ZXY: 构建邮箱查询条件
else:
- kwargs = {'username': username}
+ #ZXY: 输入不包含@符号,认为是用户名登录
+ kwargs = {'username': username} #ZXY: 构建用户名查询条件
try:
+ #ZXY: 步骤2:根据查询条件获取用户对象
+ #ZXY: get_user_model() 获取项目中使用的用户模型
user = get_user_model().objects.get(**kwargs)
+
+ #ZXY: 步骤3:验证密码
+ #ZXY: check_password() 方法会自动处理密码哈希比较
if user.check_password(password):
- return user
+ return user #ZXY: 密码正确,返回用户对象
except get_user_model().DoesNotExist:
+ #ZXY: 用户不存在,返回None表示认证失败
return None
def get_user(self, username):
+ """
+ 根据用户ID获取用户对象
+ 用于session认证时从数据库加载用户信息
+
+ Args:
+ username (str): 实际上是用户ID(Django的命名历史遗留)
+
+ Returns:
+ User: 用户对象
+ None: 用户不存在
+ """
try:
+ #ZXY: 根据主键(用户ID)获取用户对象
return get_user_model().objects.get(pk=username)
except get_user_model().DoesNotExist:
return None
diff --git a/src/DjangoBlog-master/accounts/utils.py b/src/DjangoBlog-master/accounts/utils.py
index 4b94bdf..4976524 100644
--- a/src/DjangoBlog-master/accounts/utils.py
+++ b/src/DjangoBlog-master/accounts/utils.py
@@ -7,43 +7,97 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import send_email
+#ZXY: 验证码有效期配置:5分钟,使用timedelta便于时间计算
_code_ttl = timedelta(minutes=5)
-
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
- """发送重设密码验证码
+ """发送邮箱验证码邮件
+ 用于密码重置等需要邮箱验证的场景
Args:
- to_mail: 接受邮箱
- subject: 邮件主题
- code: 验证码
+ to_mail (str): 接收邮件的邮箱地址
+ code (str): 6位数字验证码
+ subject (str): 邮件主题,默认为"验证邮件"(支持国际化)
+
+ Example:
+ >>> send_verify_email("user@example.com", "123456")
+ # 发送包含验证码123456的邮件到user@example.com
"""
+ #ZXY: 构建邮件正文,使用国际化翻译
+ #ZXY: %(code)s是字符串格式化占位符,会被实际的验证码替换
html_content = _(
"You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it "
"properly") % {'code': code}
- send_email([to_mail], subject, html_content)
+
+ #ZXY: 调用项目通用的邮件发送函数
+ send_email(
+ [to_mail], #ZXY: 收件人列表,包装成列表形式
+ subject, #ZXY: 邮件主题
+ html_content) #ZXY:邮件内容(HTML格式)
def verify(email: str, code: str) -> typing.Optional[str]:
- """验证code是否有效
+ """
+ 验证用户输入的验证码是否正确
+
Args:
- email: 请求邮箱
- code: 验证码
- Return:
- 如果有错误就返回错误str
- Node:
- 这里的错误处理不太合理,应该采用raise抛出
- 否测调用方也需要对error进行处理
+ email (str): 用户邮箱,作为缓存键
+ code (str): 用户输入的验证码
+
+ Returns:
+ Optional[str]:
+ - 验证成功返回 None
+ - 验证失败返回错误信息字符串
+
+ Note:
+ 当前设计问题:返回错误字符串的方式不够优雅
+ 建议改进:使用异常抛出,让调用方处理异常
+
+ Example:
+ >>> result = verify("user@example.com", "123456")
+ >>> if result:
+ ... print(f"验证失败: {result}")
"""
+ #ZXY: 从缓存中获取该邮箱对应的验证码
cache_code = get_code(email)
+
+ #ZXY: 比较缓存中的验证码和用户输入的验证码
if cache_code != code:
+ #ZXY: 验证失败,返回错误信息(支持国际化)
return gettext("Verification code error")
+ #ZXY: 验证成功,返回None
+ return None
def set_code(email: str, code: str):
+ """
+ 将验证码存储到缓存中,设置5分钟有效期
+
+ Args:
+ email (str): 邮箱地址,作为缓存键
+ code (str): 要存储的验证码
+
+ Technical:
+ 使用Django的缓存框架,支持多种缓存后端(数据库、Redis、内存等)
+ timedelta.seconds 将时间差转换为秒数
+ """
"""设置code"""
- cache.set(email, code, _code_ttl.seconds)
+ cache.set(
+ email, #ZXY: 缓存键:使用邮箱地址
+ code, #ZXY: 缓存值:验证码字符串
+ _code_ttl.seconds) #ZXY: 过期时间:5分钟(300秒)
def get_code(email: str) -> typing.Optional[str]:
+ """
+ 从缓存中获取指定邮箱的验证码
+
+ Args:
+ email (str): 邮箱地址,作为缓存键
+
+ Returns:
+ Optional[str]:
+ - 存在验证码则返回验证码字符串
+ - 不存在或已过期则返回None
+ """
"""获取code"""
- return cache.get(email)
+ return cache.get(email) #ZXY: 从缓存中获取值,不存在返回None
diff --git a/src/DjangoBlog-master/accounts/views.py b/src/DjangoBlog-master/accounts/views.py
index ae67aec..5aceab3 100644
--- a/src/DjangoBlog-master/accounts/views.py
+++ b/src/DjangoBlog-master/accounts/views.py
@@ -32,25 +32,60 @@ logger = logging.getLogger(__name__)
# Create your views here.
class RegisterView(FormView):
+ """
+ 用户注册视图类,继承自Django的FormView
+ 处理用户注册的全流程:表单展示、数据验证、用户创建、邮件发送
+ """
+
+ #ZXY: 指定使用的表单类
form_class = RegisterForm
+
+ #ZXY: 指定渲染的模板路径
template_name = 'account/registration_form.html'
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
+ """
+ 重写dispatch方法,添加CSRF保护装饰器
+ CSRF保护:防止跨站请求伪造攻击,确保表单提交来自本站
+ Returns:
+ HttpResponse: 处理后的HTTP响应
+ """
return super(RegisterView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
+ """
+ 表单数据验证成功后的处理逻辑
+ 这是注册流程的核心方法,包含用户创建和邮件发送
+ Args:
+ form: 已验证通过的RegisterForm实例
+ Returns:
+ HttpResponseRedirect: 重定向到结果页面
+ """
if form.is_valid():
+ #ZXY: 第一步:创建用户对象但不立即保存到数据库
+ #ZXY: save(False)表示只创建对象实例,不执行数据库INSERT
user = form.save(False)
- user.is_active = False
- user.source = 'Register'
+
+ #ZXY: 第二步:设置用户状态和属性
+ user.is_active = False #ZXY: 邮箱验证前用户处于未激活状态,无法登录
+ user.source = 'Register' #ZXY: 记录注册来源,便于后续统计分析
+
+ #ZXY: 第三步:保存用户到数据库
+ #ZXY: save(True)表示立即执行数据库保存操作
user.save(True)
- site = get_current_site().domain
+
+ #ZXY: 第四步:准备邮箱验证所需数据
+ site = get_current_site().domain #ZXY: 获取当前站点域名
+ #ZXY: 生成安全签名:对密钥+用户ID进行双重SHA256哈希,防止篡改
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
+ #ZXY: 开发环境下使用本地地址,生产环境使用真实域名
if settings.DEBUG:
- site = '127.0.0.1:8000'
- path = reverse('account:result')
+ site = '127.0.0.1:8000' #ZXY: 开发环境固定地址
+
+ # 第五步:构建邮箱验证链接
+ path = reverse('account:result') #ZXY: 生成结果页面的URL路径
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
site=site, path=path, id=user.id, sign=sign)
@@ -64,141 +99,215 @@ class RegisterView(FormView):
如果上面链接无法打开,请将此链接复制至浏览器。
{url}
""".format(url=url)
+
+ #ZXY: 第七步:发送验证邮件
send_email(
emailto=[
- user.email,
+ user.email, #ZXY: 收件人列表,这里只有注册用户
],
- title='验证您的电子邮箱',
- content=content)
+ title='验证您的电子邮箱', #ZXY: 邮件标题
+ content=content) #ZXY: 邮件正文内容
+ #ZXY: 步骤8:重定向到注册结果页面
url = reverse('accounts:result') + \
'?type=register&id=' + str(user.id)
return HttpResponseRedirect(url)
else:
+ #ZXY: 表单验证失败,重新显示表单页
return self.render_to_response({
'form': form
})
class LogoutView(RedirectView):
- url = '/login/'
+ """
+ 用户退出登录视图
+ 功能:处理用户退出,清除session,重定向到登录页
+ """
+ url = '/login/' #ZXY: 退出后重定向的URL
- @method_decorator(never_cache)
+ @method_decorator(never_cache) #ZXY: 禁止缓存,确保每次都是最新状态
def dispatch(self, request, *args, **kwargs):
return super(LogoutView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
- logout(request)
- delete_sidebar_cache()
+ """
+ 处理GET请求退出登录
+ """
+ logout(request) #ZXY: Django内置的退出函数,清除session
+ delete_sidebar_cache() #ZXY: 清除侧边栏缓存
return super(LogoutView, self).get(request, *args, **kwargs)
class LoginView(FormView):
- form_class = LoginForm
- template_name = 'account/login.html'
- success_url = '/'
- redirect_field_name = REDIRECT_FIELD_NAME
- login_ttl = 2626560 # 一个月的时间
-
- @method_decorator(sensitive_post_parameters('password'))
- @method_decorator(csrf_protect)
- @method_decorator(never_cache)
+ """
+ 用户登录视图
+ 功能:处理用户登录认证,支持"记住我"功能
+ """
+ form_class = LoginForm #ZXY: 登录表单
+ template_name = 'account/login.html' #ZXY: 登录页面模板
+ success_url = '/' #ZXY: 登录成功后的默认跳转页面
+ redirect_field_name = REDIRECT_FIELD_NAME #ZXY: 重定向字段名,默认为'next'
+ login_ttl = 2626560 #ZXY: "记住我"功能的session有效期(一个月,单位:秒)
+
+ @method_decorator(sensitive_post_parameters('password')) #ZXY: 敏感参数保护,不在错误报告中显示密码
+ @method_decorator(csrf_protect) #ZXY: CSRF保护
+ @method_decorator(never_cache) #ZXY: 禁止缓存
def dispatch(self, request, *args, **kwargs):
-
+ """
+ 添加安全装饰器的dispatch方法
+ """
return super(LoginView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
+ """
+ 添加上下文数据,主要是重定向URL
+ """
redirect_to = self.request.GET.get(self.redirect_field_name)
if redirect_to is None:
- redirect_to = '/'
+ redirect_to = '/' #ZXY: 默认重定向到首页
kwargs['redirect_to'] = redirect_to
return super(LoginView, self).get_context_data(**kwargs)
def form_valid(self, form):
+ """
+ 登录表单验证成功后的处理
+ """
+ #ZXY: 使用Django内置的AuthenticationForm进行认证
form = AuthenticationForm(data=self.request.POST, request=self.request)
if form.is_valid():
- delete_sidebar_cache()
- logger.info(self.redirect_field_name)
+ delete_sidebar_cache() #ZXY: 清除侧边栏缓存
+ logger.info(self.redirect_field_name) #ZXY: 记录日志
+ #ZXY: 执行用户登录
auth.login(self.request, form.get_user())
+
+ #ZXY: 处理"记住我"功能
if self.request.POST.get("remember"):
+ #ZXY: 设置session过期时间为一个月
self.request.session.set_expiry(self.login_ttl)
return super(LoginView, self).form_valid(form)
- # return HttpResponseRedirect('/')
+ #ZXY: return HttpResponseRedirect('/')
else:
+ #ZXY: 认证失败,重新显示表单
return self.render_to_response({
'form': form
})
def get_success_url(self):
-
+ """
+ 获取登录成功后的跳转URL
+ 支持next参数指定的重定向
+ """
redirect_to = self.request.POST.get(self.redirect_field_name)
+ #ZXY: 安全检查:确保重定向URL在允许的host范围内
if not url_has_allowed_host_and_scheme(
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
- redirect_to = self.success_url
+ redirect_to = self.success_url #ZXY: 不安全的URL使用默认首页
return redirect_to
def account_result(request):
- type = request.GET.get('type')
- id = request.GET.get('id')
+ """
+ 账户操作结果页面视图函数
+ 处理注册结果和邮箱验证结果展示
+ """
+ type = request.GET.get('type') #ZXY: 操作类型:register或validation
+ id = request.GET.get('id') #ZXY: 用户ID
+
+
+ user = get_object_or_404(get_user_model(), id=id) #ZXY: 获取用户对象,不存在返回404
+ logger.info(type) #ZXY: 记录操作类型日志
- user = get_object_or_404(get_user_model(), id=id)
- logger.info(type)
+ #ZXY: 如果用户已激活,直接跳转到首页
if user.is_active:
return HttpResponseRedirect('/')
if type and type in ['register', 'validation']:
if type == 'register':
+ #ZXY: 注册成功页面
content = '''
恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
'''
title = '注册成功'
else:
+ #ZXY: 邮箱验证处理
c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
sign = request.GET.get('sign')
+
+ #ZXY: 签名验证,防止URL篡改
if sign != c_sign:
- return HttpResponseForbidden()
+ return HttpResponseForbidden() #ZXY: 签名不匹配,返回403禁止访问
+
+ #ZXY: 激活用户账号
user.is_active = True
user.save()
content = '''
恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。
'''
title = '验证成功'
+
+ #ZXY: 渲染结果页面
return render(request, 'account/result.html', {
'title': title,
'content': content
})
else:
+ #ZXY: 无效的操作类型,跳转到首页
return HttpResponseRedirect('/')
class ForgetPasswordView(FormView):
- form_class = ForgetPasswordForm
- template_name = 'account/forget_password.html'
+ """
+ 忘记密码视图
+ 功能:处理用户密码重置
+ """
+ form_class = ForgetPasswordForm #ZXY: 忘记密码表单
+ template_name = 'account/forget_password.html' #ZXY: 模板路径
def form_valid(self, form):
+ """
+ 表单验证成功后的密码重置逻辑
+ """
if form.is_valid():
+ #ZXY: 根据邮箱查找用户
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
+
+ #ZXY: 使用Django的密码哈希函数设置新密码
blog_user.password = make_password(form.cleaned_data["new_password2"])
- blog_user.save()
+ blog_user.save() #ZXY: 保存到数据库
+
+ #ZXY: 重定向到登录页面
return HttpResponseRedirect('/login/')
else:
+ #ZXY: 表单验证失败,重新显示表单
return self.render_to_response({'form': form})
class ForgetPasswordEmailCode(View):
+ """
+ 忘记密码邮箱验证码发送视图
+ 功能:接收邮箱地址,发送密码重置验证码
+ """
def post(self, request: HttpRequest):
+ """
+ 处理POST请求,发送验证码邮件
+ """
form = ForgetPasswordCodeForm(request.POST)
if not form.is_valid():
- return HttpResponse("错误的邮箱")
- to_email = form.cleaned_data["email"]
+ return HttpResponse("错误的邮箱") #ZXY: 表单验证失败
+ to_email = form.cleaned_data["email"] #ZXY: 获取邮箱地址
+ #ZXY: 生成6位验证码
code = generate_code()
+
+ #ZXY: 发送验证码邮件
utils.send_verify_email(to_email, code)
+
+ #ZXY: 将验证码存储到缓存(5分钟有效期)
utils.set_code(to_email, code)
- return HttpResponse("ok")
+ return HttpResponse("ok") #ZXY: 返回成功响应
diff --git a/src/DjangoBlog-master/blog/admin.py b/src/DjangoBlog-master/blog/admin.py
index 46c3420..91cbeab 100644
--- a/src/DjangoBlog-master/blog/admin.py
+++ b/src/DjangoBlog-master/blog/admin.py
@@ -6,44 +6,44 @@ from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
# Register your models here.
-from .models import Article
+from .models import Article #ZNY 导入Article模型
-class ArticleForm(forms.ModelForm):
- # body = forms.CharField(widget=AdminPagedownWidget())
+class ArticleForm(forms.ModelForm): #ZNY 定义文章表单类
+ # body = forms.CharField(widget=AdminPagedownWidget()) #ZNY 注释掉的Markdown编辑器字段
class Meta:
- model = Article
- fields = '__all__'
+ model = Article #ZNY 指定表单对应的模型
+ fields = '__all__' #ZNY 包含所有模型字段
-def makr_article_publish(modeladmin, request, queryset):
- queryset.update(status='p')
+def makr_article_publish(modeladmin, request, queryset): #ZNY 发布文章的管理动作函数
+ queryset.update(status='p') #ZNY 将选中文章状态更新为发布
-def draft_article(modeladmin, request, queryset):
- queryset.update(status='d')
+def draft_article(modeladmin, request, queryset): #ZNY 设为草稿的管理动作函数
+ queryset.update(status='d') #ZNY 将选中文章状态更新为草稿
-def close_article_commentstatus(modeladmin, request, queryset):
- queryset.update(comment_status='c')
+def close_article_commentstatus(modeladmin, request, queryset): #ZNY 关闭评论的管理动作函数
+ queryset.update(comment_status='c') #ZNY 将选中文章评论状态更新为关闭
-def open_article_commentstatus(modeladmin, request, queryset):
- queryset.update(comment_status='o')
+def open_article_commentstatus(modeladmin, request, queryset): #ZNY 打开评论的管理动作函数
+ queryset.update(comment_status='o') #ZNY 将选中文章评论状态更新为打开
-makr_article_publish.short_description = _('Publish selected articles')
-draft_article.short_description = _('Draft selected articles')
-close_article_commentstatus.short_description = _('Close article comments')
-open_article_commentstatus.short_description = _('Open article comments')
+makr_article_publish.short_description = _('Publish selected articles') #ZNY 设置管理动作显示名称
+draft_article.short_description = _('Draft selected articles') #ZNY 设置管理动作显示名称
+close_article_commentstatus.short_description = _('Close article comments') #ZNY 设置管理动作显示名称
+open_article_commentstatus.short_description = _('Open article comments') #ZNY 设置管理动作显示名称
-class ArticlelAdmin(admin.ModelAdmin):
- list_per_page = 20
- search_fields = ('body', 'title')
- form = ArticleForm
- list_display = (
+class ArticlelAdmin(admin.ModelAdmin): #ZNY 文章模型管理类
+ list_per_page = 20 #ZNY 每页显示20条记录
+ search_fields = ('body', 'title') #ZNY 设置搜索字段
+ form = ArticleForm #ZNY 指定使用的表单类
+ list_display = ( #ZNY 设置列表页显示的字段
'id',
'title',
'author',
@@ -53,60 +53,60 @@ class ArticlelAdmin(admin.ModelAdmin):
'status',
'type',
'article_order')
- list_display_links = ('id', 'title')
- list_filter = ('status', 'type', 'category')
- filter_horizontal = ('tags',)
- exclude = ('creation_time', 'last_modify_time')
- view_on_site = True
- actions = [
+ list_display_links = ('id', 'title') #ZNY 设置可点击进入编辑页的字段
+ list_filter = ('status', 'type', 'category') #ZNY 设置右侧过滤器字段
+ filter_horizontal = ('tags',) #ZNY 设置标签字段使用水平过滤器
+ exclude = ('creation_time', 'last_modify_time') #ZNY 排除不需要在表单中显示的字段
+ view_on_site = True #ZNY 启用"在站点查看"功能
+ actions = [ #ZNY 注册管理动作
makr_article_publish,
draft_article,
close_article_commentstatus,
open_article_commentstatus]
- def link_to_category(self, obj):
- info = (obj.category._meta.app_label, obj.category._meta.model_name)
- link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,))
- return format_html(u'%s' % (link, obj.category.name))
+ def link_to_category(self, obj): #ZNY 自定义方法:生成分类链接
+ info = (obj.category._meta.app_label, obj.category._meta.model_name) #ZNY 获取分类模型的app和模型名称
+ link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,)) #ZNY 生成分类编辑页链接
+ return format_html(u'%s' % (link, obj.category.name)) #ZNY 返回HTML格式的链接
- link_to_category.short_description = _('category')
+ link_to_category.short_description = _('category') #ZNY 设置自定义列显示名称
- def get_form(self, request, obj=None, **kwargs):
- form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs)
- form.base_fields['author'].queryset = get_user_model(
+ def get_form(self, request, obj=None, **kwargs): #ZNY 重写获取表单方法
+ form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs) #ZNY 调用父类方法
+ form.base_fields['author'].queryset = get_user_model( #ZNY 限制作者字段只能选择超级用户
).objects.filter(is_superuser=True)
- return form
+ return form #ZNY 返回修改后的表单
- def save_model(self, request, obj, form, change):
- super(ArticlelAdmin, self).save_model(request, obj, form, change)
+ def save_model(self, request, obj, form, change): #ZNY 重写保存模型方法
+ super(ArticlelAdmin, self).save_model(request, obj, form, change) #ZNY 调用父类保存方法
- def get_view_on_site_url(self, obj=None):
- if obj:
- url = obj.get_full_url()
- return url
- else:
- from djangoblog.utils import get_current_site
- site = get_current_site().domain
- return site
+ def get_view_on_site_url(self, obj=None): #ZNY 重写获取站点查看URL方法
+ if obj: #ZNY 如果有具体对象
+ url = obj.get_full_url() #ZNY 获取文章的完整URL
+ return url #ZNY 返回文章URL
+ else: #ZNY 如果没有对象(列表页)
+ from djangoblog.utils import get_current_site #ZNY 导入获取当前站点函数
+ site = get_current_site().domain #ZNY 获取当前站点域名
+ return site #ZNY 返回站点域名
-class TagAdmin(admin.ModelAdmin):
- exclude = ('slug', 'last_mod_time', 'creation_time')
+class TagAdmin(admin.ModelAdmin): #ZNY 标签模型管理类
+ exclude = ('slug', 'last_modify_time', 'creation_time') #ZNY 排除不需要在表单中显示的字段
-class CategoryAdmin(admin.ModelAdmin):
- list_display = ('name', 'parent_category', 'index')
- exclude = ('slug', 'last_mod_time', 'creation_time')
+class CategoryAdmin(admin.ModelAdmin): #ZNY 分类模型管理类
+ list_display = ('name', 'parent_category', 'index') #ZNY 设置列表页显示的字段
+ exclude = ('slug', 'last_modify_time', 'creation_time') #ZNY 排除不需要在表单中显示的字段
-class LinksAdmin(admin.ModelAdmin):
- exclude = ('last_mod_time', 'creation_time')
+class LinksAdmin(admin.ModelAdmin): #ZNY 链接模型管理类
+ exclude = ('last_modify_time', 'creation_time') #ZNY 排除不需要在表单中显示的字段
-class SideBarAdmin(admin.ModelAdmin):
- list_display = ('name', 'content', 'is_enable', 'sequence')
- exclude = ('last_mod_time', 'creation_time')
+class SideBarAdmin(admin.ModelAdmin): #ZNY 侧边栏模型管理类
+ list_display = ('name', 'content', 'is_enable', 'sequence') #ZNY 设置列表页显示的字段
+ exclude = ('last_modify_time', 'creation_time') #ZNY 排除不需要在表单中显示的字段
-class BlogSettingsAdmin(admin.ModelAdmin):
- pass
+class BlogSettingsAdmin(admin.ModelAdmin): #ZNY 博客设置模型管理类
+ pass #ZNY 使用默认管理配置
diff --git a/src/DjangoBlog-master/blog/apps.py b/src/DjangoBlog-master/blog/apps.py
index 7930587..bdf6b45 100644
--- a/src/DjangoBlog-master/blog/apps.py
+++ b/src/DjangoBlog-master/blog/apps.py
@@ -1,5 +1,5 @@
-from django.apps import AppConfig
+from django.apps import AppConfig #ZNY 导入Django应用配置基类
-class BlogConfig(AppConfig):
- name = 'blog'
+class BlogConfig(AppConfig): #ZNY 定义博客应用的配置类
+ name = 'blog' #ZNY 指定应用的Python路径为'blog'
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/context_processors.py b/src/DjangoBlog-master/blog/context_processors.py
index 73e3088..5d35a45 100644
--- a/src/DjangoBlog-master/blog/context_processors.py
+++ b/src/DjangoBlog-master/blog/context_processors.py
@@ -1,43 +1,43 @@
-import logging
+import logging #ZNY 导入日志模块
-from django.utils import timezone
+from django.utils import timezone #ZNY 导入Django时区工具
-from djangoblog.utils import cache, get_blog_setting
-from .models import Category, Article
+from djangoblog.utils import cache, get_blog_setting #ZNY 导入缓存工具和博客设置获取函数
+from .models import Category, Article #ZNY 导入分类和文章模型
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #ZNY 获取当前模块的日志记录器
-def seo_processor(requests):
- key = 'seo_processor'
- value = cache.get(key)
- if value:
- return value
- else:
- logger.info('set processor cache.')
- setting = get_blog_setting()
- value = {
- 'SITE_NAME': setting.site_name,
- 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,
- 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes,
- 'SITE_SEO_DESCRIPTION': setting.site_seo_description,
- 'SITE_DESCRIPTION': setting.site_description,
- 'SITE_KEYWORDS': setting.site_keywords,
- 'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',
- 'ARTICLE_SUB_LENGTH': setting.article_sub_length,
- 'nav_category_list': Category.objects.all(),
- 'nav_pages': Article.objects.filter(
- type='p',
- status='p'),
- 'OPEN_SITE_COMMENT': setting.open_site_comment,
- 'BEIAN_CODE': setting.beian_code,
- 'ANALYTICS_CODE': setting.analytics_code,
- "BEIAN_CODE_GONGAN": setting.gongan_beiancode,
- "SHOW_GONGAN_CODE": setting.show_gongan_code,
- "CURRENT_YEAR": timezone.now().year,
- "GLOBAL_HEADER": setting.global_header,
- "GLOBAL_FOOTER": setting.global_footer,
- "COMMENT_NEED_REVIEW": setting.comment_need_review,
+def seo_processor(requests): #ZNY 定义SEO上下文处理器函数
+ key = 'seo_processor' #ZNY 设置缓存键名
+ value = cache.get(key) #ZNY 尝试从缓存获取数据
+ if value: #ZNY 如果缓存存在
+ return value #ZNY 直接返回缓存数据
+ else: #ZNY 如果缓存不存在
+ logger.info('set processor cache.') #ZNY 记录设置缓存日志
+ setting = get_blog_setting() #ZNY 获取博客设置
+ value = { #ZNY 构建上下文数据字典
+ 'SITE_NAME': setting.site_name, #ZNY 网站名称
+ 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense, #ZNY 是否显示谷歌广告
+ 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes, #ZNY 谷歌广告代码
+ 'SITE_SEO_DESCRIPTION': setting.site_seo_description, #ZNY 网站SEO描述
+ 'SITE_DESCRIPTION': setting.site_description, #ZNY 网站描述
+ 'SITE_KEYWORDS': setting.site_keywords, #ZNY 网站关键词
+ 'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/', #ZNY 网站基础URL
+ 'ARTICLE_SUB_LENGTH': setting.article_sub_length, #ZNY 文章摘要长度
+ 'nav_category_list': Category.objects.all(), #ZNY 导航分类列表
+ 'nav_pages': Article.objects.filter( #ZNY 导航页面列表
+ type='p', #ZNY 过滤类型为页面
+ status='p'), #ZNY 过滤状态为发布
+ 'OPEN_SITE_COMMENT': setting.open_site_comment, #ZNY 是否开启全站评论
+ 'BEIAN_CODE': setting.beian_code, #ZNY ICP备案号
+ 'ANALYTICS_CODE': setting.analytics_code, #ZNY 网站统计代码
+ "BEIAN_CODE_GONGAN": setting.gongan_beiancode, #ZNY 公安备案号
+ "SHOW_GONGAN_CODE": setting.show_gongan_code, #ZNY 是否显示公安备案
+ "CURRENT_YEAR": timezone.now().year, #ZNY 当前年份
+ "GLOBAL_HEADER": setting.global_header, #ZNY 全局头部内容
+ "GLOBAL_FOOTER": setting.global_footer, #ZNY 全局尾部内容
+ "COMMENT_NEED_REVIEW": setting.comment_need_review, #ZNY 评论是否需要审核
}
- cache.set(key, value, 60 * 60 * 10)
- return value
+ cache.set(key, value, 60 * 60 * 10) #ZNY 将数据存入缓存,有效期10小时
+ return value #ZNY 返回上下文数据
diff --git a/src/DjangoBlog-master/blog/documents.py b/src/DjangoBlog-master/blog/documents.py
index 0f1db7b..96cfb4a 100644
--- a/src/DjangoBlog-master/blog/documents.py
+++ b/src/DjangoBlog-master/blog/documents.py
@@ -7,21 +7,21 @@ from elasticsearch_dsl.connections import connections
from blog.models import Article
-ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')
+ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL') #ZNY 检查是否配置了Elasticsearch
-if ELASTICSEARCH_ENABLED:
- connections.create_connection(
- hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
- from elasticsearch import Elasticsearch
+if ELASTICSEARCH_ENABLED: #ZNY 如果Elasticsearch已启用
+ connections.create_connection( #ZNY 创建Elasticsearch连接
+ hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']]) #ZNY 从配置中获取主机地址
+ from elasticsearch import Elasticsearch #ZNY 导入Elasticsearch客户端
- es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- from elasticsearch.client import IngestClient
+ es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) #ZNY 创建Elasticsearch实例
+ from elasticsearch.client import IngestClient #ZNY 导入Ingest客户端
- c = IngestClient(es)
+ c = IngestClient(es) #ZNY 创建Ingest客户端实例
try:
- c.get_pipeline('geoip')
- except elasticsearch.exceptions.NotFoundError:
- c.put_pipeline('geoip', body='''{
+ c.get_pipeline('geoip') #ZNY 尝试获取geoip管道
+ except elasticsearch.exceptions.NotFoundError: #ZNY 如果geoip管道不存在
+ c.put_pipeline('geoip', body='''{ #ZNY 创建geoip管道
"description" : "Add geoip info",
"processors" : [
{
@@ -30,184 +30,184 @@ if ELASTICSEARCH_ENABLED:
}
}
]
- }''')
+ }''') #ZNY 定义geoip处理管道配置
-class GeoIp(InnerDoc):
- continent_name = Keyword()
- country_iso_code = Keyword()
- country_name = Keyword()
- location = GeoPoint()
+class GeoIp(InnerDoc): #ZNY 定义GeoIP内嵌文档类
+ continent_name = Keyword() #ZNY 大洲名称
+ country_iso_code = Keyword() #ZNY 国家ISO代码
+ country_name = Keyword() #ZNY 国家名称
+ location = GeoPoint() #ZNY 地理位置坐标
-class UserAgentBrowser(InnerDoc):
- Family = Keyword()
- Version = Keyword()
+class UserAgentBrowser(InnerDoc): #ZNY 定义用户代理浏览器信息类
+ Family = Keyword() #ZNY 浏览器家族
+ Version = Keyword() #ZNY 浏览器版本
-class UserAgentOS(UserAgentBrowser):
- pass
+class UserAgentOS(UserAgentBrowser): #ZNY 定义用户代理操作系统信息类,继承自浏览器类
+ pass #ZNY 继承父类字段
-class UserAgentDevice(InnerDoc):
- Family = Keyword()
- Brand = Keyword()
- Model = Keyword()
+class UserAgentDevice(InnerDoc): #ZNY 定义用户代理设备信息类
+ Family = Keyword() #ZNY 设备家族
+ Brand = Keyword() #ZNY 设备品牌
+ Model = Keyword() #ZNY 设备型号
-class UserAgent(InnerDoc):
- browser = Object(UserAgentBrowser, required=False)
- os = Object(UserAgentOS, required=False)
- device = Object(UserAgentDevice, required=False)
- string = Text()
- is_bot = Boolean()
+class UserAgent(InnerDoc): #ZNY 定义完整的用户代理信息类
+ browser = Object(UserAgentBrowser, required=False) #ZNY 浏览器信息对象
+ os = Object(UserAgentOS, required=False) #ZNY 操作系统信息对象
+ device = Object(UserAgentDevice, required=False) #ZNY 设备信息对象
+ string = Text() #ZNY 原始用户代理字符串
+ is_bot = Boolean() #ZNY 是否为机器人
-class ElapsedTimeDocument(Document):
- url = Keyword()
- time_taken = Long()
- log_datetime = Date()
- ip = Keyword()
- geoip = Object(GeoIp, required=False)
- useragent = Object(UserAgent, required=False)
+class ElapsedTimeDocument(Document): #ZNY 定义性能耗时文档类
+ url = Keyword() #ZNY 请求URL
+ time_taken = Long() #ZNY 耗时(毫秒)
+ log_datetime = Date() #ZNY 日志时间
+ ip = Keyword() #ZNY IP地址
+ geoip = Object(GeoIp, required=False) #ZNY GeoIP地理位置信息
+ useragent = Object(UserAgent, required=False) #ZNY 用户代理信息
- class Index:
- name = 'performance'
- settings = {
- "number_of_shards": 1,
- "number_of_replicas": 0
+ class Index: #ZNY 定义索引配置
+ name = 'performance' #ZNY 索引名称
+ settings = { #ZNY 索引设置
+ "number_of_shards": 1, #ZNY 分片数量
+ "number_of_replicas": 0 #ZNY 副本数量
}
- class Meta:
- doc_type = 'ElapsedTime'
+ class Meta: #ZNY 元数据配置
+ doc_type = 'ElapsedTime' #ZNY 文档类型
-class ElaspedTimeDocumentManager:
+class ElaspedTimeDocumentManager: #ZNY 性能耗时文档管理器类
@staticmethod
- def build_index():
- from elasticsearch import Elasticsearch
- client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- res = client.indices.exists(index="performance")
- if not res:
- ElapsedTimeDocument.init()
+ def build_index(): #ZNY 构建索引静态方法
+ from elasticsearch import Elasticsearch #ZNY 导入Elasticsearch
+ client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) #ZNY 创建客户端
+ res = client.indices.exists(index="performance") #ZNY 检查索引是否存在
+ if not res: #ZNY 如果索引不存在
+ ElapsedTimeDocument.init() #ZNY 初始化性能耗时文档索引
@staticmethod
- def delete_index():
- from elasticsearch import Elasticsearch
- es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- es.indices.delete(index='performance', ignore=[400, 404])
+ def delete_index(): #ZNY 删除索引静态方法
+ from elasticsearch import Elasticsearch #ZNY 导入Elasticsearch
+ es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) #ZNY 创建客户端
+ es.indices.delete(index='performance', ignore=[400, 404]) #ZNY 删除性能索引,忽略特定错误
@staticmethod
- def create(url, time_taken, log_datetime, useragent, ip):
- ElaspedTimeDocumentManager.build_index()
- ua = UserAgent()
- ua.browser = UserAgentBrowser()
- ua.browser.Family = useragent.browser.family
- ua.browser.Version = useragent.browser.version_string
-
- ua.os = UserAgentOS()
- ua.os.Family = useragent.os.family
- ua.os.Version = useragent.os.version_string
-
- ua.device = UserAgentDevice()
- ua.device.Family = useragent.device.family
- ua.device.Brand = useragent.device.brand
- ua.device.Model = useragent.device.model
- ua.string = useragent.ua_string
- ua.is_bot = useragent.is_bot
-
- doc = ElapsedTimeDocument(
- meta={
- 'id': int(
+ def create(url, time_taken, log_datetime, useragent, ip): #ZNY 创建性能记录静态方法
+ ElaspedTimeDocumentManager.build_index() #ZNY 确保索引存在
+ ua = UserAgent() #ZNY 创建用户代理对象
+ ua.browser = UserAgentBrowser() #ZNY 创建浏览器信息对象
+ ua.browser.Family = useragent.browser.family #ZNY 设置浏览器家族
+ ua.browser.Version = useragent.browser.version_string #ZNY 设置浏览器版本
+
+ ua.os = UserAgentOS() #ZNY 创建操作系统信息对象
+ ua.os.Family = useragent.os.family #ZNY 设置操作系统家族
+ ua.os.Version = useragent.os.version_string #ZNY 设置操作系统版本
+
+ ua.device = UserAgentDevice() #ZNY 创建设备信息对象
+ ua.device.Family = useragent.device.family #ZNY 设置设备家族
+ ua.device.Brand = useragent.device.brand #ZNY 设置设备品牌
+ ua.device.Model = useragent.device.model #ZNY 设置设备型号
+ ua.string = useragent.ua_string #ZNY 设置原始用户代理字符串
+ ua.is_bot = useragent.is_bot #ZNY 设置是否为机器人
+
+ doc = ElapsedTimeDocument( #ZNY 创建性能耗时文档
+ meta={ #ZNY 文档元数据
+ 'id': int( #ZNY 使用时间戳作为文档ID
round(
time.time() *
1000))
},
- url=url,
- time_taken=time_taken,
- log_datetime=log_datetime,
- useragent=ua, ip=ip)
- doc.save(pipeline="geoip")
-
-
-class ArticleDocument(Document):
- body = Text(analyzer='ik_max_word', search_analyzer='ik_smart')
- title = Text(analyzer='ik_max_word', search_analyzer='ik_smart')
- author = Object(properties={
- 'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
- 'id': Integer()
+ url=url, #ZNY 设置URL
+ time_taken=time_taken, #ZNY 设置耗时
+ log_datetime=log_datetime, #ZNY 设置日志时间
+ useragent=ua, ip=ip) #ZNY 设置用户代理和IP
+ doc.save(pipeline="geoip") #ZNY 保存文档并使用geoip管道处理
+
+
+class ArticleDocument(Document): #ZNY 定义文章文档类
+ body = Text(analyzer='ik_max_word', search_analyzer='ik_smart') #ZNY 正文,使用IK中文分词器
+ title = Text(analyzer='ik_max_word', search_analyzer='ik_smart') #ZNY 标题,使用IK中文分词器
+ author = Object(properties={ #ZNY 作者对象
+ 'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'), #ZNY 作者昵称
+ 'id': Integer() #ZNY 作者ID
})
- category = Object(properties={
- 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
- 'id': Integer()
+ category = Object(properties={ #ZNY 分类对象
+ 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'), #ZNY 分类名称
+ 'id': Integer() #ZNY 分类ID
})
- tags = Object(properties={
- 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
- 'id': Integer()
+ tags = Object(properties={ #ZNY 标签对象
+ 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'), #ZNY 标签名称
+ 'id': Integer() #ZNY 标签ID
})
- pub_time = Date()
- status = Text()
- comment_status = Text()
- type = Text()
- views = Integer()
- article_order = Integer()
-
- class Index:
- name = 'blog'
- settings = {
- "number_of_shards": 1,
- "number_of_replicas": 0
+ pub_time = Date() #ZNY 发布时间
+ status = Text() #ZNY 文章状态
+ comment_status = Text() #ZNY 评论状态
+ type = Text() #ZNY 文章类型
+ views = Integer() #ZNY 浏览量
+ article_order = Integer() #ZNY 文章排序
+
+ class Index: #ZNY 定义索引配置
+ name = 'blog' #ZNY 索引名称
+ settings = { #ZNY 索引设置
+ "number_of_shards": 1, #ZNY 分片数量
+ "number_of_replicas": 0 #ZNY 副本数量
}
- class Meta:
- doc_type = 'Article'
+ class Meta: #ZNY 元数据配置
+ doc_type = 'Article' #ZNY 文档类型
-class ArticleDocumentManager():
+class ArticleDocumentManager(): #ZNY 文章文档管理器类
- def __init__(self):
- self.create_index()
+ def __init__(self): #ZNY 初始化方法
+ self.create_index() #ZNY 创建索引
- def create_index(self):
- ArticleDocument.init()
+ def create_index(self): #ZNY 创建索引方法
+ ArticleDocument.init() #ZNY 初始化文章文档索引
- def delete_index(self):
- from elasticsearch import Elasticsearch
- es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- es.indices.delete(index='blog', ignore=[400, 404])
+ def delete_index(self): #ZNY 删除索引方法
+ from elasticsearch import Elasticsearch #ZNY 导入Elasticsearch
+ es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) #ZNY 创建客户端
+ es.indices.delete(index='blog', ignore=[400, 404]) #ZNY 删除博客索引,忽略特定错误
- def convert_to_doc(self, articles):
- return [
- ArticleDocument(
+ def convert_to_doc(self, articles): #ZNY 将文章转换为文档方法
+ return [ #ZNY 返回文档列表
+ ArticleDocument( #ZNY 创建文章文档
meta={
- 'id': article.id},
- body=article.body,
- title=article.title,
- author={
- 'nickname': article.author.username,
- 'id': article.author.id},
- category={
- 'name': article.category.name,
- 'id': article.category.id},
- tags=[
+ 'id': article.id}, #ZNY 使用文章ID作为文档ID
+ body=article.body, #ZNY 设置正文
+ title=article.title, #ZNY 设置标题
+ author={ #ZNY 设置作者信息
+ 'nickname': article.author.username, #ZNY 作者用户名
+ 'id': article.author.id}, #ZNY 作者ID
+ category={ #ZNY 设置分类信息
+ 'name': article.category.name, #ZNY 分类名称
+ 'id': article.category.id}, #ZNY 分类ID
+ tags=[ #ZNY 设置标签列表
{
- 'name': t.name,
- 'id': t.id} for t in article.tags.all()],
- pub_time=article.pub_time,
- status=article.status,
- comment_status=article.comment_status,
- type=article.type,
- views=article.views,
- article_order=article.article_order) for article in articles]
-
- def rebuild(self, articles=None):
- ArticleDocument.init()
- articles = articles if articles else Article.objects.all()
- docs = self.convert_to_doc(articles)
- for doc in docs:
- doc.save()
-
- def update_docs(self, docs):
- for doc in docs:
- doc.save()
+ 'name': t.name, #ZNY 标签名称
+ 'id': t.id} for t in article.tags.all()], #ZNY 遍历所有标签
+ pub_time=article.pub_time, #ZNY 设置发布时间
+ status=article.status, #ZNY 设置文章状态
+ comment_status=article.comment_status, #ZNY 设置评论状态
+ type=article.type, #ZNY 设置文章类型
+ views=article.views, #ZNY 设置浏览量
+ article_order=article.article_order) for article in articles] #ZNY 设置文章排序
+
+ def rebuild(self, articles=None): #ZNY 重建索引方法
+ ArticleDocument.init() #ZNY 重新初始化索引
+ articles = articles if articles else Article.objects.all() #ZNY 获取所有文章或指定文章
+ docs = self.convert_to_doc(articles) #ZNY 转换为文档格式
+ for doc in docs: #ZNY 遍历所有文档
+ doc.save() #ZNY 保存文档到Elasticsearch
+
+ def update_docs(self, docs): #ZNY 更新文档方法
+ for doc in docs: #ZNY 遍历文档列表
+ doc.save() #ZNY 保存更新后的文档
diff --git a/src/DjangoBlog-master/blog/forms.py b/src/DjangoBlog-master/blog/forms.py
index 715be76..a8e9ef6 100644
--- a/src/DjangoBlog-master/blog/forms.py
+++ b/src/DjangoBlog-master/blog/forms.py
@@ -1,19 +1,19 @@
-import logging
+import logging #ZNY 导入日志模块
-from django import forms
-from haystack.forms import SearchForm
+from django import forms #ZNY 导入Django表单模块
+from haystack.forms import SearchForm #ZNY 导入Haystack搜索表单基类
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #ZNY 获取当前模块的日志记录器
-class BlogSearchForm(SearchForm):
- querydata = forms.CharField(required=True)
+class BlogSearchForm(SearchForm): #ZNY 定义博客搜索表单类,继承自Haystack搜索表单
+ querydata = forms.CharField(required=True) #ZNY 定义查询数据字段,设置为必填
- def search(self):
- datas = super(BlogSearchForm, self).search()
- if not self.is_valid():
- return self.no_query_found()
+ def search(self): #ZNY 重写搜索方法
+ datas = super(BlogSearchForm, self).search() #ZNY 调用父类的搜索方法获取数据
+ if not self.is_valid(): #ZNY 如果表单验证失败
+ return self.no_query_found() #ZNY 返回无查询结果
- if self.cleaned_data['querydata']:
- logger.info(self.cleaned_data['querydata'])
- return datas
+ if self.cleaned_data['querydata']: #ZNY 如果查询数据存在且已清洗
+ logger.info(self.cleaned_data['querydata']) #ZNY 记录查询关键词到日志
+ return datas #ZNY 返回搜索结果数据
diff --git a/src/DjangoBlog-master/blog/management/commands/build_index.py b/src/DjangoBlog-master/blog/management/commands/build_index.py
index 3c4acd7..6a725a5 100644
--- a/src/DjangoBlog-master/blog/management/commands/build_index.py
+++ b/src/DjangoBlog-master/blog/management/commands/build_index.py
@@ -1,18 +1,18 @@
-from django.core.management.base import BaseCommand
+from django.core.management.base import BaseCommand #ZNY 导入Django管理命令基类
from blog.documents import ElapsedTimeDocument, ArticleDocumentManager, ElaspedTimeDocumentManager, \
- ELASTICSEARCH_ENABLED
+ ELASTICSEARCH_ENABLED #ZNY 导入Elasticsearch相关文档和管理器
-# TODO 参数化
-class Command(BaseCommand):
- help = 'build search index'
+# TODO 参数化 #ZNY 待办事项:将命令参数化
+class Command(BaseCommand): #ZNY 定义构建搜索索引命令类,继承BaseCommand
+ help = 'build search index' #ZNY 命令帮助信息
- def handle(self, *args, **options):
- if ELASTICSEARCH_ENABLED:
- ElaspedTimeDocumentManager.build_index()
- manager = ElapsedTimeDocument()
- manager.init()
- manager = ArticleDocumentManager()
- manager.delete_index()
- manager.rebuild()
+ def handle(self, *args, **options): #ZNY 命令处理主方法
+ if ELASTICSEARCH_ENABLED: #ZNY 检查Elasticsearch是否启用
+ ElaspedTimeDocumentManager.build_index() #ZNY 构建性能耗时文档索引
+ manager = ElapsedTimeDocument() #ZNY 创建性能耗时文档管理器实例
+ manager.init() #ZNY 初始化性能耗时文档索引
+ manager = ArticleDocumentManager() #ZNY 创建文章文档管理器实例
+ manager.delete_index() #ZNY 删除现有文章索引
+ manager.rebuild() #ZNY 重新构建文章索引
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/management/commands/build_search_words.py b/src/DjangoBlog-master/blog/management/commands/build_search_words.py
index cfe7e0d..6a31431 100644
--- a/src/DjangoBlog-master/blog/management/commands/build_search_words.py
+++ b/src/DjangoBlog-master/blog/management/commands/build_search_words.py
@@ -3,11 +3,11 @@ from django.core.management.base import BaseCommand
from blog.models import Tag, Category
-# TODO 参数化
-class Command(BaseCommand):
- help = 'build search words'
+# TODO 参数化 #ZNY 待办事项:将命令参数化
+class Command(BaseCommand): #ZNY 定义构建搜索词命令类,继承BaseCommand
+ help = 'build search words' #ZNY 命令帮助信息:构建搜索词
- def handle(self, *args, **options):
- datas = set([t.name for t in Tag.objects.all()] +
+ def handle(self, *args, **options): #ZNY 命令处理主方法
+ datas = set([t.name for t in Tag.objects.all()] + #ZNY 获取所有标签名称和分类名称,合并并去重
[t.name for t in Category.objects.all()])
- print('\n'.join(datas))
+ print('\n'.join(datas)) #ZNY 将数据用换行符连接并打印输出
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/management/commands/clear_cache.py b/src/DjangoBlog-master/blog/management/commands/clear_cache.py
index 0d66172..cd8b6da 100644
--- a/src/DjangoBlog-master/blog/management/commands/clear_cache.py
+++ b/src/DjangoBlog-master/blog/management/commands/clear_cache.py
@@ -3,9 +3,9 @@ from django.core.management.base import BaseCommand
from djangoblog.utils import cache
-class Command(BaseCommand):
- help = 'clear the whole cache'
+class Command(BaseCommand): #ZNY 定义清除缓存命令类,继承BaseCommand
+ help = 'clear the whole cache' #ZNY 命令帮助信息:清除整个缓存
- def handle(self, *args, **options):
- cache.clear()
- self.stdout.write(self.style.SUCCESS('Cleared cache\n'))
+ def handle(self, *args, **options): #ZNY 命令处理主方法
+ cache.clear() #ZNY 调用Django缓存接口清除所有缓存
+ self.stdout.write(self.style.SUCCESS('Cleared cache\n')) #ZNY 输出成功信息到标准输出
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/management/commands/create_testdata.py b/src/DjangoBlog-master/blog/management/commands/create_testdata.py
index 675d2ba..ca7140b 100644
--- a/src/DjangoBlog-master/blog/management/commands/create_testdata.py
+++ b/src/DjangoBlog-master/blog/management/commands/create_testdata.py
@@ -5,36 +5,36 @@ from django.core.management.base import BaseCommand
from blog.models import Article, Tag, Category
-class Command(BaseCommand):
- help = 'create test datas'
-
- def handle(self, *args, **options):
- user = get_user_model().objects.get_or_create(
- email='test@test.com', username='测试用户', password=make_password('test!q@w#eTYU'))[0]
-
- pcategory = Category.objects.get_or_create(
- name='我是父类目', parent_category=None)[0]
-
- category = Category.objects.get_or_create(
- name='子类目', parent_category=pcategory)[0]
-
- category.save()
- basetag = Tag()
- basetag.name = "标签"
- basetag.save()
- for i in range(1, 20):
- article = Article.objects.get_or_create(
- category=category,
- title='nice title ' + str(i),
- body='nice content ' + str(i),
- author=user)[0]
- tag = Tag()
- tag.name = "标签" + str(i)
- tag.save()
- article.tags.add(tag)
- article.tags.add(basetag)
- article.save()
-
- from djangoblog.utils import cache
- cache.clear()
- self.stdout.write(self.style.SUCCESS('created test datas \n'))
+class Command(BaseCommand): #ZNY 定义创建测试数据命令类,继承BaseCommand
+ help = 'create test datas' #ZNY 命令帮助信息:创建测试数据
+
+ def handle(self, *args, **options): #ZNY 命令处理主方法
+ user = get_user_model().objects.get_or_create( #ZNY 获取或创建测试用户
+ email='test@test.com', username='测试用户', password=make_password('test!q@w#eTYU'))[0] #ZNY 设置用户邮箱、用户名和加密密码
+
+ pcategory = Category.objects.get_or_create( #ZNY 获取或创建父分类
+ name='我是父类目', parent_category=None)[0] #ZNY 设置父分类名称,无父级分类
+
+ category = Category.objects.get_or_create( #ZNY 获取或创建子分类
+ name='子类目', parent_category=pcategory)[0] #ZNY 设置子分类名称,指定父级分类
+
+ category.save() #ZNY 保存子分类
+ basetag = Tag() #ZNY 创建基础标签实例
+ basetag.name = "标签" #ZNY 设置基础标签名称
+ basetag.save() #ZNY 保存基础标签
+ for i in range(1, 20): #ZNY 循环创建19篇文章
+ article = Article.objects.get_or_create( #ZNY 获取或创建文章
+ category=category, #ZNY 设置文章分类
+ title='nice title ' + str(i), #ZNY 设置带编号的文章标题
+ body='nice content ' + str(i), #ZNY 设置带编号的文章正文
+ author=user)[0] #ZNY 设置文章作者
+ tag = Tag() #ZNY 创建新标签实例
+ tag.name = "标签" + str(i) #ZNY 设置带编号的标签名称
+ tag.save() #ZNY 保存标签
+ article.tags.add(tag) #ZNY 将新标签添加到文章
+ article.tags.add(basetag) #ZNY 将基础标签添加到文章
+ article.save() #ZNY 保存文章
+
+ from djangoblog.utils import cache #ZNY 导入缓存工具
+ cache.clear() #ZNY 清除所有缓存
+ self.stdout.write(self.style.SUCCESS('created test datas \n')) #ZNY 输出成功信息到标准输出
diff --git a/src/DjangoBlog-master/blog/management/commands/ping_baidu.py b/src/DjangoBlog-master/blog/management/commands/ping_baidu.py
index 2c7fbdd..c6427a6 100644
--- a/src/DjangoBlog-master/blog/management/commands/ping_baidu.py
+++ b/src/DjangoBlog-master/blog/management/commands/ping_baidu.py
@@ -4,47 +4,47 @@ from djangoblog.spider_notify import SpiderNotify
from djangoblog.utils import get_current_site
from blog.models import Article, Tag, Category
-site = get_current_site().domain
-
-
-class Command(BaseCommand):
- help = 'notify baidu url'
-
- def add_arguments(self, parser):
- parser.add_argument(
- 'data_type',
- type=str,
- choices=[
- 'all',
- 'article',
- 'tag',
- 'category'],
- help='article : all article,tag : all tag,category: all category,all: All of these')
-
- def get_full_url(self, path):
- url = "https://{site}{path}".format(site=site, path=path)
- return url
-
- def handle(self, *args, **options):
- type = options['data_type']
- self.stdout.write('start get %s' % type)
-
- urls = []
- if type == 'article' or type == 'all':
- for article in Article.objects.filter(status='p'):
- urls.append(article.get_full_url())
- if type == 'tag' or type == 'all':
- for tag in Tag.objects.all():
- url = tag.get_absolute_url()
- urls.append(self.get_full_url(url))
- if type == 'category' or type == 'all':
- for category in Category.objects.all():
- url = category.get_absolute_url()
- urls.append(self.get_full_url(url))
-
- self.stdout.write(
- self.style.SUCCESS(
- 'start notify %d urls' %
+site = get_current_site().domain #ZNY 获取当前站点的域名
+
+
+class Command(BaseCommand): #ZNY 定义通知百度URL命令类,继承BaseCommand
+ help = 'notify baidu url' #ZNY 命令帮助信息:通知百度URL
+
+ def add_arguments(self, parser): #ZNY 添加命令行参数方法
+ parser.add_argument( #ZNY 添加数据类型参数
+ 'data_type', #ZNY 参数名称
+ type=str, #ZNY 参数类型为字符串
+ choices=[ #ZNY 参数可选值
+ 'all', #ZNY 所有类型
+ 'article', #ZNY 仅文章
+ 'tag', #ZNY 仅标签
+ 'category'], #ZNY 仅分类
+ help='article : all article,tag : all tag,category: all category,all: All of these') #ZNY 参数帮助信息
+
+ def get_full_url(self, path): #ZNY 获取完整URL方法
+ url = "https://{site}{path}".format(site=site, path=path) #ZNY 构建完整的HTTPS URL
+ return url #ZNY 返回完整URL
+
+ def handle(self, *args, **options): #ZNY 命令处理主方法
+ type = options['data_type'] #ZNY 从参数获取数据类型
+ self.stdout.write('start get %s' % type) #ZNY 输出开始获取数据的提示
+
+ urls = [] #ZNY 初始化URL列表
+ if type == 'article' or type == 'all': #ZNY 如果类型是文章或全部
+ for article in Article.objects.filter(status='p'): #ZNY 遍历所有已发布的文章
+ urls.append(article.get_full_url()) #ZNY 将文章完整URL添加到列表
+ if type == 'tag' or type == 'all': #ZNY 如果类型是标签或全部
+ for tag in Tag.objects.all(): #ZNY 遍历所有标签
+ url = tag.get_absolute_url() #ZNY 获取标签的相对URL
+ urls.append(self.get_full_url(url)) #ZNY 转换为完整URL并添加到列表
+ if type == 'category' or type == 'all': #ZNY 如果类型是分类或全部
+ for category in Category.objects.all(): #ZNY 遍历所有分类
+ url = category.get_absolute_url() #ZNY 获取分类的相对URL
+ urls.append(self.get_full_url(url)) #ZNY 转换为完整URL并添加到列表
+
+ self.stdout.write( #ZNY 输出开始通知的提示
+ self.style.SUCCESS( #ZNY 使用成功样式
+ 'start notify %d urls' % #ZNY 显示要通知的URL数量
len(urls)))
- SpiderNotify.baidu_notify(urls)
- self.stdout.write(self.style.SUCCESS('finish notify'))
+ SpiderNotify.baidu_notify(urls) #ZNY 调用百度通知功能推送URL
+ self.stdout.write(self.style.SUCCESS('finish notify')) #ZNY 输出完成通知的提示
diff --git a/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py b/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
index d0f4612..57e65bd 100644
--- a/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
+++ b/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
@@ -7,41 +7,41 @@ from oauth.models import OAuthUser
from oauth.oauthmanager import get_manager_by_type
-class Command(BaseCommand):
- help = 'sync user avatar'
+class Command(BaseCommand): #ZNY 定义同步用户头像命令类,继承BaseCommand
+ help = 'sync user avatar' #ZNY 命令帮助信息:同步用户头像
- def test_picture(self, url):
+ def test_picture(self, url): #ZNY 测试图片URL是否可访问方法
try:
- if requests.get(url, timeout=2).status_code == 200:
- return True
- except:
- pass
+ if requests.get(url, timeout=2).status_code == 200: #ZNY 发送HTTP请求测试图片URL,设置2秒超时
+ return True #ZNY 如果返回200状态码,返回True
+ except: #ZNY 捕获所有异常
+ pass #ZNY 发生异常时忽略
- def handle(self, *args, **options):
- static_url = static("../")
- users = OAuthUser.objects.all()
- self.stdout.write(f'开始同步{len(users)}个用户头像')
- for u in users:
- self.stdout.write(f'开始同步:{u.nickname}')
- url = u.picture
- if url:
- if url.startswith(static_url):
- if self.test_picture(url):
- continue
- else:
- if u.metadata:
- manage = get_manager_by_type(u.type)
- url = manage.get_picture(u.metadata)
- url = save_user_avatar(url)
- else:
- url = static('blog/img/avatar.png')
- else:
- url = save_user_avatar(url)
- else:
- url = static('blog/img/avatar.png')
- if url:
- self.stdout.write(
- f'结束同步:{u.nickname}.url:{url}')
- u.picture = url
- u.save()
- self.stdout.write('结束同步')
+ def handle(self, *args, **options): #ZNY 命令处理主方法
+ static_url = static("../") #ZNY 获取静态文件基础URL
+ users = OAuthUser.objects.all() #ZNY 获取所有OAuth用户
+ self.stdout.write(f'开始同步{len(users)}个用户头像') #ZNY 输出开始同步的提示,显示用户数量
+ for u in users: #ZNY 遍历所有用户
+ self.stdout.write(f'开始同步:{u.nickname}') #ZNY 输出开始同步当前用户的提示
+ url = u.picture #ZNY 获取用户当前头像URL
+ if url: #ZNY 如果头像URL存在
+ if url.startswith(static_url): #ZNY 如果头像URL是静态文件路径
+ if self.test_picture(url): #ZNY 测试静态图片是否可访问
+ continue #ZNY 如果可访问则跳过当前用户
+ else: #ZNY 如果静态图片不可访问
+ if u.metadata: #ZNY 如果用户有元数据
+ manage = get_manager_by_type(u.type) #ZNY 根据OAuth类型获取对应的管理器
+ url = manage.get_picture(u.metadata) #ZNY 从元数据中获取头像URL
+ url = save_user_avatar(url) #ZNY 保存用户头像并返回新的URL
+ else: #ZNY 如果没有元数据
+ url = static('blog/img/avatar.png') #ZNY 使用默认头像
+ else: #ZNY 如果头像URL不是静态文件路径
+ url = save_user_avatar(url) #ZNY 保存用户头像并返回新的URL
+ else: #ZNY 如果没有头像URL
+ url = static('blog/img/avatar.png') #ZNY 使用默认头像
+ if url: #ZNY 如果最终头像URL存在
+ self.stdout.write( #ZNY 输出同步完成提示
+ f'结束同步:{u.nickname}.url:{url}') #ZNY 显示用户名和最终头像URL
+ u.picture = url #ZNY 更新用户头像URL
+ u.save() #ZNY 保存用户信息
+ self.stdout.write('结束同步') #ZNY 输出同步结束提示
diff --git a/src/DjangoBlog-master/blog/middleware.py b/src/DjangoBlog-master/blog/middleware.py
index 94dd70c..4f427ee 100644
--- a/src/DjangoBlog-master/blog/middleware.py
+++ b/src/DjangoBlog-master/blog/middleware.py
@@ -1,42 +1,42 @@
-import logging
-import time
+import logging #ZNY 导入日志模块
+import time #ZNY 导入时间模块
-from ipware import get_client_ip
-from user_agents import parse
+from ipware import get_client_ip #ZNY 导入获取客户端IP的工具
+from user_agents import parse #ZNY 导入解析用户代理的工具
-from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager
+from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager #ZNY 导入Elasticsearch配置和性能文档管理器
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #ZNY 获取当前模块的日志记录器
-class OnlineMiddleware(object):
- def __init__(self, get_response=None):
- self.get_response = get_response
- super().__init__()
+class OnlineMiddleware(object): #ZNY 定义在线中间件类
+ def __init__(self, get_response=None): #ZNY 初始化方法
+ self.get_response = get_response #ZNY 存储get_response函数
+ super().__init__() #ZNY 调用父类初始化
- def __call__(self, request):
- ''' page render time '''
- start_time = time.time()
- response = self.get_response(request)
- http_user_agent = request.META.get('HTTP_USER_AGENT', '')
- ip, _ = get_client_ip(request)
- user_agent = parse(http_user_agent)
- if not response.streaming:
+ def __call__(self, request): #ZNY 使实例可调用,处理请求
+ ''' page render time ''' #ZNY 页面渲染时间统计
+ start_time = time.time() #ZNY 记录请求开始时间
+ response = self.get_response(request) #ZNY 调用后续中间件和视图处理请求
+ http_user_agent = request.META.get('HTTP_USER_AGENT', '') #ZNY 获取用户代理字符串
+ ip, _ = get_client_ip(request) #ZNY 获取客户端IP地址
+ user_agent = parse(http_user_agent) #ZNY 解析用户代理信息
+ if not response.streaming: #ZNY 如果不是流式响应
try:
- cast_time = time.time() - start_time
- if ELASTICSEARCH_ENABLED:
- time_taken = round((cast_time) * 1000, 2)
- url = request.path
- from django.utils import timezone
- ElaspedTimeDocumentManager.create(
- url=url,
- time_taken=time_taken,
- log_datetime=timezone.now(),
- useragent=user_agent,
- ip=ip)
- response.content = response.content.replace(
- b'', str.encode(str(cast_time)[:5]))
- except Exception as e:
- logger.error("Error OnlineMiddleware: %s" % e)
+ cast_time = time.time() - start_time #ZNY 计算总处理时间
+ if ELASTICSEARCH_ENABLED: #ZNY 如果Elasticsearch已启用
+ time_taken = round((cast_time) * 1000, 2) #ZNY 转换为毫秒并保留两位小数
+ url = request.path #ZNY 获取请求路径
+ from django.utils import timezone #ZNY 导入时区工具
+ ElaspedTimeDocumentManager.create( #ZNY 创建性能记录文档
+ url=url, #ZNY 请求URL
+ time_taken=time_taken, #ZNY 耗时
+ log_datetime=timezone.now(), #ZNY 当前时间
+ useragent=user_agent, #ZNY 用户代理信息
+ ip=ip) #ZNY IP地址
+ response.content = response.content.replace( #ZNY 在响应内容中替换占位符
+ b'', str.encode(str(cast_time)[:5])) #ZNY 将加载时间插入到指定位置
+ except Exception as e: #ZNY 捕获异常
+ logger.error("Error OnlineMiddleware: %s" % e) #ZNY 记录错误日志
- return response
+ return response #ZNY 返回响应对象
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/migrations/0001_initial.py b/src/DjangoBlog-master/blog/migrations/0001_initial.py
index 3d391b6..23a943e 100644
--- a/src/DjangoBlog-master/blog/migrations/0001_initial.py
+++ b/src/DjangoBlog-master/blog/migrations/0001_initial.py
@@ -12,126 +12,126 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL), #ZNY 依赖Django自带的用户模型
]
operations = [
migrations.CreateModel(
name='BlogSettings',
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('sitename', models.CharField(default='', max_length=200, verbose_name='网站名称')),
- ('site_description', models.TextField(default='', max_length=1000, verbose_name='网站描述')),
- ('site_seo_description', models.TextField(default='', max_length=1000, verbose_name='网站SEO描述')),
- ('site_keywords', models.TextField(default='', max_length=1000, verbose_name='网站关键字')),
- ('article_sub_length', models.IntegerField(default=300, verbose_name='文章摘要长度')),
- ('sidebar_article_count', models.IntegerField(default=10, verbose_name='侧边栏文章数目')),
- ('sidebar_comment_count', models.IntegerField(default=5, verbose_name='侧边栏评论数目')),
- ('article_comment_count', models.IntegerField(default=5, verbose_name='文章页面默认显示评论数目')),
- ('show_google_adsense', models.BooleanField(default=False, verbose_name='是否显示谷歌广告')),
- ('google_adsense_codes', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='广告内容')),
- ('open_site_comment', models.BooleanField(default=True, verbose_name='是否打开网站评论功能')),
- ('beiancode', models.CharField(blank=True, default='', max_length=2000, null=True, verbose_name='备案号')),
- ('analyticscode', models.TextField(default='', max_length=1000, verbose_name='网站统计代码')),
- ('show_gongan_code', models.BooleanField(default=False, verbose_name='是否显示公安备案号')),
- ('gongan_beiancode', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='公安备案号')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), #ZNY 主键,使用BigAutoField支持大数据量
+ ('sitename', models.CharField(default='', max_length=200, verbose_name='网站名称')), #ZNY 网站名称
+ ('site_description', models.TextField(default='', max_length=1000, verbose_name='网站描述')), #ZNY 网站描述
+ ('site_seo_description', models.TextField(default='', max_length=1000, verbose_name='网站SEO描述')), #ZNY SEO描述
+ ('site_keywords', models.TextField(default='', max_length=1000, verbose_name='网站关键字')), #ZNY 网站关键词
+ ('article_sub_length', models.IntegerField(default=300, verbose_name='文章摘要长度')), #ZNY 文章摘要长度
+ ('sidebar_article_count', models.IntegerField(default=10, verbose_name='侧边栏文章数目')), #ZNY 侧边栏文章数量
+ ('sidebar_comment_count', models.IntegerField(default=5, verbose_name='侧边栏评论数目')), #ZNY 侧边栏评论数量
+ ('article_comment_count', models.IntegerField(default=5, verbose_name='文章页面默认显示评论数目')), #ZNY 文章页评论显示数量
+ ('show_google_adsense', models.BooleanField(default=False, verbose_name='是否显示谷歌广告')), #ZNY 谷歌广告开关
+ ('google_adsense_codes', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='广告内容')), #ZNY 广告代码
+ ('open_site_comment', models.BooleanField(default=True, verbose_name='是否打开网站评论功能')), #ZNY 全站评论功能开关
+ ('beiancode', models.CharField(blank=True, default='', max_length=2000, null=True, verbose_name='备案号')), #ZNY ICP备案号
+ ('analyticscode', models.TextField(default='', max_length=1000, verbose_name='网站统计代码')), #ZNY 网站统计代码
+ ('show_gongan_code', models.BooleanField(default=False, verbose_name='是否显示公安备案号')), #ZNY 公安备案显示开关
+ ('gongan_beiancode', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='公安备案号')), #ZNY 公安备案号
],
options={
- 'verbose_name': '网站配置',
- 'verbose_name_plural': '网站配置',
+ 'verbose_name': '网站配置', #ZNY 单数名称
+ 'verbose_name_plural': '网站配置', #ZNY 复数名称
},
),
migrations.CreateModel(
name='Links',
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=30, unique=True, verbose_name='链接名称')),
- ('link', models.URLField(verbose_name='链接地址')),
- ('sequence', models.IntegerField(unique=True, verbose_name='排序')),
- ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
- ('show_type', models.CharField(choices=[('i', '首页'), ('l', '列表页'), ('p', '文章页面'), ('a', '全站'), ('s', '友情链接页面')], default='i', max_length=1, verbose_name='显示类型')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), #ZNY 主键
+ ('name', models.CharField(max_length=30, unique=True, verbose_name='链接名称')), #ZNY 链接名称,唯一
+ ('link', models.URLField(verbose_name='链接地址')), #ZNY 链接地址
+ ('sequence', models.IntegerField(unique=True, verbose_name='排序')), #ZNY 排序序号,唯一
+ ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), #ZNY 是否启用显示
+ ('show_type', models.CharField(choices=[('i', '首页'), ('l', '列表页'), ('p', '文章页面'), ('a', '全站'), ('s', '友情链接页面')], default='i', max_length=1, verbose_name='显示类型')), #ZNY 链接显示位置
+ ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), #ZNY 创建时间
+ ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), #ZNY 最后修改时间
],
options={
- 'verbose_name': '友情链接',
- 'verbose_name_plural': '友情链接',
- 'ordering': ['sequence'],
+ 'verbose_name': '友情链接', #ZNY 单数名称
+ 'verbose_name_plural': '友情链接', #ZNY 复数名称
+ 'ordering': ['sequence'], #ZNY 按排序字段升序排列
},
),
migrations.CreateModel(
name='SideBar',
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=100, verbose_name='标题')),
- ('content', models.TextField(verbose_name='内容')),
- ('sequence', models.IntegerField(unique=True, verbose_name='排序')),
- ('is_enable', models.BooleanField(default=True, verbose_name='是否启用')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), #ZNY 主键
+ ('name', models.CharField(max_length=100, verbose_name='标题')), #ZNY 侧边栏标题
+ ('content', models.TextField(verbose_name='内容')), #ZNY 侧边栏内容
+ ('sequence', models.IntegerField(unique=True, verbose_name='排序')), #ZNY 排序序号,唯一
+ ('is_enable', models.BooleanField(default=True, verbose_name='是否启用')), #ZNY 是否启用
+ ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), #ZNY 创建时间
+ ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), #ZNY 最后修改时间
],
options={
- 'verbose_name': '侧边栏',
- 'verbose_name_plural': '侧边栏',
- 'ordering': ['sequence'],
+ 'verbose_name': '侧边栏', #ZNY 单数名称
+ 'verbose_name_plural': '侧边栏', #ZNY 复数名称
+ 'ordering': ['sequence'], #ZNY 按排序字段升序排列
},
),
migrations.CreateModel(
name='Tag',
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('name', models.CharField(max_length=30, unique=True, verbose_name='标签名')),
- ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)),
+ ('id', models.AutoField(primary_key=True, serialize=False)), #ZNY 主键,使用AutoField
+ ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), #ZNY 创建时间
+ ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), #ZNY 最后修改时间
+ ('name', models.CharField(max_length=30, unique=True, verbose_name='标签名')), #ZNY 标签名称,唯一
+ ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)), #ZNY SEO友好的URL片段
],
options={
- 'verbose_name': '标签',
- 'verbose_name_plural': '标签',
- 'ordering': ['name'],
+ 'verbose_name': '标签', #ZNY 单数名称
+ 'verbose_name_plural': '标签', #ZNY 复数名称
+ 'ordering': ['name'], #ZNY 按标签名升序排列
},
),
migrations.CreateModel(
name='Category',
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('name', models.CharField(max_length=30, unique=True, verbose_name='分类名')),
- ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)),
- ('index', models.IntegerField(default=0, verbose_name='权重排序-越大越靠前')),
- ('parent_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='父级分类')),
+ ('id', models.AutoField(primary_key=True, serialize=False)), #ZNY 主键,使用AutoField
+ ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), #ZNY 创建时间
+ ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), #ZNY 最后修改时间
+ ('name', models.CharField(max_length=30, unique=True, verbose_name='分类名')), #ZNY 分类名称,唯一
+ ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)), #ZNY SEO友好的URL片段
+ ('index', models.IntegerField(default=0, verbose_name='权重排序-越大越靠前')), #ZNY 排序权重
+ ('parent_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='父级分类')), #ZNY 父级分类,支持多级分类
],
options={
- 'verbose_name': '分类',
- 'verbose_name_plural': '分类',
- 'ordering': ['-index'],
+ 'verbose_name': '分类', #ZNY 单数名称
+ 'verbose_name_plural': '分类', #ZNY 复数名称
+ 'ordering': ['-index'], #ZNY 按权重降序排列
},
),
migrations.CreateModel(
name='Article',
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('title', models.CharField(max_length=200, unique=True, verbose_name='标题')),
- ('body', mdeditor.fields.MDTextField(verbose_name='正文')),
- ('pub_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='发布时间')),
- ('status', models.CharField(choices=[('d', '草稿'), ('p', '发表')], default='p', max_length=1, verbose_name='文章状态')),
- ('comment_status', models.CharField(choices=[('o', '打开'), ('c', '关闭')], default='o', max_length=1, verbose_name='评论状态')),
- ('type', models.CharField(choices=[('a', '文章'), ('p', '页面')], default='a', max_length=1, verbose_name='类型')),
- ('views', models.PositiveIntegerField(default=0, verbose_name='浏览量')),
- ('article_order', models.IntegerField(default=0, verbose_name='排序,数字越大越靠前')),
- ('show_toc', models.BooleanField(default=False, verbose_name='是否显示toc目录')),
- ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
- ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类')),
- ('tags', models.ManyToManyField(blank=True, to='blog.tag', verbose_name='标签集合')),
+ ('id', models.AutoField(primary_key=True, serialize=False)), #ZNY 主键,使用AutoField
+ ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), #ZNY 创建时间
+ ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), #ZNY 最后修改时间
+ ('title', models.CharField(max_length=200, unique=True, verbose_name='标题')), #ZNY 文章标题,唯一
+ ('body', mdeditor.fields.MDTextField(verbose_name='正文')), #ZNY 正文,支持Markdown格式
+ ('pub_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='发布时间')), #ZNY 发布时间
+ ('status', models.CharField(choices=[('d', '草稿'), ('p', '发表')], default='p', max_length=1, verbose_name='文章状态')), #ZNY 文章状态:草稿或发表
+ ('comment_status', models.CharField(choices=[('o', '打开'), ('c', '关闭')], default='o', max_length=1, verbose_name='评论状态')), #ZNY 评论状态:打开或关闭
+ ('type', models.CharField(choices=[('a', '文章'), ('p', '页面')], default='a', max_length=1, verbose_name='类型')), #ZNY 内容类型:文章或页面
+ ('views', models.PositiveIntegerField(default=0, verbose_name='浏览量')), #ZNY 浏览量统计
+ ('article_order', models.IntegerField(default=0, verbose_name='排序,数字越大越靠前')), #ZNY 文章排序权重
+ ('show_toc', models.BooleanField(default=False, verbose_name='是否显示toc目录')), #ZNY 是否显示目录
+ ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), #ZNY 作者,关联用户模型
+ ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类')), #ZNY 分类,关联分类表
+ ('tags', models.ManyToManyField(blank=True, to='blog.tag', verbose_name='标签集合')), #ZNY 标签,多对多关系
],
options={
- 'verbose_name': '文章',
- 'verbose_name_plural': '文章',
- 'ordering': ['-article_order', '-pub_time'],
- 'get_latest_by': 'id',
+ 'verbose_name': '文章', #ZNY 单数名称
+ 'verbose_name_plural': '文章', #ZNY 复数名称
+ 'ordering': ['-article_order', '-pub_time'], #ZNY 先按排序权重降序,再按发布时间降序
+ 'get_latest_by': 'id', #ZNY 指定获取最新记录的依据字段
},
),
]
diff --git a/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py b/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
index adbaa36..8de8ad5 100644
--- a/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
+++ b/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
@@ -3,21 +3,24 @@
from django.db import migrations, models
+from django.db import migrations, models
+
+
class Migration(migrations.Migration):
dependencies = [
- ('blog', '0001_initial'),
+ ('blog', '0001_initial'), #ZNY 依赖于blog应用的初始迁移文件0001_initial
]
operations = [
migrations.AddField(
- model_name='blogsettings',
- name='global_footer',
- field=models.TextField(blank=True, default='', null=True, verbose_name='公共尾部'),
+ model_name='blogsettings', #ZNY 向BlogSettings模型添加字段
+ name='global_footer', #ZNY 字段名称为global_footer
+ field=models.TextField(blank=True, default='', null=True, verbose_name='公共尾部'), #ZNY 公共尾部字段,用于存储全站公共底部内容
),
migrations.AddField(
- model_name='blogsettings',
- name='global_header',
- field=models.TextField(blank=True, default='', null=True, verbose_name='公共头部'),
+ model_name='blogsettings', #ZNY 向BlogSettings模型添加字段
+ name='global_header', #ZNY 字段名称为global_header
+ field=models.TextField(blank=True, default='', null=True, verbose_name='公共头部'), #ZNY 公共头部字段,用于存储全站公共头部内容
),
- ]
+ ]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py b/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
index e9f5502..40241f4 100644
--- a/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
+++ b/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
@@ -5,13 +5,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('blog', '0002_blogsettings_global_footer_and_more'),
+ ('blog', '0002_blogsettings_global_footer_and_more'), #ZNY 依赖于blog应用的第二个迁移文件0002
]
operations = [
migrations.AddField(
- model_name='blogsettings',
- name='comment_need_review',
- field=models.BooleanField(default=False, verbose_name='评论是否需要审核'),
+ model_name='blogsettings', #ZNY 向BlogSettings模型添加新字段
+ name='comment_need_review', #ZNY 字段名称为comment_need_review
+ field=models.BooleanField(default=False, verbose_name='评论是否需要审核'), #ZNY 评论审核开关字段,控制评论是否需要审核才能显示
),
]
diff --git a/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py b/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
index ceb1398..88f4d5d 100644
--- a/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
+++ b/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
@@ -5,23 +5,23 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
- ('blog', '0003_blogsettings_comment_need_review'),
+ ('blog', '0003_blogsettings_comment_need_review'), #ZNY 依赖于blog应用的第三个迁移文件0003
]
operations = [
migrations.RenameField(
- model_name='blogsettings',
- old_name='analyticscode',
- new_name='analytics_code',
+ model_name='blogsettings', #ZNY 指定要修改的模型为BlogSettings
+ old_name='analyticscode', #ZNY 原字段名称为analyticscode
+ new_name='analytics_code', #ZNY 新字段名称为analytics_code,修改为更规范的命名
),
migrations.RenameField(
- model_name='blogsettings',
- old_name='beiancode',
- new_name='beian_code',
+ model_name='blogsettings', #ZNY 指定要修改的模型为BlogSettings
+ old_name='beiancode', #ZNY 原字段名称为beiancode
+ new_name='beian_code', #ZNY 新字段名称为beian_code,修改为更规范的命名
),
migrations.RenameField(
- model_name='blogsettings',
- old_name='sitename',
- new_name='site_name',
+ model_name='blogsettings', #ZNY 指定要修改的模型为BlogSettings
+ old_name='sitename', #ZNY 原字段名称为sitename
+ new_name='site_name', #ZNY 新字段名称为site_name,修改为更规范的命名
),
- ]
+ ]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py b/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
index d08e853..a227e17 100644
--- a/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
+++ b/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
@@ -10,291 +10,291 @@ import mdeditor.fields
class Migration(migrations.Migration):
dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('blog', '0004_rename_analyticscode_blogsettings_analytics_code_and_more'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL), #ZNY 依赖Django用户模型
+ ('blog', '0004_rename_analyticscode_blogsettings_analytics_code_and_more'), #ZNY 依赖于blog应用的第四个迁移文件
]
operations = [
migrations.AlterModelOptions(
name='article',
- options={'get_latest_by': 'id', 'ordering': ['-article_order', '-pub_time'], 'verbose_name': 'article', 'verbose_name_plural': 'article'},
+ options={'get_latest_by': 'id', 'ordering': ['-article_order', '-pub_time'], 'verbose_name': 'article', 'verbose_name_plural': 'article'}, #ZNY 修改Article模型的选项,将中文改为英文
),
migrations.AlterModelOptions(
name='category',
- options={'ordering': ['-index'], 'verbose_name': 'category', 'verbose_name_plural': 'category'},
+ options={'ordering': ['-index'], 'verbose_name': 'category', 'verbose_name_plural': 'category'}, #ZNY 修改Category模型的选项,将中文改为英文
),
migrations.AlterModelOptions(
name='links',
- options={'ordering': ['sequence'], 'verbose_name': 'link', 'verbose_name_plural': 'link'},
+ options={'ordering': ['sequence'], 'verbose_name': 'link', 'verbose_name_plural': 'link'}, #ZNY 修改Links模型的选项,将中文改为英文
),
migrations.AlterModelOptions(
name='sidebar',
- options={'ordering': ['sequence'], 'verbose_name': 'sidebar', 'verbose_name_plural': 'sidebar'},
+ options={'ordering': ['sequence'], 'verbose_name': 'sidebar', 'verbose_name_plural': 'sidebar'}, #ZNY 修改Sidebar模型的选项,将中文改为英文
),
migrations.AlterModelOptions(
name='tag',
- options={'ordering': ['name'], 'verbose_name': 'tag', 'verbose_name_plural': 'tag'},
+ options={'ordering': ['name'], 'verbose_name': 'tag', 'verbose_name_plural': 'tag'}, #ZNY 修改Tag模型的选项,将中文改为英文
),
migrations.RemoveField(
model_name='article',
- name='created_time',
+ name='created_time', #ZNY 删除Article模型的created_time字段
),
migrations.RemoveField(
model_name='article',
- name='last_mod_time',
+ name='last_mod_time', #ZNY 删除Article模型的last_mod_time字段
),
migrations.RemoveField(
model_name='category',
- name='created_time',
+ name='created_time', #ZNY 删除Category模型的created_time字段
),
migrations.RemoveField(
model_name='category',
- name='last_mod_time',
+ name='last_mod_time', #ZNY 删除Category模型的last_mod_time字段
),
migrations.RemoveField(
model_name='links',
- name='created_time',
+ name='created_time', #ZNY 删除Links模型的created_time字段
),
migrations.RemoveField(
model_name='sidebar',
- name='created_time',
+ name='created_time', #ZNY 删除Sidebar模型的created_time字段
),
migrations.RemoveField(
model_name='tag',
- name='created_time',
+ name='created_time', #ZNY 删除Tag模型的created_time字段
),
migrations.RemoveField(
model_name='tag',
- name='last_mod_time',
+ name='last_mod_time', #ZNY 删除Tag模型的last_mod_time字段
),
migrations.AddField(
model_name='article',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
+ name='creation_time', #ZNY 向Article模型添加creation_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), #ZNY 创建时间字段,英文标签
),
migrations.AddField(
model_name='article',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
+ name='last_modify_time', #ZNY 向Article模型添加last_modify_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), #ZNY 最后修改时间字段,英文标签
),
migrations.AddField(
model_name='category',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
+ name='creation_time', #ZNY 向Category模型添加creation_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), #ZNY 创建时间字段,英文标签
),
migrations.AddField(
model_name='category',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
+ name='last_modify_time', #ZNY 向Category模型添加last_modify_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), #ZNY 最后修改时间字段,英文标签
),
migrations.AddField(
model_name='links',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
+ name='creation_time', #ZNY 向Links模型添加creation_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), #ZNY 创建时间字段,英文标签
),
migrations.AddField(
model_name='sidebar',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
+ name='creation_time', #ZNY 向Sidebar模型添加creation_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), #ZNY 创建时间字段,英文标签
),
migrations.AddField(
model_name='tag',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
+ name='creation_time', #ZNY 向Tag模型添加creation_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), #ZNY 创建时间字段,英文标签
),
migrations.AddField(
model_name='tag',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
+ name='last_modify_time', #ZNY 向Tag模型添加last_modify_time字段
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), #ZNY 最后修改时间字段,英文标签
),
migrations.AlterField(
model_name='article',
name='article_order',
- field=models.IntegerField(default=0, verbose_name='order'),
+ field=models.IntegerField(default=0, verbose_name='order'), #ZNY 修改Article模型的article_order字段标签为英文
),
migrations.AlterField(
model_name='article',
name='author',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), #ZNY 修改Article模型的author字段标签为英文
),
migrations.AlterField(
model_name='article',
name='body',
- field=mdeditor.fields.MDTextField(verbose_name='body'),
+ field=mdeditor.fields.MDTextField(verbose_name='body'), #ZNY 修改Article模型的body字段标签为英文
),
migrations.AlterField(
model_name='article',
name='category',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='category'),
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='category'), #ZNY 修改Article模型的category字段标签为英文
),
migrations.AlterField(
model_name='article',
name='comment_status',
- field=models.CharField(choices=[('o', 'Open'), ('c', 'Close')], default='o', max_length=1, verbose_name='comment status'),
+ field=models.CharField(choices=[('o', 'Open'), ('c', 'Close')], default='o', max_length=1, verbose_name='comment status'), #ZNY 修改Article模型的comment_status字段选项和标签为英文
),
migrations.AlterField(
model_name='article',
name='pub_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='publish time'),
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='publish time'), #ZNY 修改Article模型的pub_time字段标签为英文
),
migrations.AlterField(
model_name='article',
name='show_toc',
- field=models.BooleanField(default=False, verbose_name='show toc'),
+ field=models.BooleanField(default=False, verbose_name='show toc'), #ZNY 修改Article模型的show_toc字段标签为英文
),
migrations.AlterField(
model_name='article',
name='status',
- field=models.CharField(choices=[('d', 'Draft'), ('p', 'Published')], default='p', max_length=1, verbose_name='status'),
+ field=models.CharField(choices=[('d', 'Draft'), ('p', 'Published')], default='p', max_length=1, verbose_name='status'), #ZNY 修改Article模型的status字段选项和标签为英文
),
migrations.AlterField(
model_name='article',
name='tags',
- field=models.ManyToManyField(blank=True, to='blog.tag', verbose_name='tag'),
+ field=models.ManyToManyField(blank=True, to='blog.tag', verbose_name='tag'), #ZNY 修改Article模型的tags字段标签为英文
),
migrations.AlterField(
model_name='article',
name='title',
- field=models.CharField(max_length=200, unique=True, verbose_name='title'),
+ field=models.CharField(max_length=200, unique=True, verbose_name='title'), #ZNY 修改Article模型的title字段标签为英文
),
migrations.AlterField(
model_name='article',
name='type',
- field=models.CharField(choices=[('a', 'Article'), ('p', 'Page')], default='a', max_length=1, verbose_name='type'),
+ field=models.CharField(choices=[('a', 'Article'), ('p', 'Page')], default='a', max_length=1, verbose_name='type'), #ZNY 修改Article模型的type字段选项和标签为英文
),
migrations.AlterField(
model_name='article',
name='views',
- field=models.PositiveIntegerField(default=0, verbose_name='views'),
+ field=models.PositiveIntegerField(default=0, verbose_name='views'), #ZNY 修改Article模型的views字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='article_comment_count',
- field=models.IntegerField(default=5, verbose_name='article comment count'),
+ field=models.IntegerField(default=5, verbose_name='article comment count'), #ZNY 修改BlogSettings模型的article_comment_count字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='article_sub_length',
- field=models.IntegerField(default=300, verbose_name='article sub length'),
+ field=models.IntegerField(default=300, verbose_name='article sub length'), #ZNY 修改BlogSettings模型的article_sub_length字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='google_adsense_codes',
- field=models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='adsense code'),
+ field=models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='adsense code'), #ZNY 修改BlogSettings模型的google_adsense_codes字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='open_site_comment',
- field=models.BooleanField(default=True, verbose_name='open site comment'),
+ field=models.BooleanField(default=True, verbose_name='open site comment'), #ZNY 修改BlogSettings模型的open_site_comment字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='show_google_adsense',
- field=models.BooleanField(default=False, verbose_name='show adsense'),
+ field=models.BooleanField(default=False, verbose_name='show adsense'), #ZNY 修改BlogSettings模型的show_google_adsense字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='sidebar_article_count',
- field=models.IntegerField(default=10, verbose_name='sidebar article count'),
+ field=models.IntegerField(default=10, verbose_name='sidebar article count'), #ZNY 修改BlogSettings模型的sidebar_article_count字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='sidebar_comment_count',
- field=models.IntegerField(default=5, verbose_name='sidebar comment count'),
+ field=models.IntegerField(default=5, verbose_name='sidebar comment count'), #ZNY 修改BlogSettings模型的sidebar_comment_count字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='site_description',
- field=models.TextField(default='', max_length=1000, verbose_name='site description'),
+ field=models.TextField(default='', max_length=1000, verbose_name='site description'), #ZNY 修改BlogSettings模型的site_description字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='site_keywords',
- field=models.TextField(default='', max_length=1000, verbose_name='site keywords'),
+ field=models.TextField(default='', max_length=1000, verbose_name='site keywords'), #ZNY 修改BlogSettings模型的site_keywords字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='site_name',
- field=models.CharField(default='', max_length=200, verbose_name='site name'),
+ field=models.CharField(default='', max_length=200, verbose_name='site name'), #ZNY 修改BlogSettings模型的site_name字段标签为英文
),
migrations.AlterField(
model_name='blogsettings',
name='site_seo_description',
- field=models.TextField(default='', max_length=1000, verbose_name='site seo description'),
+ field=models.TextField(default='', max_length=1000, verbose_name='site seo description'), #ZNY 修改BlogSettings模型的site_seo_description字段标签为英文
),
migrations.AlterField(
model_name='category',
name='index',
- field=models.IntegerField(default=0, verbose_name='index'),
+ field=models.IntegerField(default=0, verbose_name='index'), #ZNY 修改Category模型的index字段标签为英文
),
migrations.AlterField(
model_name='category',
name='name',
- field=models.CharField(max_length=30, unique=True, verbose_name='category name'),
+ field=models.CharField(max_length=30, unique=True, verbose_name='category name'), #ZNY 修改Category模型的name字段标签为英文
),
migrations.AlterField(
model_name='category',
name='parent_category',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='parent category'),
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='parent category'), #ZNY 修改Category模型的parent_category字段标签为英文
),
migrations.AlterField(
model_name='links',
name='is_enable',
- field=models.BooleanField(default=True, verbose_name='is show'),
+ field=models.BooleanField(default=True, verbose_name='is show'), #ZNY 修改Links模型的is_enable字段标签为英文
),
migrations.AlterField(
model_name='links',
name='last_mod_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), #ZNY 修改Links模型的last_mod_time字段标签为英文
),
migrations.AlterField(
model_name='links',
name='link',
- field=models.URLField(verbose_name='link'),
+ field=models.URLField(verbose_name='link'), #ZNY 修改Links模型的link字段标签为英文
),
migrations.AlterField(
model_name='links',
name='name',
- field=models.CharField(max_length=30, unique=True, verbose_name='link name'),
+ field=models.CharField(max_length=30, unique=True, verbose_name='link name'), #ZNY 修改Links模型的name字段标签为英文
),
migrations.AlterField(
model_name='links',
name='sequence',
- field=models.IntegerField(unique=True, verbose_name='order'),
+ field=models.IntegerField(unique=True, verbose_name='order'), #ZNY 修改Links模型的sequence字段标签为英文
),
migrations.AlterField(
model_name='links',
name='show_type',
- field=models.CharField(choices=[('i', 'index'), ('l', 'list'), ('p', 'post'), ('a', 'all'), ('s', 'slide')], default='i', max_length=1, verbose_name='show type'),
+ field=models.CharField(choices=[('i', 'index'), ('l', 'list'), ('p', 'post'), ('a', 'all'), ('s', 'slide')], default='i', max_length=1, verbose_name='show type'), #ZNY 修改Links模型的show_type字段选项和标签为英文
),
migrations.AlterField(
model_name='sidebar',
name='content',
- field=models.TextField(verbose_name='content'),
+ field=models.TextField(verbose_name='content'), #ZNY 修改Sidebar模型的content字段标签为英文
),
migrations.AlterField(
model_name='sidebar',
name='is_enable',
- field=models.BooleanField(default=True, verbose_name='is enable'),
+ field=models.BooleanField(default=True, verbose_name='is enable'), #ZNY 修改Sidebar模型的is_enable字段标签为英文
),
migrations.AlterField(
model_name='sidebar',
name='last_mod_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
+ field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), #ZNY 修改Sidebar模型的last_mod_time字段标签为英文
),
migrations.AlterField(
model_name='sidebar',
name='name',
- field=models.CharField(max_length=100, verbose_name='title'),
+ field=models.CharField(max_length=100, verbose_name='title'), #ZNY 修改Sidebar模型的name字段标签为英文
),
migrations.AlterField(
model_name='sidebar',
name='sequence',
- field=models.IntegerField(unique=True, verbose_name='order'),
+ field=models.IntegerField(unique=True, verbose_name='order'), #ZNY 修改Sidebar模型的sequence字段标签为英文
),
migrations.AlterField(
model_name='tag',
name='name',
- field=models.CharField(max_length=30, unique=True, verbose_name='tag name'),
+ field=models.CharField(max_length=30, unique=True, verbose_name='tag name'), #ZNY 修改Tag模型的name字段标签为英文
),
]
diff --git a/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py b/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
index e36feb4..3cc9c73 100644
--- a/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
+++ b/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
@@ -6,12 +6,12 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
- ('blog', '0005_alter_article_options_alter_category_options_and_more'),
+ ('blog', '0005_alter_article_options_alter_category_options_and_more'), #ZNY 依赖于blog应用的第五个迁移文件0005
]
operations = [
migrations.AlterModelOptions(
- name='blogsettings',
- options={'verbose_name': 'Website configuration', 'verbose_name_plural': 'Website configuration'},
+ name='blogsettings', #ZNY 修改BlogSettings模型的选项配置
+ options={'verbose_name': 'Website configuration', 'verbose_name_plural': 'Website configuration'}, #ZNY 将BlogSettings模型的显示名称从中文改为英文
),
- ]
+ ]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/models.py b/src/DjangoBlog-master/blog/models.py
index 083788b..3f63aad 100644
--- a/src/DjangoBlog-master/blog/models.py
+++ b/src/DjangoBlog-master/blog/models.py
@@ -14,363 +14,363 @@ from uuslug import slugify
from djangoblog.utils import cache_decorator, cache
from djangoblog.utils import get_current_site
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #ZNY 获取当前模块的日志记录器
-class LinkShowType(models.TextChoices):
- I = ('i', _('index'))
- L = ('l', _('list'))
- P = ('p', _('post'))
- A = ('a', _('all'))
- S = ('s', _('slide'))
+class LinkShowType(models.TextChoices): #ZNY 定义链接显示类型枚举类
+ I = ('i', _('index')) #ZNY 首页显示
+ L = ('l', _('list')) #ZNY 列表页显示
+ P = ('p', _('post')) #ZNY 文章页面显示
+ A = ('a', _('all')) #ZNY 全站显示
+ S = ('s', _('slide')) #ZNY 友情链接页面显示
-class BaseModel(models.Model):
- id = models.AutoField(primary_key=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_modify_time = models.DateTimeField(_('modify time'), default=now)
+class BaseModel(models.Model): #ZNY 定义基础模型抽象类
+ id = models.AutoField(primary_key=True) #ZNY 自增主键字段
+ creation_time = models.DateTimeField(_('creation time'), default=now) #ZNY 创建时间字段
+ last_modify_time = models.DateTimeField(_('modify time'), default=now) #ZNY 最后修改时间字段
- def save(self, *args, **kwargs):
- is_update_views = isinstance(
+ def save(self, *args, **kwargs): #ZNY 重写保存方法
+ is_update_views = isinstance( #ZNY 检查是否是更新文章浏览量的操作
self,
Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views']
- if is_update_views:
- Article.objects.filter(pk=self.pk).update(views=self.views)
- else:
- if 'slug' in self.__dict__:
- slug = getattr(
+ if is_update_views: #ZNY 如果是更新浏览量
+ Article.objects.filter(pk=self.pk).update(views=self.views) #ZNY 直接更新浏览量字段
+ else: #ZNY 如果不是更新浏览量
+ if 'slug' in self.__dict__: #ZNY 如果模型有slug字段
+ slug = getattr( #ZNY 获取标题或名称作为slug源
self, 'title') if 'title' in self.__dict__ else getattr(
self, 'name')
- setattr(self, 'slug', slugify(slug))
- super().save(*args, **kwargs)
+ setattr(self, 'slug', slugify(slug)) #ZNY 生成并设置slug字段
+ super().save(*args, **kwargs) #ZNY 调用父类保存方法
- def get_full_url(self):
- site = get_current_site().domain
- url = "https://{site}{path}".format(site=site,
+ def get_full_url(self): #ZNY 获取完整URL方法
+ site = get_current_site().domain #ZNY 获取当前站点域名
+ url = "https://{site}{path}".format(site=site, #ZNY 构建完整URL
path=self.get_absolute_url())
- return url
+ return url #ZNY 返回完整URL
class Meta:
- abstract = True
+ abstract = True #ZNY 设置为抽象基类,不创建数据库表
- @abstractmethod
- def get_absolute_url(self):
- pass
+ @abstractmethod #ZNY 抽象方法,子类必须实现
+ def get_absolute_url(self): #ZNY 获取绝对URL方法
+ pass #ZNY 由子类具体实现
-class Article(BaseModel):
- """文章"""
- STATUS_CHOICES = (
- ('d', _('Draft')),
- ('p', _('Published')),
+class Article(BaseModel): #ZNY 定义文章模型,继承基础模型
+ """文章""" #ZNY 文章模型文档字符串
+ STATUS_CHOICES = ( #ZNY 文章状态选项
+ ('d', _('Draft')), #ZNY 草稿状态
+ ('p', _('Published')), #ZNY 发布状态
)
- COMMENT_STATUS = (
- ('o', _('Open')),
- ('c', _('Close')),
+ COMMENT_STATUS = ( #ZNY 评论状态选项
+ ('o', _('Open')), #ZNY 开放评论
+ ('c', _('Close')), #ZNY 关闭评论
)
- TYPE = (
- ('a', _('Article')),
- ('p', _('Page')),
+ TYPE = ( #ZNY 文章类型选项
+ ('a', _('Article')), #ZNY 普通文章
+ ('p', _('Page')), #ZNY 页面类型
)
- title = models.CharField(_('title'), max_length=200, unique=True)
- body = MDTextField(_('body'))
- pub_time = models.DateTimeField(
+ title = models.CharField(_('title'), max_length=200, unique=True) #ZNY 文章标题,唯一
+ body = MDTextField(_('body')) #ZNY 文章正文,使用Markdown字段
+ pub_time = models.DateTimeField( #ZNY 发布时间字段
_('publish time'), blank=False, null=False, default=now)
- status = models.CharField(
+ status = models.CharField( #ZNY 文章状态字段
_('status'),
max_length=1,
choices=STATUS_CHOICES,
- default='p')
- comment_status = models.CharField(
+ default='p') #ZNY 默认发布状态
+ comment_status = models.CharField( #ZNY 评论状态字段
_('comment status'),
max_length=1,
choices=COMMENT_STATUS,
- default='o')
- type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a')
- views = models.PositiveIntegerField(_('views'), default=0)
- author = models.ForeignKey(
+ default='o') #ZNY 默认开放评论
+ type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a') #ZNY 文章类型,默认普通文章
+ views = models.PositiveIntegerField(_('views'), default=0) #ZNY 浏览量统计字段
+ author = models.ForeignKey( #ZNY 作者外键字段
settings.AUTH_USER_MODEL,
verbose_name=_('author'),
blank=False,
null=False,
- on_delete=models.CASCADE)
- article_order = models.IntegerField(
- _('order'), blank=False, null=False, default=0)
- show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False)
- category = models.ForeignKey(
+ on_delete=models.CASCADE) #ZNY 级联删除
+ article_order = models.IntegerField( #ZNY 文章排序字段
+ _('order'), blank=False, null=False, default=0) #ZNY 数字越大越靠前
+ show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) #ZNY 是否显示目录
+ category = models.ForeignKey( #ZNY 分类外键字段
'Category',
verbose_name=_('category'),
on_delete=models.CASCADE,
blank=False,
- null=False)
- tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True)
+ null=False) #ZNY 必须选择分类
+ tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) #ZNY 标签多对多关系
- def body_to_string(self):
- return self.body
+ def body_to_string(self): #ZNY 获取正文字符串方法
+ return self.body #ZNY 返回正文内容
- def __str__(self):
- return self.title
+ def __str__(self): #ZNY 字符串表示方法
+ return self.title #ZNY 返回文章标题
class Meta:
- ordering = ['-article_order', '-pub_time']
- verbose_name = _('article')
- verbose_name_plural = verbose_name
- get_latest_by = 'id'
+ ordering = ['-article_order', '-pub_time'] #ZNY 默认按排序和发布时间降序排列
+ verbose_name = _('article') #ZNY 单数显示名称
+ verbose_name_plural = verbose_name #ZNY 复数显示名称
+ get_latest_by = 'id' #ZNY 获取最新记录的依据字段
- def get_absolute_url(self):
- return reverse('blog:detailbyid', kwargs={
+ def get_absolute_url(self): #ZNY 实现获取绝对URL方法
+ return reverse('blog:detailbyid', kwargs={ #ZNY 生成文章详情页URL
'article_id': self.id,
'year': self.creation_time.year,
'month': self.creation_time.month,
'day': self.creation_time.day
})
- @cache_decorator(60 * 60 * 10)
- def get_category_tree(self):
- tree = self.category.get_category_tree()
- names = list(map(lambda c: (c.name, c.get_absolute_url()), tree))
-
- return names
-
- def save(self, *args, **kwargs):
- super().save(*args, **kwargs)
-
- def viewed(self):
- self.views += 1
- self.save(update_fields=['views'])
-
- def comment_list(self):
- cache_key = 'article_comments_{id}'.format(id=self.id)
- value = cache.get(cache_key)
- if value:
- logger.info('get article comments:{id}'.format(id=self.id))
- return value
- else:
- comments = self.comment_set.filter(is_enable=True).order_by('-id')
- cache.set(cache_key, comments, 60 * 100)
- logger.info('set article comments:{id}'.format(id=self.id))
- return comments
-
- def get_admin_url(self):
- info = (self._meta.app_label, self._meta.model_name)
- return reverse('admin:%s_%s_change' % info, args=(self.pk,))
-
- @cache_decorator(expiration=60 * 100)
- def next_article(self):
+ @cache_decorator(60 * 60 * 10) #ZNY 缓存装饰器,缓存10小时
+ def get_category_tree(self): #ZNY 获取分类树方法
+ tree = self.category.get_category_tree() #ZNY 获取分类的层级树
+ names = list(map(lambda c: (c.name, c.get_absolute_url()), tree)) #ZNY 转换为名称和URL的元组列表
+
+ return names #ZNY 返回分类树列表
+
+ def save(self, *args, **kwargs): #ZNY 重写保存方法
+ super().save(*args, **kwargs) #ZNY 调用父类保存方法
+
+ def viewed(self): #ZNY 增加浏览量方法
+ self.views += 1 #ZNY 浏览量加1
+ self.save(update_fields=['views']) #ZNY 只更新浏览量字段
+
+ def comment_list(self): #ZNY 获取评论列表方法
+ cache_key = 'article_comments_{id}'.format(id=self.id) #ZNY 生成评论缓存键
+ value = cache.get(cache_key) #ZNY 尝试从缓存获取评论
+ if value: #ZNY 如果缓存存在
+ logger.info('get article comments:{id}'.format(id=self.id)) #ZNY 记录获取缓存日志
+ return value #ZNY 返回缓存评论
+ else: #ZNY 如果缓存不存在
+ comments = self.comment_set.filter(is_enable=True).order_by('-id') #ZNY 从数据库获取启用的评论
+ cache.set(cache_key, comments, 60 * 100) #ZNY 设置缓存,有效期100分钟
+ logger.info('set article comments:{id}'.format(id=self.id)) #ZNY 记录设置缓存日志
+ return comments #ZNY 返回评论列表
+
+ def get_admin_url(self): #ZNY 获取管理后台URL方法
+ info = (self._meta.app_label, self._meta.model_name) #ZNY 获取应用和模型名称
+ return reverse('admin:%s_%s_change' % info, args=(self.pk,)) #ZNY 生成管理后台编辑URL
+
+ @cache_decorator(expiration=60 * 100) #ZNY 缓存装饰器,缓存100分钟
+ def next_article(self): #ZNY 获取下一篇文章方法
# 下一篇
- return Article.objects.filter(
- id__gt=self.id, status='p').order_by('id').first()
+ return Article.objects.filter( #ZNY 查询ID大于当前文章且已发布的文章
+ id__gt=self.id, status='p').order_by('id').first() #ZNY 按ID升序取第一篇
- @cache_decorator(expiration=60 * 100)
- def prev_article(self):
+ @cache_decorator(expiration=60 * 100) #ZNY 缓存装饰器,缓存100分钟
+ def prev_article(self): #ZNY 获取上一篇文章方法
# 前一篇
- return Article.objects.filter(id__lt=self.id, status='p').first()
+ return Article.objects.filter(id__lt=self.id, status='p').first() #ZNY 查询ID小于当前文章且已发布的文章
- def get_first_image_url(self):
+ def get_first_image_url(self): #ZNY 获取文章首张图片URL方法
"""
Get the first image url from article.body.
:return:
"""
- match = re.search(r'!\[.*?\]\((.+?)\)', self.body)
- if match:
- return match.group(1)
- return ""
+ match = re.search(r'!\[.*?\]\((.+?)\)', self.body) #ZNY 使用正则匹配Markdown图片语法
+ if match: #ZNY 如果匹配到图片
+ return match.group(1) #ZNY 返回图片URL
+ return "" #ZNY 没有图片返回空字符串
-class Category(BaseModel):
- """文章分类"""
- name = models.CharField(_('category name'), max_length=30, unique=True)
- parent_category = models.ForeignKey(
+class Category(BaseModel): #ZNY 定义分类模型,继承基础模型
+ """文章分类""" #ZNY 分类模型文档字符串
+ name = models.CharField(_('category name'), max_length=30, unique=True) #ZNY 分类名称,唯一
+ parent_category = models.ForeignKey( #ZNY 父级分类自关联字段
'self',
verbose_name=_('parent category'),
blank=True,
null=True,
- on_delete=models.CASCADE)
- slug = models.SlugField(default='no-slug', max_length=60, blank=True)
- index = models.IntegerField(default=0, verbose_name=_('index'))
+ on_delete=models.CASCADE) #ZNY 支持多级分类
+ slug = models.SlugField(default='no-slug', max_length=60, blank=True) #ZNY URL友好的分类名
+ index = models.IntegerField(default=0, verbose_name=_('index')) #ZNY 排序索引,越大越靠前
class Meta:
- ordering = ['-index']
- verbose_name = _('category')
- verbose_name_plural = verbose_name
+ ordering = ['-index'] #ZNY 按索引降序排列
+ verbose_name = _('category') #ZNY 单数显示名称
+ verbose_name_plural = verbose_name #ZNY 复数显示名称
- def get_absolute_url(self):
- return reverse(
+ def get_absolute_url(self): #ZNY 实现获取绝对URL方法
+ return reverse( #ZNY 生成分类详情页URL
'blog:category_detail', kwargs={
'category_name': self.slug})
- def __str__(self):
- return self.name
+ def __str__(self): #ZNY 字符串表示方法
+ return self.name #ZNY 返回分类名称
- @cache_decorator(60 * 60 * 10)
- def get_category_tree(self):
+ @cache_decorator(60 * 60 * 10) #ZNY 缓存装饰器,缓存10小时
+ def get_category_tree(self): #ZNY 获取分类树方法
"""
递归获得分类目录的父级
:return:
"""
- categorys = []
+ categorys = [] #ZNY 初始化分类列表
- def parse(category):
- categorys.append(category)
- if category.parent_category:
- parse(category.parent_category)
+ def parse(category): #ZNY 递归解析函数
+ categorys.append(category) #ZNY 添加当前分类到列表
+ if category.parent_category: #ZNY 如果存在父级分类
+ parse(category.parent_category) #ZNY 递归解析父级分类
- parse(self)
- return categorys
+ parse(self) #ZNY 从当前分类开始解析
+ return categorys #ZNY 返回分类树列表
- @cache_decorator(60 * 60 * 10)
- def get_sub_categorys(self):
+ @cache_decorator(60 * 60 * 10) #ZNY 缓存装饰器,缓存10小时
+ def get_sub_categorys(self): #ZNY 获取所有子分类方法
"""
获得当前分类目录所有子集
:return:
"""
- categorys = []
- all_categorys = Category.objects.all()
+ categorys = [] #ZNY 初始化子分类列表
+ all_categorys = Category.objects.all() #ZNY 获取所有分类
- def parse(category):
- if category not in categorys:
- categorys.append(category)
- childs = all_categorys.filter(parent_category=category)
- for child in childs:
- if category not in categorys:
- categorys.append(child)
- parse(child)
+ def parse(category): #ZNY 递归解析函数
+ if category not in categorys: #ZNY 如果分类不在列表中
+ categorys.append(category) #ZNY 添加当前分类
+ childs = all_categorys.filter(parent_category=category) #ZNY 获取直接子分类
+ for child in childs: #ZNY 遍历子分类
+ if category not in categorys: #ZNY 如果子分类不在列表中
+ categorys.append(child) #ZNY 添加子分类
+ parse(child) #ZNY 递归解析子分类的子分类
- parse(self)
- return categorys
+ parse(self) #ZNY 从当前分类开始解析
+ return categorys #ZNY 返回所有子分类列表
-class Tag(BaseModel):
- """文章标签"""
- name = models.CharField(_('tag name'), max_length=30, unique=True)
- slug = models.SlugField(default='no-slug', max_length=60, blank=True)
+class Tag(BaseModel): #ZNY 定义标签模型,继承基础模型
+ """文章标签""" #ZNY 标签模型文档字符串
+ name = models.CharField(_('tag name'), max_length=30, unique=True) #ZNY 标签名称,唯一
+ slug = models.SlugField(default='no-slug', max_length=60, blank=True) #ZNY URL友好的标签名
- def __str__(self):
- return self.name
+ def __str__(self): #ZNY 字符串表示方法
+ return self.name #ZNY 返回标签名称
- def get_absolute_url(self):
- return reverse('blog:tag_detail', kwargs={'tag_name': self.slug})
+ def get_absolute_url(self): #ZNY 实现获取绝对URL方法
+ return reverse('blog:tag_detail', kwargs={'tag_name': self.slug}) #ZNY 生成标签详情页URL
- @cache_decorator(60 * 60 * 10)
- def get_article_count(self):
- return Article.objects.filter(tags__name=self.name).distinct().count()
+ @cache_decorator(60 * 60 * 10) #ZNY 缓存装饰器,缓存10小时
+ def get_article_count(self): #ZNY 获取标签下文章数量方法
+ return Article.objects.filter(tags__name=self.name).distinct().count() #ZNY 统计使用该标签的文章数量
class Meta:
- ordering = ['name']
- verbose_name = _('tag')
- verbose_name_plural = verbose_name
+ ordering = ['name'] #ZNY 按名称升序排列
+ verbose_name = _('tag') #ZNY 单数显示名称
+ verbose_name_plural = verbose_name #ZNY 复数显示名称
-class Links(models.Model):
- """友情链接"""
+class Links(models.Model): #ZNY 定义友情链接模型
+ """友情链接""" #ZNY 友情链接模型文档字符串
- name = models.CharField(_('link name'), max_length=30, unique=True)
- link = models.URLField(_('link'))
- sequence = models.IntegerField(_('order'), unique=True)
- is_enable = models.BooleanField(
- _('is show'), default=True, blank=False, null=False)
- show_type = models.CharField(
+ name = models.CharField(_('link name'), max_length=30, unique=True) #ZNY 链接名称,唯一
+ link = models.URLField(_('link')) #ZNY 链接地址
+ sequence = models.IntegerField(_('order'), unique=True) #ZNY 排序序号,唯一
+ is_enable = models.BooleanField( #ZNY 是否启用显示字段
+ _('is show'), default=True, blank=False, null=False) #ZNY 默认启用
+ show_type = models.CharField( #ZNY 显示类型字段
_('show type'),
max_length=1,
choices=LinkShowType.choices,
- default=LinkShowType.I)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_mod_time = models.DateTimeField(_('modify time'), default=now)
+ default=LinkShowType.I) #ZNY 默认在首页显示
+ creation_time = models.DateTimeField(_('creation time'), default=now) #ZNY 创建时间
+ last_mod_time = models.DateTimeField(_('modify time'), default=now) #ZNY 最后修改时间
class Meta:
- ordering = ['sequence']
- verbose_name = _('link')
- verbose_name_plural = verbose_name
+ ordering = ['sequence'] #ZNY 按排序序号升序排列
+ verbose_name = _('link') #ZNY 单数显示名称
+ verbose_name_plural = verbose_name #ZNY 复数显示名称
- def __str__(self):
- return self.name
+ def __str__(self): #ZNY 字符串表示方法
+ return self.name #ZNY 返回链接名称
-class SideBar(models.Model):
- """侧边栏,可以展示一些html内容"""
- name = models.CharField(_('title'), max_length=100)
- content = models.TextField(_('content'))
- sequence = models.IntegerField(_('order'), unique=True)
- is_enable = models.BooleanField(_('is enable'), default=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_mod_time = models.DateTimeField(_('modify time'), default=now)
+class SideBar(models.Model): #ZNY 定义侧边栏模型
+ """侧边栏,可以展示一些html内容""" #ZNY 侧边栏模型文档字符串
+ name = models.CharField(_('title'), max_length=100) #ZNY 侧边栏标题
+ content = models.TextField(_('content')) #ZNY 侧边栏内容
+ sequence = models.IntegerField(_('order'), unique=True) #ZNY 排序序号,唯一
+ is_enable = models.BooleanField(_('is enable'), default=True) #ZNY 是否启用,默认启用
+ creation_time = models.DateTimeField(_('creation time'), default=now) #ZNY 创建时间
+ last_mod_time = models.DateTimeField(_('modify time'), default=now) #ZNY 最后修改时间
class Meta:
- ordering = ['sequence']
- verbose_name = _('sidebar')
- verbose_name_plural = verbose_name
+ ordering = ['sequence'] #ZNY 按排序序号升序排列
+ verbose_name = _('sidebar') #ZNY 单数显示名称
+ verbose_name_plural = verbose_name #ZNY 复数显示名称
- def __str__(self):
- return self.name
+ def __str__(self): #ZNY 字符串表示方法
+ return self.name #ZNY 返回侧边栏名称
-class BlogSettings(models.Model):
- """blog的配置"""
- site_name = models.CharField(
+class BlogSettings(models.Model): #ZNY 定义博客设置模型
+ """blog的配置""" #ZNY 博客设置模型文档字符串
+ site_name = models.CharField( #ZNY 网站名称字段
_('site name'),
max_length=200,
null=False,
blank=False,
default='')
- site_description = models.TextField(
+ site_description = models.TextField( #ZNY 网站描述字段
_('site description'),
max_length=1000,
null=False,
blank=False,
default='')
- site_seo_description = models.TextField(
+ site_seo_description = models.TextField( #ZNY 网站SEO描述字段
_('site seo description'), max_length=1000, null=False, blank=False, default='')
- site_keywords = models.TextField(
+ site_keywords = models.TextField( #ZNY 网站关键词字段
_('site keywords'),
max_length=1000,
null=False,
blank=False,
default='')
- article_sub_length = models.IntegerField(_('article sub length'), default=300)
- sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10)
- sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5)
- article_comment_count = models.IntegerField(_('article comment count'), default=5)
- show_google_adsense = models.BooleanField(_('show adsense'), default=False)
- google_adsense_codes = models.TextField(
+ article_sub_length = models.IntegerField(_('article sub length'), default=300) #ZNY 文章摘要长度
+ sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10) #ZNY 侧边栏文章数量
+ sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5) #ZNY 侧边栏评论数量
+ article_comment_count = models.IntegerField(_('article comment count'), default=5) #ZNY 文章页评论数量
+ show_google_adsense = models.BooleanField(_('show adsense'), default=False) #ZNY 是否显示谷歌广告
+ google_adsense_codes = models.TextField( #ZNY 谷歌广告代码字段
_('adsense code'), max_length=2000, null=True, blank=True, default='')
- open_site_comment = models.BooleanField(_('open site comment'), default=True)
- global_header = models.TextField("公共头部", null=True, blank=True, default='')
- global_footer = models.TextField("公共尾部", null=True, blank=True, default='')
- beian_code = models.CharField(
+ open_site_comment = models.BooleanField(_('open site comment'), default=True) #ZNY 是否开启全站评论
+ global_header = models.TextField("公共头部", null=True, blank=True, default='') #ZNY 全局头部内容
+ global_footer = models.TextField("公共尾部", null=True, blank=True, default='') #ZNY 全局尾部内容
+ beian_code = models.CharField( #ZNY ICP备案号字段
'备案号',
max_length=2000,
null=True,
blank=True,
default='')
- analytics_code = models.TextField(
+ analytics_code = models.TextField( #ZNY 网站统计代码字段
"网站统计代码",
max_length=1000,
null=False,
blank=False,
default='')
- show_gongan_code = models.BooleanField(
+ show_gongan_code = models.BooleanField( #ZNY 是否显示公安备案号字段
'是否显示公安备案号', default=False, null=False)
- gongan_beiancode = models.TextField(
+ gongan_beiancode = models.TextField( #ZNY 公安备案号字段
'公安备案号',
max_length=2000,
null=True,
blank=True,
default='')
- comment_need_review = models.BooleanField(
+ comment_need_review = models.BooleanField( #ZNY 评论是否需要审核字段
'评论是否需要审核', default=False, null=False)
class Meta:
- verbose_name = _('Website configuration')
- verbose_name_plural = verbose_name
+ verbose_name = _('Website configuration') #ZNY 单数显示名称
+ verbose_name_plural = verbose_name #ZNY 复数显示名称
- def __str__(self):
- return self.site_name
+ def __str__(self): #ZNY 字符串表示方法
+ return self.site_name #ZNY 返回网站名称
- def clean(self):
- if BlogSettings.objects.exclude(id=self.id).count():
- raise ValidationError(_('There can only be one configuration'))
+ def clean(self): #ZNY 数据清洗验证方法
+ if BlogSettings.objects.exclude(id=self.id).count(): #ZNY 检查是否已存在其他配置
+ raise ValidationError(_('There can only be one configuration')) #ZNY 只能有一个配置,抛出验证错误
- def save(self, *args, **kwargs):
- super().save(*args, **kwargs)
- from djangoblog.utils import cache
- cache.clear()
+ def save(self, *args, **kwargs): #ZNY 重写保存方法
+ super().save(*args, **kwargs) #ZNY 调用父类保存方法
+ from djangoblog.utils import cache #ZNY 导入缓存工具
+ cache.clear() #ZNY 保存配置后清除所有缓存
diff --git a/src/DjangoBlog-master/blog/search_indexes.py b/src/DjangoBlog-master/blog/search_indexes.py
index 7f1dfac..5eb13d9 100644
--- a/src/DjangoBlog-master/blog/search_indexes.py
+++ b/src/DjangoBlog-master/blog/search_indexes.py
@@ -1,13 +1,13 @@
-from haystack import indexes
+from haystack import indexes #ZNY 导入Haystack搜索索引模块
-from blog.models import Article
+from blog.models import Article #ZNY 导入文章模型
-class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
- text = indexes.CharField(document=True, use_template=True)
+class ArticleIndex(indexes.SearchIndex, indexes.Indexable): #ZNY 定义文章搜索索引类,继承搜索索引和可索引接口
+ text = indexes.CharField(document=True, use_template=True) #ZNY 定义主搜索字段,使用模板构建索引内容
- def get_model(self):
- return Article
+ def get_model(self): #ZNY 获取索引对应的模型方法
+ return Article #ZNY 返回文章模型类
- def index_queryset(self, using=None):
- return self.get_model().objects.filter(status='p')
+ def index_queryset(self, using=None): #ZNY 定义索引查询集方法
+ return self.get_model().objects.filter(status='p') #ZNY 只索引已发布状态的文章
diff --git a/src/DjangoBlog-master/blog/templatetags/blog_tags.py b/src/DjangoBlog-master/blog/templatetags/blog_tags.py
index d6cd5d5..830a248 100644
--- a/src/DjangoBlog-master/blog/templatetags/blog_tags.py
+++ b/src/DjangoBlog-master/blog/templatetags/blog_tags.py
@@ -20,325 +20,325 @@ from djangoblog.utils import get_current_site
from oauth.models import OAuthUser
from djangoblog.plugin_manage import hooks
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #ZNY 获取当前模块的日志记录器
-register = template.Library()
+register = template.Library() #ZNY 创建Django模板标签库实例
-@register.simple_tag(takes_context=True)
-def head_meta(context):
- return mark_safe(hooks.apply_filters('head_meta', '', context))
+@register.simple_tag(takes_context=True) #ZNY 注册简单模板标签,接收模板上下文
+def head_meta(context): #ZNY 生成头部meta信息的模板标签
+ return mark_safe(hooks.apply_filters('head_meta', '', context)) #ZNY 应用过滤器并返回安全的HTML字符串
-@register.simple_tag
-def timeformat(data):
+@register.simple_tag #ZNY 注册简单模板标签
+def timeformat(data): #ZNY 时间格式化模板标签
try:
- return data.strftime(settings.TIME_FORMAT)
+ return data.strftime(settings.TIME_FORMAT) #ZNY 按照设置的时间格式格式化时间
except Exception as e:
- logger.error(e)
- return ""
+ logger.error(e) #ZNY 记录错误日志
+ return "" #ZNY 出错时返回空字符串
-@register.simple_tag
-def datetimeformat(data):
+@register.simple_tag #ZNY 注册简单模板标签
+def datetimeformat(data): #ZNY 日期时间格式化模板标签
try:
- return data.strftime(settings.DATE_TIME_FORMAT)
+ return data.strftime(settings.DATE_TIME_FORMAT) #ZNY 按照设置的日期时间格式格式化
except Exception as e:
- logger.error(e)
- return ""
+ logger.error(e) #ZNY 记录错误日志
+ return "" #ZNY 出错时返回空字符串
-@register.filter()
-@stringfilter
-def custom_markdown(content):
- return mark_safe(CommonMarkdown.get_markdown(content))
+@register.filter() #ZNY 注册模板过滤器
+@stringfilter #ZNY 确保输入被转换为字符串
+def custom_markdown(content): #ZNY 自定义Markdown转换过滤器
+ return mark_safe(CommonMarkdown.get_markdown(content)) #ZNY 将Markdown内容转换为HTML并标记为安全
-@register.simple_tag
-def get_markdown_toc(content):
- from djangoblog.utils import CommonMarkdown
- body, toc = CommonMarkdown.get_markdown_with_toc(content)
- return mark_safe(toc)
+@register.simple_tag #ZNY 注册简单模板标签
+def get_markdown_toc(content): #ZNY 获取Markdown目录模板标签
+ from djangoblog.utils import CommonMarkdown #ZNY 导入Markdown工具类
+ body, toc = CommonMarkdown.get_markdown_with_toc(content) #ZNY 获取带目录的Markdown内容
+ return mark_safe(toc) #ZNY 返回目录HTML并标记为安全
-@register.filter()
-@stringfilter
-def comment_markdown(content):
- content = CommonMarkdown.get_markdown(content)
- return mark_safe(sanitize_html(content))
+@register.filter() #ZNY 注册模板过滤器
+@stringfilter #ZNY 确保输入被转换为字符串
+def comment_markdown(content): #ZNY 评论Markdown转换过滤器
+ content = CommonMarkdown.get_markdown(content) #ZNY 将Markdown转换为HTML
+ return mark_safe(sanitize_html(content)) #ZNY 对HTML进行安全过滤后返回
-@register.filter(is_safe=True)
-@stringfilter
-def truncatechars_content(content):
+@register.filter(is_safe=True) #ZNY 注册安全模板过滤器
+@stringfilter #ZNY 确保输入被转换为字符串
+def truncatechars_content(content): #ZNY 截断文章内容过滤器
"""
获得文章内容的摘要
:param content:
:return:
"""
- from django.template.defaultfilters import truncatechars_html
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
- return truncatechars_html(content, blogsetting.article_sub_length)
+ from django.template.defaultfilters import truncatechars_html #ZNY 导入HTML截断函数
+ from djangoblog.utils import get_blog_setting #ZNY 导入博客设置获取函数
+ blogsetting = get_blog_setting() #ZNY 获取博客设置
+ return truncatechars_html(content, blogsetting.article_sub_length) #ZNY 按设置的文章摘要长度截断HTML内容
-@register.filter(is_safe=True)
-@stringfilter
-def truncate(content):
- from django.utils.html import strip_tags
+@register.filter(is_safe=True) #ZNY 注册安全模板过滤器
+@stringfilter #ZNY 确保输入被转换为字符串
+def truncate(content): #ZNY 简单截断过滤器
+ from django.utils.html import strip_tags #ZNY 导入HTML标签去除函数
- return strip_tags(content)[:150]
+ return strip_tags(content)[:150] #ZNY 去除HTML标签后截取前150个字符
-@register.inclusion_tag('blog/tags/breadcrumb.html')
-def load_breadcrumb(article):
+@register.inclusion_tag('blog/tags/breadcrumb.html') #ZNY 注册包含标签,指定模板
+def load_breadcrumb(article): #ZNY 加载面包屑导航标签
"""
获得文章面包屑
:param article:
:return:
"""
- names = article.get_category_tree()
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
- site = get_current_site().domain
- names.append((blogsetting.site_name, '/'))
- names = names[::-1]
+ names = article.get_category_tree() #ZNY 获取文章分类树
+ from djangoblog.utils import get_blog_setting #ZNY 导入博客设置获取函数
+ blogsetting = get_blog_setting() #ZNY 获取博客设置
+ site = get_current_site().domain #ZNY 获取当前站点域名
+ names.append((blogsetting.site_name, '/')) #ZNY 添加首页链接
+ names = names[::-1] #ZNY 反转列表顺序
return {
- 'names': names,
- 'title': article.title,
- 'count': len(names) + 1
+ 'names': names, #ZNY 面包屑名称和URL列表
+ 'title': article.title, #ZNY 文章标题
+ 'count': len(names) + 1 #ZNY 面包屑数量
}
-@register.inclusion_tag('blog/tags/article_tag_list.html')
-def load_articletags(article):
+@register.inclusion_tag('blog/tags/article_tag_list.html') #ZNY 注册包含标签,指定模板
+def load_articletags(article): #ZNY 加载文章标签列表标签
"""
文章标签
:param article:
:return:
"""
- tags = article.tags.all()
- tags_list = []
- for tag in tags:
- url = tag.get_absolute_url()
- count = tag.get_article_count()
- tags_list.append((
- url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES)
+ tags = article.tags.all() #ZNY 获取文章的所有标签
+ tags_list = [] #ZNY 初始化标签列表
+ for tag in tags: #ZNY 遍历每个标签
+ url = tag.get_absolute_url() #ZNY 获取标签URL
+ count = tag.get_article_count() #ZNY 获取标签下文章数量
+ tags_list.append(( #ZNY 添加标签信息到列表
+ url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES) #ZNY URL、数量、标签对象、随机颜色
))
return {
- 'article_tags_list': tags_list
+ 'article_tags_list': tags_list #ZNY 返回标签列表
}
-@register.inclusion_tag('blog/tags/sidebar.html')
-def load_sidebar(user, linktype):
+@register.inclusion_tag('blog/tags/sidebar.html') #ZNY 注册包含标签,指定模板
+def load_sidebar(user, linktype): #ZNY 加载侧边栏标签
"""
加载侧边栏
:return:
"""
- value = cache.get("sidebar" + linktype)
- if value:
- value['user'] = user
- return value
- else:
- logger.info('load sidebar')
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
- recent_articles = Article.objects.filter(
- status='p')[:blogsetting.sidebar_article_count]
- sidebar_categorys = Category.objects.all()
- extra_sidebars = SideBar.objects.filter(
- is_enable=True).order_by('sequence')
- most_read_articles = Article.objects.filter(status='p').order_by(
- '-views')[:blogsetting.sidebar_article_count]
- dates = Article.objects.datetimes('creation_time', 'month', order='DESC')
- links = Links.objects.filter(is_enable=True).filter(
- Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A))
- commment_list = Comment.objects.filter(is_enable=True).order_by(
- '-id')[:blogsetting.sidebar_comment_count]
+ value = cache.get("sidebar" + linktype) #ZNY 尝试从缓存获取侧边栏数据
+ if value: #ZNY 如果缓存存在
+ value['user'] = user #ZNY 添加用户信息
+ return value #ZNY 返回缓存数据
+ else: #ZNY 如果缓存不存在
+ logger.info('load sidebar') #ZNY 记录加载侧边栏日志
+ from djangoblog.utils import get_blog_setting #ZNY 导入博客设置获取函数
+ blogsetting = get_blog_setting() #ZNY 获取博客设置
+ recent_articles = Article.objects.filter( #ZNY 获取最近文章
+ status='p')[:blogsetting.sidebar_article_count] #ZNY 只取已发布文章,按设置数量限制
+ sidebar_categorys = Category.objects.all() #ZNY 获取所有分类
+ extra_sidebars = SideBar.objects.filter( #ZNY 获取额外侧边栏
+ is_enable=True).order_by('sequence') #ZNY 只取启用的,按顺序排序
+ most_read_articles = Article.objects.filter(status='p').order_by( #ZNY 获取最多阅读文章
+ '-views')[:blogsetting.sidebar_article_count] #ZNY 按浏览量降序排列,按设置数量限制
+ dates = Article.objects.datetimes('creation_time', 'month', order='DESC') #ZNY 获取文章按月归档日期
+ links = Links.objects.filter(is_enable=True).filter( #ZNY 获取友情链接
+ Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A)) #ZNY 按显示类型过滤
+ commment_list = Comment.objects.filter(is_enable=True).order_by( #ZNY 获取最新评论
+ '-id')[:blogsetting.sidebar_comment_count] #ZNY 按ID降序排列,按设置数量限制
# 标签云 计算字体大小
# 根据总数计算出平均值 大小为 (数目/平均值)*步长
- increment = 5
- tags = Tag.objects.all()
- sidebar_tags = None
- if tags and len(tags) > 0:
- s = [t for t in [(t, t.get_article_count()) for t in tags] if t[1]]
- count = sum([t[1] for t in s])
- dd = 1 if (count == 0 or not len(tags)) else count / len(tags)
- import random
- sidebar_tags = list(
- map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s))
- random.shuffle(sidebar_tags)
-
- value = {
- 'recent_articles': recent_articles,
- 'sidebar_categorys': sidebar_categorys,
- 'most_read_articles': most_read_articles,
- 'article_dates': dates,
- 'sidebar_comments': commment_list,
- 'sidabar_links': links,
- 'show_google_adsense': blogsetting.show_google_adsense,
- 'google_adsense_codes': blogsetting.google_adsense_codes,
- 'open_site_comment': blogsetting.open_site_comment,
- 'show_gongan_code': blogsetting.show_gongan_code,
- 'sidebar_tags': sidebar_tags,
- 'extra_sidebars': extra_sidebars
+ increment = 5 #ZNY 字体大小增量
+ tags = Tag.objects.all() #ZNY 获取所有标签
+ sidebar_tags = None #ZNY 初始化侧边栏标签
+ if tags and len(tags) > 0: #ZNY 如果有标签
+ s = [t for t in [(t, t.get_article_count()) for t in tags] if t[1]] #ZNY 生成标签和文章数量列表
+ count = sum([t[1] for t in s]) #ZNY 计算总文章数
+ dd = 1 if (count == 0 or not len(tags)) else count / len(tags) #ZNY 计算平均值
+ import random #ZNY 导入随机模块
+ sidebar_tags = list( #ZNY 生成侧边栏标签列表
+ map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s)) #ZNY 计算每个标签的字体大小
+ random.shuffle(sidebar_tags) #ZNY 随机打乱标签顺序
+
+ value = { #ZNY 构建侧边栏数据字典
+ 'recent_articles': recent_articles, #ZNY 最近文章
+ 'sidebar_categorys': sidebar_categorys, #ZNY 分类列表
+ 'most_read_articles': most_read_articles, #ZNY 最多阅读文章
+ 'article_dates': dates, #ZNY 文章归档日期
+ 'sidebar_comments': commment_list, #ZNY 侧边栏评论
+ 'sidabar_links': links, #ZNY 侧边栏链接
+ 'show_google_adsense': blogsetting.show_google_adsense, #ZNY 是否显示谷歌广告
+ 'google_adsense_codes': blogsetting.google_adsense_codes, #ZNY 谷歌广告代码
+ 'open_site_comment': blogsetting.open_site_comment, #ZNY 是否开启全站评论
+ 'show_gongan_code': blogsetting.show_gongan_code, #ZNY 是否显示公安备案
+ 'sidebar_tags': sidebar_tags, #ZNY 侧边栏标签云
+ 'extra_sidebars': extra_sidebars #ZNY 额外侧边栏
}
- cache.set("sidebar" + linktype, value, 60 * 60 * 60 * 3)
- logger.info('set sidebar cache.key:{key}'.format(key="sidebar" + linktype))
- value['user'] = user
- return value
+ cache.set("sidebar" + linktype, value, 60 * 60 * 60 * 3) #ZNY 将侧边栏数据存入缓存,有效期3小时
+ logger.info('set sidebar cache.key:{key}'.format(key="sidebar" + linktype)) #ZNY 记录缓存设置日志
+ value['user'] = user #ZNY 添加用户信息
+ return value #ZNY 返回侧边栏数据
-@register.inclusion_tag('blog/tags/article_meta_info.html')
-def load_article_metas(article, user):
+@register.inclusion_tag('blog/tags/article_meta_info.html') #ZNY 注册包含标签,指定模板
+def load_article_metas(article, user): #ZNY 加载文章meta信息标签
"""
获得文章meta信息
:param article:
:return:
"""
return {
- 'article': article,
- 'user': user
+ 'article': article, #ZNY 文章对象
+ 'user': user #ZNY 用户对象
}
-@register.inclusion_tag('blog/tags/article_pagination.html')
-def load_pagination_info(page_obj, page_type, tag_name):
- previous_url = ''
- next_url = ''
- if page_type == '':
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse('blog:index_page', kwargs={'page': next_number})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
+@register.inclusion_tag('blog/tags/article_pagination.html') #ZNY 注册包含标签,指定模板
+def load_pagination_info(page_obj, page_type, tag_name): #ZNY 加载分页信息标签
+ previous_url = '' #ZNY 初始化上一页URL
+ next_url = '' #ZNY 初始化下一页URL
+ if page_type == '': #ZNY 如果是首页分页
+ if page_obj.has_next(): #ZNY 如果有下一页
+ next_number = page_obj.next_page_number() #ZNY 获取下一页页码
+ next_url = reverse('blog:index_page', kwargs={'page': next_number}) #ZNY 生成下一页URL
+ if page_obj.has_previous(): #ZNY 如果有上一页
+ previous_number = page_obj.previous_page_number() #ZNY 获取上一页页码
+ previous_url = reverse( #ZNY 生成上一页URL
'blog:index_page', kwargs={
'page': previous_number})
- if page_type == '分类标签归档':
- tag = get_object_or_404(Tag, name=tag_name)
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse(
+ if page_type == '分类标签归档': #ZNY 如果是标签归档分页
+ tag = get_object_or_404(Tag, name=tag_name) #ZNY 获取标签对象
+ if page_obj.has_next(): #ZNY 如果有下一页
+ next_number = page_obj.next_page_number() #ZNY 获取下一页页码
+ next_url = reverse( #ZNY 生成标签下一页URL
'blog:tag_detail_page',
kwargs={
'page': next_number,
'tag_name': tag.slug})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
+ if page_obj.has_previous(): #ZNY 如果有上一页
+ previous_number = page_obj.previous_page_number() #ZNY 获取上一页页码
+ previous_url = reverse( #ZNY 生成标签上一页URL
'blog:tag_detail_page',
kwargs={
'page': previous_number,
'tag_name': tag.slug})
- if page_type == '作者文章归档':
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse(
+ if page_type == '作者文章归档': #ZNY 如果是作者归档分页
+ if page_obj.has_next(): #ZNY 如果有下一页
+ next_number = page_obj.next_page_number() #ZNY 获取下一页页码
+ next_url = reverse( #ZNY 生成作者下一页URL
'blog:author_detail_page',
kwargs={
'page': next_number,
'author_name': tag_name})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
+ if page_obj.has_previous(): #ZNY 如果有上一页
+ previous_number = page_obj.previous_page_number() #ZNY 获取上一页页码
+ previous_url = reverse( #ZNY 生成作者上一页URL
'blog:author_detail_page',
kwargs={
'page': previous_number,
'author_name': tag_name})
- if page_type == '分类目录归档':
- category = get_object_or_404(Category, name=tag_name)
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse(
+ if page_type == '分类目录归档': #ZNY 如果是分类归档分页
+ category = get_object_or_404(Category, name=tag_name) #ZNY 获取分类对象
+ if page_obj.has_next(): #ZNY 如果有下一页
+ next_number = page_obj.next_page_number() #ZNY 获取下一页页码
+ next_url = reverse( #ZNY 生成分类下一页URL
'blog:category_detail_page',
kwargs={
'page': next_number,
'category_name': category.slug})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
+ if page_obj.has_previous(): #ZNY 如果有上一页
+ previous_number = page_obj.previous_page_number() #ZNY 获取上一页页码
+ previous_url = reverse( #ZNY 生成分类上一页URL
'blog:category_detail_page',
kwargs={
'page': previous_number,
'category_name': category.slug})
return {
- 'previous_url': previous_url,
- 'next_url': next_url,
- 'page_obj': page_obj
+ 'previous_url': previous_url, #ZNY 上一页URL
+ 'next_url': next_url, #ZNY 下一页URL
+ 'page_obj': page_obj #ZNY 分页对象
}
-@register.inclusion_tag('blog/tags/article_info.html')
-def load_article_detail(article, isindex, user):
+@register.inclusion_tag('blog/tags/article_info.html') #ZNY 注册包含标签,指定模板
+def load_article_detail(article, isindex, user): #ZNY 加载文章详情标签
"""
加载文章详情
:param article:
:param isindex:是否列表页,若是列表页只显示摘要
:return:
"""
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
+ from djangoblog.utils import get_blog_setting #ZNY 导入博客设置获取函数
+ blogsetting = get_blog_setting() #ZNY 获取博客设置
return {
- 'article': article,
- 'isindex': isindex,
- 'user': user,
- 'open_site_comment': blogsetting.open_site_comment,
+ 'article': article, #ZNY 文章对象
+ 'isindex': isindex, #ZNY 是否是列表页
+ 'user': user, #ZNY 用户对象
+ 'open_site_comment': blogsetting.open_site_comment, #ZNY 是否开启全站评论
}
# return only the URL of the gravatar
# TEMPLATE USE: {{ email|gravatar_url:150 }}
-@register.filter
-def gravatar_url(email, size=40):
+@register.filter #ZNY 注册模板过滤器
+def gravatar_url(email, size=40): #ZNY 获取Gravatar头像URL过滤器
"""获得gravatar头像"""
- cachekey = 'gravatat/' + email
- url = cache.get(cachekey)
- if url:
- return url
- else:
- usermodels = OAuthUser.objects.filter(email=email)
- if usermodels:
- o = list(filter(lambda x: x.picture is not None, usermodels))
- if o:
- return o[0].picture
- email = email.encode('utf-8')
-
- default = static('blog/img/avatar.png')
-
- url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5(
- email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)}))
- cache.set(cachekey, url, 60 * 60 * 10)
- logger.info('set gravatar cache.key:{key}'.format(key=cachekey))
- return url
-
-
-@register.filter
-def gravatar(email, size=40):
+ cachekey = 'gravatat/' + email #ZNY 生成缓存键
+ url = cache.get(cachekey) #ZNY 尝试从缓存获取头像URL
+ if url: #ZNY 如果缓存存在
+ return url #ZNY 返回缓存URL
+ else: #ZNY 如果缓存不存在
+ usermodels = OAuthUser.objects.filter(email=email) #ZNY 查询OAuth用户
+ if usermodels: #ZNY 如果找到用户
+ o = list(filter(lambda x: x.picture is not None, usermodels)) #ZNY 过滤有头像的用户
+ if o: #ZNY 如果有头像的用户存在
+ return o[0].picture #ZNY 返回用户自定义头像
+ email = email.encode('utf-8') #ZNY 编码邮箱
+
+ default = static('blog/img/avatar.png') #ZNY 设置默认头像路径
+
+ url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5( #ZNY 生成Gravatar URL
+ email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)})) #ZNY 使用MD5哈希和URL参数
+ cache.set(cachekey, url, 60 * 60 * 10) #ZNY 将URL存入缓存,有效期10小时
+ logger.info('set gravatar cache.key:{key}'.format(key=cachekey)) #ZNY 记录缓存设置日志
+ return url #ZNY 返回头像URL
+
+
+@register.filter #ZNY 注册模板过滤器
+def gravatar(email, size=40): #ZNY 获取Gravatar头像HTML过滤器
"""获得gravatar头像"""
- url = gravatar_url(email, size)
- return mark_safe(
+ url = gravatar_url(email, size) #ZNY 获取头像URL
+ return mark_safe( #ZNY 返回安全的HTML图像标签
'
' %
(url, size, size))
-@register.simple_tag
-def query(qs, **kwargs):
+@register.simple_tag #ZNY 注册简单模板标签
+def query(qs, **kwargs): #ZNY 查询集过滤模板标签
""" template tag which allows queryset filtering. Usage:
{% query books author=author as mybooks %}
{% for book in mybooks %}
...
{% endfor %}
"""
- return qs.filter(**kwargs)
+ return qs.filter(**kwargs) #ZNY 应用过滤条件并返回查询集
-@register.filter
-def addstr(arg1, arg2):
+@register.filter #ZNY 注册模板过滤器
+def addstr(arg1, arg2): #ZNY 字符串连接过滤器
"""concatenate arg1 & arg2"""
- return str(arg1) + str(arg2)
+ return str(arg1) + str(arg2) #ZNY 将两个参数转换为字符串后连接
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/tests.py b/src/DjangoBlog-master/blog/tests.py
index ee13505..7a204ea 100644
--- a/src/DjangoBlog-master/blog/tests.py
+++ b/src/DjangoBlog-master/blog/tests.py
@@ -19,214 +19,214 @@ from oauth.models import OAuthUser, OAuthConfig
# Create your tests here.
-class ArticleTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
-
- def test_validate_article(self):
- site = get_current_site().domain
- user = BlogUser.objects.get_or_create(
+class ArticleTest(TestCase): #ZNY 定义文章测试类,继承Django测试用例
+ def setUp(self): #ZNY 测试初始化方法
+ self.client = Client() #ZNY 创建测试客户端
+ self.factory = RequestFactory() #ZNY 创建请求工厂
+
+ def test_validate_article(self): #ZNY 测试文章验证方法
+ site = get_current_site().domain #ZNY 获取当前站点域名
+ user = BlogUser.objects.get_or_create( #ZNY 获取或创建测试用户
email="liangliangyy@gmail.com",
username="liangliangyy")[0]
- user.set_password("liangliangyy")
- user.is_staff = True
- user.is_superuser = True
- user.save()
- response = self.client.get(user.get_absolute_url())
- self.assertEqual(response.status_code, 200)
- response = self.client.get('/admin/servermanager/emailsendlog/')
- response = self.client.get('admin/admin/logentry/')
- s = SideBar()
- s.sequence = 1
- s.name = 'test'
- s.content = 'test content'
- s.is_enable = True
- s.save()
-
- category = Category()
- category.name = "category"
- category.creation_time = timezone.now()
- category.last_mod_time = timezone.now()
- category.save()
-
- tag = Tag()
- tag.name = "nicetag"
- tag.save()
-
- article = Article()
- article.title = "nicetitle"
- article.body = "nicecontent"
- article.author = user
- article.category = category
- article.type = 'a'
- article.status = 'p'
-
- article.save()
- self.assertEqual(0, article.tags.count())
- article.tags.add(tag)
- article.save()
- self.assertEqual(1, article.tags.count())
-
- for i in range(20):
- article = Article()
- article.title = "nicetitle" + str(i)
- article.body = "nicetitle" + str(i)
- article.author = user
- article.category = category
- article.type = 'a'
- article.status = 'p'
- article.save()
- article.tags.add(tag)
- article.save()
- from blog.documents import ELASTICSEARCH_ENABLED
- if ELASTICSEARCH_ENABLED:
- call_command("build_index")
- response = self.client.get('/search', {'q': 'nicetitle'})
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get(article.get_absolute_url())
- self.assertEqual(response.status_code, 200)
- from djangoblog.spider_notify import SpiderNotify
- SpiderNotify.notify(article.get_absolute_url())
- response = self.client.get(tag.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get(category.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get('/search', {'q': 'django'})
- self.assertEqual(response.status_code, 200)
- s = load_articletags(article)
- self.assertIsNotNone(s)
-
- self.client.login(username='liangliangyy', password='liangliangyy')
-
- response = self.client.get(reverse('blog:archives'))
- self.assertEqual(response.status_code, 200)
-
- p = Paginator(Article.objects.all(), settings.PAGINATE_BY)
- self.check_pagination(p, '', '')
-
- p = Paginator(Article.objects.filter(tags=tag), settings.PAGINATE_BY)
- self.check_pagination(p, '分类标签归档', tag.slug)
-
- p = Paginator(
+ user.set_password("liangliangyy") #ZNY 设置用户密码
+ user.is_staff = True #ZNY 设置为管理员
+ user.is_superuser = True #ZNY 设置为超级用户
+ user.save() #ZNY 保存用户
+ response = self.client.get(user.get_absolute_url()) #ZNY 访问用户详情页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+ response = self.client.get('/admin/servermanager/emailsendlog/') #ZNY 访问邮件发送日志管理页
+ response = self.client.get('admin/admin/logentry/') #ZNY 访问日志条目管理页
+ s = SideBar() #ZNY 创建侧边栏实例
+ s.sequence = 1 #ZNY 设置排序序号
+ s.name = 'test' #ZNY 设置侧边栏名称
+ s.content = 'test content' #ZNY 设置侧边栏内容
+ s.is_enable = True #ZNY 启用侧边栏
+ s.save() #ZNY 保存侧边栏
+
+ category = Category() #ZNY 创建分类实例
+ category.name = "category" #ZNY 设置分类名称
+ category.creation_time = timezone.now() #ZNY 设置创建时间
+ category.last_mod_time = timezone.now() #ZNY 设置修改时间
+ category.save() #ZNY 保存分类
+
+ tag = Tag() #ZNY 创建标签实例
+ tag.name = "nicetag" #ZNY 设置标签名称
+ tag.save() #ZNY 保存标签
+
+ article = Article() #ZNY 创建文章实例
+ article.title = "nicetitle" #ZNY 设置文章标题
+ article.body = "nicecontent" #ZNY 设置文章正文
+ article.author = user #ZNY 设置文章作者
+ article.category = category #ZNY 设置文章分类
+ article.type = 'a' #ZNY 设置文章类型为普通文章
+ article.status = 'p' #ZNY 设置文章状态为发布
+
+ article.save() #ZNY 保存文章
+ self.assertEqual(0, article.tags.count()) #ZNY 断言初始标签数量为0
+ article.tags.add(tag) #ZNY 添加标签到文章
+ article.save() #ZNY 保存文章
+ self.assertEqual(1, article.tags.count()) #ZNY 断言标签数量为1
+
+ for i in range(20): #ZNY 循环创建20篇文章
+ article = Article() #ZNY 创建文章实例
+ article.title = "nicetitle" + str(i) #ZNY 设置带编号的文章标题
+ article.body = "nicetitle" + str(i) #ZNY 设置带编号的文章正文
+ article.author = user #ZNY 设置文章作者
+ article.category = category #ZNY 设置文章分类
+ article.type = 'a' #ZNY 设置文章类型为普通文章
+ article.status = 'p' #ZNY 设置文章状态为发布
+ article.save() #ZNY 保存文章
+ article.tags.add(tag) #ZNY 添加标签到文章
+ article.save() #ZNY 保存文章
+ from blog.documents import ELASTICSEARCH_ENABLED #ZNY 导入Elasticsearch启用标志
+ if ELASTICSEARCH_ENABLED: #ZNY 如果Elasticsearch已启用
+ call_command("build_index") #ZNY 调用构建索引命令
+ response = self.client.get('/search', {'q': 'nicetitle'}) #ZNY 搜索文章
+ self.assertEqual(response.status_code, 200) #ZNY 断言搜索响应状态码为200
+
+ response = self.client.get(article.get_absolute_url()) #ZNY 访问文章详情页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+ from djangoblog.spider_notify import SpiderNotify #ZNY 导入蜘蛛通知模块
+ SpiderNotify.notify(article.get_absolute_url()) #ZNY 通知蜘蛛文章URL
+ response = self.client.get(tag.get_absolute_url()) #ZNY 访问标签详情页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ response = self.client.get(category.get_absolute_url()) #ZNY 访问分类详情页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ response = self.client.get('/search', {'q': 'django'}) #ZNY 搜索django相关内容
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+ s = load_articletags(article) #ZNY 加载文章标签
+ self.assertIsNotNone(s) #ZNY 断言标签不为空
+
+ self.client.login(username='liangliangyy', password='liangliangyy') #ZNY 登录用户
+
+ response = self.client.get(reverse('blog:archives')) #ZNY 访问文章归档页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ p = Paginator(Article.objects.all(), settings.PAGINATE_BY) #ZNY 创建文章分页器
+ self.check_pagination(p, '', '') #ZNY 检查首页分页
+
+ p = Paginator(Article.objects.filter(tags=tag), settings.PAGINATE_BY) #ZNY 创建标签文章分页器
+ self.check_pagination(p, '分类标签归档', tag.slug) #ZNY 检查标签分页
+
+ p = Paginator( #ZNY 创建作者文章分页器
Article.objects.filter(
author__username='liangliangyy'), settings.PAGINATE_BY)
- self.check_pagination(p, '作者文章归档', 'liangliangyy')
+ self.check_pagination(p, '作者文章归档', 'liangliangyy') #ZNY 检查作者分页
- p = Paginator(Article.objects.filter(category=category), settings.PAGINATE_BY)
- self.check_pagination(p, '分类目录归档', category.slug)
+ p = Paginator(Article.objects.filter(category=category), settings.PAGINATE_BY) #ZNY 创建分类文章分页器
+ self.check_pagination(p, '分类目录归档', category.slug) #ZNY 检查分类分页
- f = BlogSearchForm()
- f.search()
- # self.client.login(username='liangliangyy', password='liangliangyy')
- from djangoblog.spider_notify import SpiderNotify
- SpiderNotify.baidu_notify([article.get_full_url()])
+ f = BlogSearchForm() #ZNY 创建搜索表单实例
+ f.search() #ZNY 执行搜索
+ # self.client.login(username='liangliangyy', password='liangliangyy') #ZNY 注释掉的登录代码
+ from djangoblog.spider_notify import SpiderNotify #ZNY 导入蜘蛛通知模块
+ SpiderNotify.baidu_notify([article.get_full_url()]) #ZNY 通知百度文章完整URL
- from blog.templatetags.blog_tags import gravatar_url, gravatar
- u = gravatar_url('liangliangyy@gmail.com')
- u = gravatar('liangliangyy@gmail.com')
+ from blog.templatetags.blog_tags import gravatar_url, gravatar #ZNY 导入Gravatar相关函数
+ u = gravatar_url('liangliangyy@gmail.com') #ZNY 获取Gravatar头像URL
+ u = gravatar('liangliangyy@gmail.com') #ZNY 获取Gravatar头像HTML
- link = Links(
+ link = Links( #ZNY 创建友情链接实例
sequence=1,
name="lylinux",
link='https://wwww.lylinux.net')
- link.save()
- response = self.client.get('/links.html')
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get('/feed/')
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get('/sitemap.xml')
- self.assertEqual(response.status_code, 200)
-
- self.client.get("/admin/blog/article/1/delete/")
- self.client.get('/admin/servermanager/emailsendlog/')
- self.client.get('/admin/admin/logentry/')
- self.client.get('/admin/admin/logentry/1/change/')
-
- def check_pagination(self, p, type, value):
- for page in range(1, p.num_pages + 1):
- s = load_pagination_info(p.page(page), type, value)
- self.assertIsNotNone(s)
- if s['previous_url']:
- response = self.client.get(s['previous_url'])
- self.assertEqual(response.status_code, 200)
- if s['next_url']:
- response = self.client.get(s['next_url'])
- self.assertEqual(response.status_code, 200)
-
- def test_image(self):
- import requests
- rsp = requests.get(
+ link.save() #ZNY 保存链接
+ response = self.client.get('/links.html') #ZNY 访问友情链接页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ response = self.client.get('/feed/') #ZNY 访问RSS订阅
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ response = self.client.get('/sitemap.xml') #ZNY 访问站点地图
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ self.client.get("/admin/blog/article/1/delete/") #ZNY 访问文章删除页
+ self.client.get('/admin/servermanager/emailsendlog/') #ZNY 访问邮件发送日志管理页
+ self.client.get('/admin/admin/logentry/') #ZNY 访问日志条目管理页
+ self.client.get('/admin/admin/logentry/1/change/') #ZNY 访问日志条目编辑页
+
+ def check_pagination(self, p, type, value): #ZNY 检查分页方法
+ for page in range(1, p.num_pages + 1): #ZNY 遍历所有分页
+ s = load_pagination_info(p.page(page), type, value) #ZNY 加载分页信息
+ self.assertIsNotNone(s) #ZNY 断言分页信息不为空
+ if s['previous_url']: #ZNY 如果存在上一页URL
+ response = self.client.get(s['previous_url']) #ZNY 访问上一页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+ if s['next_url']: #ZNY 如果存在下一页URL
+ response = self.client.get(s['next_url']) #ZNY 访问下一页
+ self.assertEqual(response.status_code, 200) #ZNY 断言响应状态码为200
+
+ def test_image(self): #ZNY 测试图片相关功能
+ import requests #ZNY 导入requests库
+ rsp = requests.get( #ZNY 下载图片
'https://www.python.org/static/img/python-logo.png')
- imagepath = os.path.join(settings.BASE_DIR, 'python.png')
- with open(imagepath, 'wb') as file:
+ imagepath = os.path.join(settings.BASE_DIR, 'python.png') #ZNY 构建图片保存路径
+ with open(imagepath, 'wb') as file: #ZNY 保存图片到本地
file.write(rsp.content)
- rsp = self.client.post('/upload')
- self.assertEqual(rsp.status_code, 403)
- sign = get_sha256(get_sha256(settings.SECRET_KEY))
- with open(imagepath, 'rb') as file:
- imgfile = SimpleUploadedFile(
+ rsp = self.client.post('/upload') #ZNY 测试未授权上传
+ self.assertEqual(rsp.status_code, 403) #ZNY 断言返回403禁止访问
+ sign = get_sha256(get_sha256(settings.SECRET_KEY)) #ZNY 生成上传签名
+ with open(imagepath, 'rb') as file: #ZNY 打开图片文件
+ imgfile = SimpleUploadedFile( #ZNY 创建上传文件对象
'python.png', file.read(), content_type='image/jpg')
- form_data = {'python.png': imgfile}
- rsp = self.client.post(
+ form_data = {'python.png': imgfile} #ZNY 构建表单数据
+ rsp = self.client.post( #ZNY 执行上传请求
'/upload?sign=' + sign, form_data, follow=True)
- self.assertEqual(rsp.status_code, 200)
- os.remove(imagepath)
- from djangoblog.utils import save_user_avatar, send_email
- send_email(['qq@qq.com'], 'testTitle', 'testContent')
- save_user_avatar(
+ self.assertEqual(rsp.status_code, 200) #ZNY 断言上传成功
+ os.remove(imagepath) #ZNY 删除临时图片文件
+ from djangoblog.utils import save_user_avatar, send_email #ZNY 导入工具函数
+ send_email(['qq@qq.com'], 'testTitle', 'testContent') #ZNY 测试发送邮件
+ save_user_avatar( #ZNY 测试保存用户头像
'https://www.python.org/static/img/python-logo.png')
- def test_errorpage(self):
- rsp = self.client.get('/eee')
- self.assertEqual(rsp.status_code, 404)
+ def test_errorpage(self): #ZNY 测试错误页面
+ rsp = self.client.get('/eee') #ZNY 访问不存在的页面
+ self.assertEqual(rsp.status_code, 404) #ZNY 断言返回404错误
- def test_commands(self):
- user = BlogUser.objects.get_or_create(
+ def test_commands(self): #ZNY 测试管理命令
+ user = BlogUser.objects.get_or_create( #ZNY 获取或创建测试用户
email="liangliangyy@gmail.com",
username="liangliangyy")[0]
- user.set_password("liangliangyy")
- user.is_staff = True
- user.is_superuser = True
- user.save()
-
- c = OAuthConfig()
- c.type = 'qq'
- c.appkey = 'appkey'
- c.appsecret = 'appsecret'
- c.save()
-
- u = OAuthUser()
- u.type = 'qq'
- u.openid = 'openid'
- u.user = user
- u.picture = static("/blog/img/avatar.png")
- u.metadata = '''
+ user.set_password("liangliangyy") #ZNY 设置用户密码
+ user.is_staff = True #ZNY 设置为管理员
+ user.is_superuser = True #ZNY 设置为超级用户
+ user.save() #ZNY 保存用户
+
+ c = OAuthConfig() #ZNY 创建OAuth配置实例
+ c.type = 'qq' #ZNY 设置OAuth类型为QQ
+ c.appkey = 'appkey' #ZNY 设置应用密钥
+ c.appsecret = 'appsecret' #ZNY 设置应用密钥
+ c.save() #ZNY 保存配置
+
+ u = OAuthUser() #ZNY 创建OAuth用户实例
+ u.type = 'qq' #ZNY 设置OAuth类型为QQ
+ u.openid = 'openid' #ZNY 设置OpenID
+ u.user = user #ZNY 关联用户
+ u.picture = static("/blog/img/avatar.png") #ZNY 设置头像路径
+ u.metadata = ''' #ZNY 设置元数据
{
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
}'''
- u.save()
+ u.save() #ZNY 保存OAuth用户
- u = OAuthUser()
- u.type = 'qq'
- u.openid = 'openid1'
- u.picture = 'https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30'
- u.metadata = '''
+ u = OAuthUser() #ZNY 创建另一个OAuth用户实例
+ u.type = 'qq' #ZNY 设置OAuth类型为QQ
+ u.openid = 'openid1' #ZNY 设置不同的OpenID
+ u.picture = 'https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30' #ZNY 设置头像URL
+ u.metadata = ''' #ZNY 设置元数据
{
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
}'''
- u.save()
-
- from blog.documents import ELASTICSEARCH_ENABLED
- if ELASTICSEARCH_ENABLED:
- call_command("build_index")
- call_command("ping_baidu", "all")
- call_command("create_testdata")
- call_command("clear_cache")
- call_command("sync_user_avatar")
- call_command("build_search_words")
+ u.save() #ZNY 保存OAuth用户
+
+ from blog.documents import ELASTICSEARCH_ENABLED #ZNY 导入Elasticsearch启用标志
+ if ELASTICSEARCH_ENABLED: #ZNY 如果Elasticsearch已启用
+ call_command("build_index") #ZNY 调用构建索引命令
+ call_command("ping_baidu", "all") #ZNY 调用百度推送命令
+ call_command("create_testdata") #ZNY 调用创建测试数据命令
+ call_command("clear_cache") #ZNY 调用清除缓存命令
+ call_command("sync_user_avatar") #ZNY 调用同步用户头像命令
+ call_command("build_search_words") #ZNY 调用构建搜索词命令
diff --git a/src/DjangoBlog-master/blog/urls.py b/src/DjangoBlog-master/blog/urls.py
index adf2703..39f0007 100644
--- a/src/DjangoBlog-master/blog/urls.py
+++ b/src/DjangoBlog-master/blog/urls.py
@@ -1,62 +1,62 @@
-from django.urls import path
-from django.views.decorators.cache import cache_page
+from django.urls import path #ZNY 导入Django URL路径模块
+from django.views.decorators.cache import cache_page #ZNY 导入缓存页面装饰器
-from . import views
+from . import views #ZNY 导入当前应用的视图模块
-app_name = "blog"
-urlpatterns = [
- path(
+app_name = "blog" #ZNY 定义应用命名空间为blog
+urlpatterns = [ #ZNY 定义URL模式列表
+ path( #ZNY 首页路由
r'',
- views.IndexView.as_view(),
- name='index'),
- path(
- r'page//',
- views.IndexView.as_view(),
- name='index_page'),
- path(
- r'article////.html',
- views.ArticleDetailView.as_view(),
- name='detailbyid'),
- path(
- r'category/.html',
- views.CategoryDetailView.as_view(),
- name='category_detail'),
- path(
- r'category//.html',
- views.CategoryDetailView.as_view(),
- name='category_detail_page'),
- path(
- r'author/.html',
- views.AuthorDetailView.as_view(),
- name='author_detail'),
- path(
- r'author//.html',
- views.AuthorDetailView.as_view(),
- name='author_detail_page'),
- path(
- r'tag/.html',
- views.TagDetailView.as_view(),
- name='tag_detail'),
- path(
- r'tag//.html',
- views.TagDetailView.as_view(),
- name='tag_detail_page'),
- path(
+ views.IndexView.as_view(), #ZNY 使用类视图处理首页
+ name='index'), #ZNY URL名称为index
+ path( #ZNY 首页分页路由
+ r'page//', #ZNY 带页码参数的URL
+ views.IndexView.as_view(), #ZNY 使用相同的类视图处理分页
+ name='index_page'), #ZNY URL名称为index_page
+ path( #ZNY 文章详情页路由
+ r'article////.html', #ZNY 包含年月日和文章ID的URL
+ views.ArticleDetailView.as_view(), #ZNY 使用文章详情类视图
+ name='detailbyid'), #ZNY URL名称为detailbyid
+ path( #ZNY 分类详情页路由
+ r'category/.html', #ZNY 包含分类名称的URL
+ views.CategoryDetailView.as_view(), #ZNY 使用分类详情类视图
+ name='category_detail'), #ZNY URL名称为category_detail
+ path( #ZNY 分类分页路由
+ r'category//.html', #ZNY 包含分类名称和页码的URL
+ views.CategoryDetailView.as_view(), #ZNY 使用相同的类视图处理分页
+ name='category_detail_page'), #ZNY URL名称为category_detail_page
+ path( #ZNY 作者详情页路由
+ r'author/.html', #ZNY 包含作者名称的URL
+ views.AuthorDetailView.as_view(), #ZNY 使用作者详情类视图
+ name='author_detail'), #ZNY URL名称为author_detail
+ path( #ZNY 作者分页路由
+ r'author//.html', #ZNY 包含作者名称和页码的URL
+ views.AuthorDetailView.as_view(), #ZNY 使用相同的类视图处理分页
+ name='author_detail_page'), #ZNY URL名称为author_detail_page
+ path( #ZNY 标签详情页路由
+ r'tag/.html', #ZNY 包含标签名称的URL
+ views.TagDetailView.as_view(), #ZNY 使用标签详情类视图
+ name='tag_detail'), #ZNY URL名称为tag_detail
+ path( #ZNY 标签分页路由
+ r'tag//.html', #ZNY 包含标签名称和页码的URL
+ views.TagDetailView.as_view(), #ZNY 使用相同的类视图处理分页
+ name='tag_detail_page'), #ZNY URL名称为tag_detail_page
+ path( #ZNY 文章归档页路由
'archives.html',
- cache_page(
- 60 * 60)(
- views.ArchivesView.as_view()),
- name='archives'),
- path(
+ cache_page( #ZNY 使用缓存装饰器缓存页面
+ 60 * 60)( #ZNY 缓存1小时
+ views.ArchivesView.as_view()), #ZNY 使用归档视图
+ name='archives'), #ZNY URL名称为archives
+ path( #ZNY 友情链接页路由
'links.html',
- views.LinkListView.as_view(),
- name='links'),
- path(
+ views.LinkListView.as_view(), #ZNY 使用链接列表视图
+ name='links'), #ZNY URL名称为links
+ path( #ZNY 文件上传路由
r'upload',
- views.fileupload,
- name='upload'),
- path(
+ views.fileupload, #ZNY 使用函数视图处理文件上传
+ name='upload'), #ZNY URL名称为upload
+ path( #ZNY 清理缓存路由
r'clean',
- views.clean_cache_view,
- name='clean'),
+ views.clean_cache_view, #ZNY 使用函数视图清理缓存
+ name='clean'), #ZNY URL名称为clean
]
diff --git a/src/DjangoBlog-master/blog/views.py b/src/DjangoBlog-master/blog/views.py
index d5dc7ec..29041e3 100644
--- a/src/DjangoBlog-master/blog/views.py
+++ b/src/DjangoBlog-master/blog/views.py
@@ -21,359 +21,359 @@ from djangoblog.plugin_manage import hooks
from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME
from djangoblog.utils import cache, get_blog_setting, get_sha256
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #ZNY 获取当前模块的日志记录器
-class ArticleListView(ListView):
+class ArticleListView(ListView): #ZNY 文章列表视图基类,继承Django通用列表视图
# template_name属性用于指定使用哪个模板进行渲染
- template_name = 'blog/article_index.html'
+ template_name = 'blog/article_index.html' #ZNY 指定使用的模板文件
# context_object_name属性用于给上下文变量取名(在模板中使用该名字)
- context_object_name = 'article_list'
+ context_object_name = 'article_list' #ZNY 模板中使用的上下文变量名
# 页面类型,分类目录或标签列表等
- page_type = ''
- paginate_by = settings.PAGINATE_BY
- page_kwarg = 'page'
- link_type = LinkShowType.L
+ page_type = '' #ZNY 页面类型标识
+ paginate_by = settings.PAGINATE_BY #ZNY 每页显示的文章数量
+ page_kwarg = 'page' #ZNY 分页参数名称
+ link_type = LinkShowType.L #ZNY 链接显示类型,默认为列表页
- def get_view_cache_key(self):
- return self.request.get['pages']
+ def get_view_cache_key(self): #ZNY 获取视图缓存键方法
+ return self.request.get['pages'] #ZNY 从请求中获取页面参数作为缓存键
@property
- def page_number(self):
- page_kwarg = self.page_kwarg
- page = self.kwargs.get(
+ def page_number(self): #ZNY 页码属性,计算当前页码
+ page_kwarg = self.page_kwarg #ZNY 获取分页参数名
+ page = self.kwargs.get( #ZNY 从URL参数、GET参数或默认值获取页码
page_kwarg) or self.request.GET.get(page_kwarg) or 1
- return page
+ return page #ZNY 返回当前页码
- def get_queryset_cache_key(self):
+ def get_queryset_cache_key(self): #ZNY 获取查询集缓存键方法
"""
子类重写.获得queryset的缓存key
"""
- raise NotImplementedError()
+ raise NotImplementedError() #ZNY 要求子类必须实现此方法
- def get_queryset_data(self):
+ def get_queryset_data(self): #ZNY 获取查询集数据方法
"""
子类重写.获取queryset的数据
"""
- raise NotImplementedError()
+ raise NotImplementedError() #ZNY 要求子类必须实现此方法
- def get_queryset_from_cache(self, cache_key):
+ def get_queryset_from_cache(self, cache_key): #ZNY 从缓存获取查询集方法
'''
缓存页面数据
:param cache_key: 缓存key
:return:
'''
- value = cache.get(cache_key)
- if value:
- logger.info('get view cache.key:{key}'.format(key=cache_key))
- return value
- else:
- article_list = self.get_queryset_data()
- cache.set(cache_key, article_list)
- logger.info('set view cache.key:{key}'.format(key=cache_key))
- return article_list
-
- def get_queryset(self):
+ value = cache.get(cache_key) #ZNY 尝试从缓存获取数据
+ if value: #ZNY 如果缓存存在
+ logger.info('get view cache.key:{key}'.format(key=cache_key)) #ZNY 记录缓存命中日志
+ return value #ZNY 返回缓存数据
+ else: #ZNY 如果缓存不存在
+ article_list = self.get_queryset_data() #ZNY 从数据库获取数据
+ cache.set(cache_key, article_list) #ZNY 将数据存入缓存
+ logger.info('set view cache.key:{key}'.format(key=cache_key)) #ZNY 记录缓存设置日志
+ return article_list #ZNY 返回数据
+
+ def get_queryset(self): #ZNY 重写获取查询集方法
'''
重写默认,从缓存获取数据
:return:
'''
- key = self.get_queryset_cache_key()
- value = self.get_queryset_from_cache(key)
- return value
+ key = self.get_queryset_cache_key() #ZNY 获取缓存键
+ value = self.get_queryset_from_cache(key) #ZNY 从缓存获取数据
+ return value #ZNY 返回查询集
- def get_context_data(self, **kwargs):
- kwargs['linktype'] = self.link_type
- return super(ArticleListView, self).get_context_data(**kwargs)
+ def get_context_data(self, **kwargs): #ZNY 获取上下文数据方法
+ kwargs['linktype'] = self.link_type #ZNY 添加链接类型到上下文
+ return super(ArticleListView, self).get_context_data(**kwargs) #ZNY 调用父类方法
-class IndexView(ArticleListView):
+class IndexView(ArticleListView): #ZNY 首页视图,继承文章列表视图
'''
首页
'''
# 友情链接类型
- link_type = LinkShowType.I
+ link_type = LinkShowType.I #ZNY 设置链接显示类型为首页
- def get_queryset_data(self):
- article_list = Article.objects.filter(type='a', status='p')
- return article_list
+ def get_queryset_data(self): #ZNY 实现获取查询集数据方法
+ article_list = Article.objects.filter(type='a', status='p') #ZNY 获取所有已发布的普通文章
+ return article_list #ZNY 返回文章列表
- def get_queryset_cache_key(self):
- cache_key = 'index_{page}'.format(page=self.page_number)
- return cache_key
+ def get_queryset_cache_key(self): #ZNY 实现获取查询集缓存键方法
+ cache_key = 'index_{page}'.format(page=self.page_number) #ZNY 生成首页缓存键,包含页码
+ return cache_key #ZNY 返回缓存键
-class ArticleDetailView(DetailView):
+class ArticleDetailView(DetailView): #ZNY 文章详情视图,继承Django通用详情视图
'''
文章详情页面
'''
- template_name = 'blog/article_detail.html'
- model = Article
- pk_url_kwarg = 'article_id'
- context_object_name = "article"
-
- def get_context_data(self, **kwargs):
- comment_form = CommentForm()
-
- article_comments = self.object.comment_list()
- parent_comments = article_comments.filter(parent_comment=None)
- blog_setting = get_blog_setting()
- paginator = Paginator(parent_comments, blog_setting.article_comment_count)
- page = self.request.GET.get('comment_page', '1')
- if not page.isnumeric():
- page = 1
- else:
- page = int(page)
- if page < 1:
- page = 1
- if page > paginator.num_pages:
- page = paginator.num_pages
-
- p_comments = paginator.page(page)
- next_page = p_comments.next_page_number() if p_comments.has_next() else None
- prev_page = p_comments.previous_page_number() if p_comments.has_previous() else None
-
- if next_page:
- kwargs[
+ template_name = 'blog/article_detail.html' #ZNY 指定详情页模板
+ model = Article #ZNY 指定模型类
+ pk_url_kwarg = 'article_id' #ZNY URL中的主键参数名
+ context_object_name = "article" #ZNY 模板中使用的上下文变量名
+
+ def get_context_data(self, **kwargs): #ZNY 重写获取上下文数据方法
+ comment_form = CommentForm() #ZNY 创建评论表单实例
+
+ article_comments = self.object.comment_list() #ZNY 获取文章评论列表
+ parent_comments = article_comments.filter(parent_comment=None) #ZNY 获取顶级评论(无父评论)
+ blog_setting = get_blog_setting() #ZNY 获取博客设置
+ paginator = Paginator(parent_comments, blog_setting.article_comment_count) #ZNY 创建评论分页器
+ page = self.request.GET.get('comment_page', '1') #ZNY 获取评论页码,默认为1
+ if not page.isnumeric(): #ZNY 如果页码不是数字
+ page = 1 #ZNY 设置为第一页
+ else: #ZNY 如果是数字
+ page = int(page) #ZNY 转换为整数
+ if page < 1: #ZNY 如果页码小于1
+ page = 1 #ZNY 设置为第一页
+ if page > paginator.num_pages: #ZNY 如果页码大于总页数
+ page = paginator.num_pages #ZNY 设置为最后一页
+
+ p_comments = paginator.page(page) #ZNY 获取指定页的评论
+ next_page = p_comments.next_page_number() if p_comments.has_next() else None #ZNY 计算下一页页码
+ prev_page = p_comments.previous_page_number() if p_comments.has_previous() else None #ZNY 计算上一页页码
+
+ if next_page: #ZNY 如果存在下一页
+ kwargs[ #ZNY 设置下一页评论URL
'comment_next_page_url'] = self.object.get_absolute_url() + f'?comment_page={next_page}#commentlist-container'
- if prev_page:
- kwargs[
+ if prev_page: #ZNY 如果存在上一页
+ kwargs[ #ZNY 设置上一页评论URL
'comment_prev_page_url'] = self.object.get_absolute_url() + f'?comment_page={prev_page}#commentlist-container'
- kwargs['form'] = comment_form
- kwargs['article_comments'] = article_comments
- kwargs['p_comments'] = p_comments
- kwargs['comment_count'] = len(
+ kwargs['form'] = comment_form #ZNY 添加评论表单到上下文
+ kwargs['article_comments'] = article_comments #ZNY 添加所有评论到上下文
+ kwargs['p_comments'] = p_comments #ZNY 添加分页评论到上下文
+ kwargs['comment_count'] = len( #ZNY 添加评论总数到上下文
article_comments) if article_comments else 0
- kwargs['next_article'] = self.object.next_article
- kwargs['prev_article'] = self.object.prev_article
+ kwargs['next_article'] = self.object.next_article #ZNY 添加下一篇文章到上下文
+ kwargs['prev_article'] = self.object.prev_article #ZNY 添加上一篇文章到上下文
- context = super(ArticleDetailView, self).get_context_data(**kwargs)
- article = self.object
+ context = super(ArticleDetailView, self).get_context_data(**kwargs) #ZNY 调用父类方法获取基础上下文
+ article = self.object #ZNY 获取当前文章对象
# Action Hook, 通知插件"文章详情已获取"
- hooks.run_action('after_article_body_get', article=article, request=self.request)
+ hooks.run_action('after_article_body_get', article=article, request=self.request) #ZNY 执行文章内容获取后的动作钩子
# # Filter Hook, 允许插件修改文章正文
- article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article,
+ article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article, #ZNY 应用文章内容过滤器
request=self.request)
- return context
+ return context #ZNY 返回完整的上下文数据
-class CategoryDetailView(ArticleListView):
+class CategoryDetailView(ArticleListView): #ZNY 分类详情视图,继承文章列表视图
'''
分类目录列表
'''
- page_type = "分类目录归档"
+ page_type = "分类目录归档" #ZNY 设置页面类型
- def get_queryset_data(self):
- slug = self.kwargs['category_name']
- category = get_object_or_404(Category, slug=slug)
+ def get_queryset_data(self): #ZNY 实现获取查询集数据方法
+ slug = self.kwargs['category_name'] #ZNY 从URL参数获取分类slug
+ category = get_object_or_404(Category, slug=slug) #ZNY 获取分类对象,不存在则404
- categoryname = category.name
- self.categoryname = categoryname
- categorynames = list(
+ categoryname = category.name #ZNY 获取分类名称
+ self.categoryname = categoryname #ZNY 保存分类名称到实例变量
+ categorynames = list( #ZNY 获取所有子分类名称列表
map(lambda c: c.name, category.get_sub_categorys()))
- article_list = Article.objects.filter(
+ article_list = Article.objects.filter( #ZNY 获取该分类及其子分类下的所有已发布文章
category__name__in=categorynames, status='p')
- return article_list
-
- def get_queryset_cache_key(self):
- slug = self.kwargs['category_name']
- category = get_object_or_404(Category, slug=slug)
- categoryname = category.name
- self.categoryname = categoryname
- cache_key = 'category_list_{categoryname}_{page}'.format(
+ return article_list #ZNY 返回文章列表
+
+ def get_queryset_cache_key(self): #ZNY 实现获取查询集缓存键方法
+ slug = self.kwargs['category_name'] #ZNY 从URL参数获取分类slug
+ category = get_object_or_404(Category, slug=slug) #ZNY 获取分类对象
+ categoryname = category.name #ZNY 获取分类名称
+ self.categoryname = categoryname #ZNY 保存分类名称到实例变量
+ cache_key = 'category_list_{categoryname}_{page}'.format( #ZNY 生成分类缓存键
categoryname=categoryname, page=self.page_number)
- return cache_key
+ return cache_key #ZNY 返回缓存键
- def get_context_data(self, **kwargs):
+ def get_context_data(self, **kwargs): #ZNY 重写获取上下文数据方法
- categoryname = self.categoryname
+ categoryname = self.categoryname #ZNY 获取分类名称
try:
- categoryname = categoryname.split('/')[-1]
+ categoryname = categoryname.split('/')[-1] #ZNY 尝试获取分类路径的最后一部分
except BaseException:
- pass
- kwargs['page_type'] = CategoryDetailView.page_type
- kwargs['tag_name'] = categoryname
- return super(CategoryDetailView, self).get_context_data(**kwargs)
+ pass #ZNY 如果出错则忽略
+ kwargs['page_type'] = CategoryDetailView.page_type #ZNY 添加页面类型到上下文
+ kwargs['tag_name'] = categoryname #ZNY 添加分类名称到上下文
+ return super(CategoryDetailView, self).get_context_data(**kwargs) #ZNY 调用父类方法
-class AuthorDetailView(ArticleListView):
+class AuthorDetailView(ArticleListView): #ZNY 作者详情视图,继承文章列表视图
'''
作者详情页
'''
- page_type = '作者文章归档'
+ page_type = '作者文章归档' #ZNY 设置页面类型
- def get_queryset_cache_key(self):
- from uuslug import slugify
- author_name = slugify(self.kwargs['author_name'])
- cache_key = 'author_{author_name}_{page}'.format(
+ def get_queryset_cache_key(self): #ZNY 实现获取查询集缓存键方法
+ from uuslug import slugify #ZNY 导入slugify函数
+ author_name = slugify(self.kwargs['author_name']) #ZNY 对作者名称进行slugify处理
+ cache_key = 'author_{author_name}_{page}'.format( #ZNY 生成作者缓存键
author_name=author_name, page=self.page_number)
- return cache_key
+ return cache_key #ZNY 返回缓存键
- def get_queryset_data(self):
- author_name = self.kwargs['author_name']
- article_list = Article.objects.filter(
+ def get_queryset_data(self): #ZNY 实现获取查询集数据方法
+ author_name = self.kwargs['author_name'] #ZNY 从URL参数获取作者名称
+ article_list = Article.objects.filter( #ZNY 获取该作者的所有已发布普通文章
author__username=author_name, type='a', status='p')
- return article_list
+ return article_list #ZNY 返回文章列表
- def get_context_data(self, **kwargs):
- author_name = self.kwargs['author_name']
- kwargs['page_type'] = AuthorDetailView.page_type
- kwargs['tag_name'] = author_name
- return super(AuthorDetailView, self).get_context_data(**kwargs)
+ def get_context_data(self, **kwargs): #ZNY 重写获取上下文数据方法
+ author_name = self.kwargs['author_name'] #ZNY 获取作者名称
+ kwargs['page_type'] = AuthorDetailView.page_type #ZNY 添加页面类型到上下文
+ kwargs['tag_name'] = author_name #ZNY 添加作者名称到上下文
+ return super(AuthorDetailView, self).get_context_data(**kwargs) #ZNY 调用父类方法
-class TagDetailView(ArticleListView):
+class TagDetailView(ArticleListView): #ZNY 标签详情视图,继承文章列表视图
'''
标签列表页面
'''
- page_type = '分类标签归档'
-
- def get_queryset_data(self):
- slug = self.kwargs['tag_name']
- tag = get_object_or_404(Tag, slug=slug)
- tag_name = tag.name
- self.name = tag_name
- article_list = Article.objects.filter(
+ page_type = '分类标签归档' #ZNY 设置页面类型
+
+ def get_queryset_data(self): #ZNY 实现获取查询集数据方法
+ slug = self.kwargs['tag_name'] #ZNY 从URL参数获取标签slug
+ tag = get_object_or_404(Tag, slug=slug) #ZNY 获取标签对象,不存在则404
+ tag_name = tag.name #ZNY 获取标签名称
+ self.name = tag_name #ZNY 保存标签名称到实例变量
+ article_list = Article.objects.filter( #ZNY 获取该标签下的所有已发布普通文章
tags__name=tag_name, type='a', status='p')
- return article_list
-
- def get_queryset_cache_key(self):
- slug = self.kwargs['tag_name']
- tag = get_object_or_404(Tag, slug=slug)
- tag_name = tag.name
- self.name = tag_name
- cache_key = 'tag_{tag_name}_{page}'.format(
+ return article_list #ZNY 返回文章列表
+
+ def get_queryset_cache_key(self): #ZNY 实现获取查询集缓存键方法
+ slug = self.kwargs['tag_name'] #ZNY 从URL参数获取标签slug
+ tag = get_object_or_404(Tag, slug=slug) #ZNY 获取标签对象
+ tag_name = tag.name #ZNY 获取标签名称
+ self.name = tag_name #ZNY 保存标签名称到实例变量
+ cache_key = 'tag_{tag_name}_{page}'.format( #ZNY 生成标签缓存键
tag_name=tag_name, page=self.page_number)
- return cache_key
+ return cache_key #ZNY 返回缓存键
- def get_context_data(self, **kwargs):
- # tag_name = self.kwargs['tag_name']
- tag_name = self.name
- kwargs['page_type'] = TagDetailView.page_type
- kwargs['tag_name'] = tag_name
- return super(TagDetailView, self).get_context_data(**kwargs)
+ def get_context_data(self, **kwargs): #ZNY 重写获取上下文数据方法
+ # tag_name = self.kwargs['tag_name'] #ZNY 注释掉的获取标签名称方式
+ tag_name = self.name #ZNY 从实例变量获取标签名称
+ kwargs['page_type'] = TagDetailView.page_type #ZNY 添加页面类型到上下文
+ kwargs['tag_name'] = tag_name #ZNY 添加标签名称到上下文
+ return super(TagDetailView, self).get_context_data(**kwargs) #ZNY 调用父类方法
-class ArchivesView(ArticleListView):
+class ArchivesView(ArticleListView): #ZNY 文章归档视图,继承文章列表视图
'''
文章归档页面
'''
- page_type = '文章归档'
- paginate_by = None
- page_kwarg = None
- template_name = 'blog/article_archives.html'
+ page_type = '文章归档' #ZNY 设置页面类型
+ paginate_by = None #ZNY 不分页
+ page_kwarg = None #ZNY 无分页参数
+ template_name = 'blog/article_archives.html' #ZNY 指定归档页面模板
- def get_queryset_data(self):
- return Article.objects.filter(status='p').all()
+ def get_queryset_data(self): #ZNY 实现获取查询集数据方法
+ return Article.objects.filter(status='p').all() #ZNY 获取所有已发布文章
- def get_queryset_cache_key(self):
- cache_key = 'archives'
- return cache_key
+ def get_queryset_cache_key(self): #ZNY 实现获取查询集缓存键方法
+ cache_key = 'archives' #ZNY 归档页面使用固定缓存键
+ return cache_key #ZNY 返回缓存键
-class LinkListView(ListView):
- model = Links
- template_name = 'blog/links_list.html'
+class LinkListView(ListView): #ZNY 友情链接列表视图,继承Django通用列表视图
+ model = Links #ZNY 指定模型类
+ template_name = 'blog/links_list.html' #ZNY 指定模板文件
- def get_queryset(self):
- return Links.objects.filter(is_enable=True)
+ def get_queryset(self): #ZNY 重写获取查询集方法
+ return Links.objects.filter(is_enable=True) #ZNY 只返回启用的友情链接
-class EsSearchView(SearchView):
- def get_context(self):
- paginator, page = self.build_page()
- context = {
- "query": self.query,
- "form": self.form,
- "page": page,
- "paginator": paginator,
- "suggestion": None,
+class EsSearchView(SearchView): #ZNY Elasticsearch搜索视图,继承Haystack搜索视图
+ def get_context(self): #ZNY 重写获取上下文方法
+ paginator, page = self.build_page() #ZNY 构建分页
+ context = { #ZNY 构建上下文字典
+ "query": self.query, #ZNY 搜索查询词
+ "form": self.form, #ZNY 搜索表单
+ "page": page, #ZNY 当前页
+ "paginator": paginator, #ZNY 分页器
+ "suggestion": None, #ZNY 搜索建议,初始为None
}
- if hasattr(self.results, "query") and self.results.query.backend.include_spelling:
- context["suggestion"] = self.results.query.get_spelling_suggestion()
- context.update(self.extra_context())
+ if hasattr(self.results, "query") and self.results.query.backend.include_spelling: #ZNY 如果支持拼写建议
+ context["suggestion"] = self.results.query.get_spelling_suggestion() #ZNY 获取拼写建议
+ context.update(self.extra_context()) #ZNY 更新额外上下文
- return context
+ return context #ZNY 返回完整上下文
-@csrf_exempt
-def fileupload(request):
+@csrf_exempt #ZNY 免除CSRF保护
+def fileupload(request): #ZNY 文件上传视图函数
"""
该方法需自己写调用端来上传图片,该方法仅提供图床功能
:param request:
:return:
"""
- if request.method == 'POST':
- sign = request.GET.get('sign', None)
- if not sign:
- return HttpResponseForbidden()
- if not sign == get_sha256(get_sha256(settings.SECRET_KEY)):
- return HttpResponseForbidden()
- response = []
- for filename in request.FILES:
- timestr = timezone.now().strftime('%Y/%m/%d')
- imgextensions = ['jpg', 'png', 'jpeg', 'bmp']
- fname = u''.join(str(filename))
- isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0
- base_dir = os.path.join(settings.STATICFILES, "files" if not isimage else "image", timestr)
- if not os.path.exists(base_dir):
- os.makedirs(base_dir)
- savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}"))
- if not savepath.startswith(base_dir):
- return HttpResponse("only for post")
- with open(savepath, 'wb+') as wfile:
- for chunk in request.FILES[filename].chunks():
+ if request.method == 'POST': #ZNY 只处理POST请求
+ sign = request.GET.get('sign', None) #ZNY 从GET参数获取签名
+ if not sign: #ZNY 如果没有签名
+ return HttpResponseForbidden() #ZNY 返回403禁止访问
+ if not sign == get_sha256(get_sha256(settings.SECRET_KEY)): #ZNY 验证签名是否正确
+ return HttpResponseForbidden() #ZNY 签名错误返回403
+ response = [] #ZNY 初始化响应列表
+ for filename in request.FILES: #ZNY 遍历所有上传的文件
+ timestr = timezone.now().strftime('%Y/%m/%d') #ZNY 生成时间目录字符串
+ imgextensions = ['jpg', 'png', 'jpeg', 'bmp'] #ZNY 图片扩展名列表
+ fname = u''.join(str(filename)) #ZNY 获取文件名
+ isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0 #ZNY 判断是否为图片文件
+ base_dir = os.path.join(settings.STATICFILES, "files" if not isimage else "image", timestr) #ZNY 构建保存目录
+ if not os.path.exists(base_dir): #ZNY 如果目录不存在
+ os.makedirs(base_dir) #ZNY 创建目录
+ savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}")) #ZNY 生成保存路径,使用UUID作为文件名
+ if not savepath.startswith(base_dir): #ZNY 安全检查,确保路径在指定目录内
+ return HttpResponse("only for post") #ZNY 路径非法返回错误
+ with open(savepath, 'wb+') as wfile: #ZNY 打开文件进行写入
+ for chunk in request.FILES[filename].chunks(): #ZNY 分块写入文件
wfile.write(chunk)
- if isimage:
- from PIL import Image
- image = Image.open(savepath)
- image.save(savepath, quality=20, optimize=True)
- url = static(savepath)
- response.append(url)
- return HttpResponse(response)
+ if isimage: #ZNY 如果是图片文件
+ from PIL import Image #ZNY 导入PIL库
+ image = Image.open(savepath) #ZNY 打开图片
+ image.save(savepath, quality=20, optimize=True) #ZNY 压缩图片并保存
+ url = static(savepath) #ZNY 生成静态文件URL
+ response.append(url) #ZNY 将URL添加到响应列表
+ return HttpResponse(response) #ZNY 返回URL列表
- else:
- return HttpResponse("only for post")
+ else: #ZNY 非POST请求
+ return HttpResponse("only for post") #ZNY 返回只支持POST请求
-def page_not_found_view(
+def page_not_found_view( #ZNY 404页面未找到视图
request,
exception,
template_name='blog/error_page.html'):
- if exception:
- logger.error(exception)
- url = request.get_full_path()
- return render(request,
+ if exception: #ZNY 如果有异常信息
+ logger.error(exception) #ZNY 记录错误日志
+ url = request.get_full_path() #ZNY 获取请求的完整路径
+ return render(request, #ZNY 渲染错误页面
template_name,
{'message': _('Sorry, the page you requested is not found, please click the home page to see other?'),
- 'statuscode': '404'},
- status=404)
+ 'statuscode': '404'}, #ZNY 错误消息和状态码
+ status=404) #ZNY 返回404状态码
-def server_error_view(request, template_name='blog/error_page.html'):
- return render(request,
+def server_error_view(request, template_name='blog/error_page.html'): #ZNY 500服务器错误视图
+ return render(request, #ZNY 渲染错误页面
template_name,
{'message': _('Sorry, the server is busy, please click the home page to see other?'),
- 'statuscode': '500'},
- status=500)
+ 'statuscode': '500'}, #ZNY 错误消息和状态码
+ status=500) #ZNY 返回500状态码
-def permission_denied_view(
+def permission_denied_view( #ZNY 403权限拒绝视图
request,
exception,
template_name='blog/error_page.html'):
- if exception:
- logger.error(exception)
- return render(
+ if exception: #ZNY 如果有异常信息
+ logger.error(exception) #ZNY 记录错误日志
+ return render( #ZNY 渲染错误页面
request, template_name, {
'message': _('Sorry, you do not have permission to access this page?'),
- 'statuscode': '403'}, status=403)
+ 'statuscode': '403'}, status=403) #ZNY 错误消息和状态码,返回403状态码
-def clean_cache_view(request):
- cache.clear()
- return HttpResponse('ok')
+def clean_cache_view(request): #ZNY 清理缓存视图
+ cache.clear() #ZNY 清除所有缓存
+ return HttpResponse('ok') #ZNY 返回成功响应
\ No newline at end of file